summaryrefslogtreecommitdiffhomepage
path: root/.rules/docs/obsidian/workspace-api.md
blob: deedb03f1fac1319553167e3ddd564da3013f981 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# Obsidian Workspace & Views API

## Workspace

Access via `this.app.workspace`. Extends `Events`. Manages the UI layout as a tree of workspace items.

### Layout Structure

The workspace is a tree:
- **WorkspaceSplit**: Lays out children side by side (vertical or horizontal)
- **WorkspaceTabs**: Shows one child at a time with tab headers
- **WorkspaceLeaf**: Terminal node that displays a View

Three root splits: `leftSplit` (sidebar), `rootSplit` (main area), `rightSplit` (sidebar).

### Properties

| Property | Type | Description |
|----------|------|-------------|
| `activeEditor` | `MarkdownFileInfo \| null` | Current editor component |
| `activeLeaf` | `WorkspaceLeaf \| null` | Currently focused leaf. Avoid using directly; prefer helper methods. |
| `layoutReady` | `boolean` | Whether layout is initialized |
| `containerEl` | `HTMLElement` | Workspace container |
| `leftSplit` | `WorkspaceSidedock \| WorkspaceMobileDrawer` | Left sidebar |
| `rightSplit` | `WorkspaceSidedock \| WorkspaceMobileDrawer` | Right sidebar |
| `rootSplit` | `WorkspaceRoot` | Main content area |
| `leftRibbon` / `rightRibbon` | `WorkspaceRibbon` | Ribbon bars |
| `requestSaveLayout` | `Debouncer` | Debounced layout save |

### Getting Leaves & Views

| Method | Returns | Description |
|--------|---------|-------------|
| `getActiveFile()` | `TFile \| null` | Active file from current FileView, or most recent |
| `getActiveViewOfType(type)` | `T \| null` | Get active view if it matches type |
| `getLeavesOfType(viewType)` | `WorkspaceLeaf[]` | All leaves of a view type |
| `getLeafById(id)` | `WorkspaceLeaf \| null` | Get leaf by ID |
| `getLastOpenFiles()` | `string[]` | 10 most recently opened filenames |
| `getMostRecentLeaf(root?)` | `WorkspaceLeaf \| null` | Most recent leaf in root split |
| `iterateAllLeaves(cb)` | `void` | Iterate all leaves (main + sidebars + floating) |
| `iterateRootLeaves(cb)` | `void` | Iterate main area leaves only |
| `getGroupLeaves(group)` | `WorkspaceLeaf[]` | Get leaves in a link group |

### Creating & Managing Leaves

| Method | Returns | Description |
|--------|---------|-------------|
| `getLeaf(newLeaf?)` | `WorkspaceLeaf` | Get/create leaf. `false`=reuse existing, `true`/`'tab'`=new tab, `'split'`=split adjacent, `'window'`=popout. |
| `getLeaf('split', direction)` | `WorkspaceLeaf` | Split: `'vertical'` (right) or `'horizontal'` (below) |
| `getLeftLeaf(split)` | `WorkspaceLeaf` | Create leaf in left sidebar |
| `getRightLeaf(split)` | `WorkspaceLeaf` | Create leaf in right sidebar |
| `ensureSideLeaf(type, side, options?)` | `WorkspaceLeaf` | Get or create sidebar leaf (v1.7.2) |
| `createLeafBySplit(leaf, direction?, before?)` | `WorkspaceLeaf` | Split an existing leaf |
| `createLeafInParent(parent, index)` | `WorkspaceLeaf` | Create leaf in specific parent |
| `duplicateLeaf(leaf, leafType?, direction?)` | `Promise<WorkspaceLeaf>` | Duplicate a leaf |
| `detachLeavesOfType(viewType)` | `void` | Remove all leaves of a type |

### Navigation

| Method | Description |
|--------|-------------|
| `setActiveLeaf(leaf, params?)` | Set the active leaf |
| `revealLeaf(leaf)` | Bring leaf to foreground, uncollapse sidebar if needed. `await` for full load. (v1.7.2) |
| `openLinkText(linktext, sourcePath, newLeaf?, openViewState?)` | Open internal link |
| `moveLeafToPopout(leaf, data?)` | Move leaf to popout window (desktop only) |
| `openPopoutLeaf(data?)` | Open new popout window with leaf (desktop only) |
| `onLayoutReady(callback)` | Run callback when layout is ready (or immediately if already ready). (v0.11.0) |

### Workspace Events

| Event | Callback Args | Description |
|-------|--------------|-------------|
| `'file-open'` | `(file: TFile \| null)` | Active file changed (new leaf, existing leaf, or embed) |
| `'active-leaf-change'` | `(leaf: WorkspaceLeaf \| null)` | Active leaf changed |
| `'layout-change'` | `()` | Layout changed |
| `'editor-change'` | `(editor: Editor, info: MarkdownView \| MarkdownFileInfo)` | Editor content changed |
| `'editor-paste'` | `(evt: ClipboardEvent, editor: Editor, info)` | Editor paste. Check `evt.defaultPrevented`. |
| `'editor-drop'` | `(evt: DragEvent, editor: Editor, info)` | Editor drop. Check `evt.defaultPrevented`. |
| `'editor-menu'` | `(menu: Menu, editor: Editor, info)` | Editor context menu (v1.1.0) |
| `'file-menu'` | `(menu: Menu, file: TAbstractFile, source: string, leaf?: WorkspaceLeaf)` | File context menu |
| `'files-menu'` | `(menu: Menu, files: TAbstractFile[], source: string, leaf?: WorkspaceLeaf)` | Multi-file context menu (v1.4.10) |
| `'url-menu'` | `(menu: Menu, url: string)` | External URL context menu (v1.5.1) |
| `'quick-preview'` | `(file: TFile, data: string)` | Active markdown file modified (pre-save) |
| `'resize'` | `()` | Window/item resized |
| `'quit'` | `()` | App about to quit (best-effort, not guaranteed) |
| `'window-open'` | `(win: WorkspaceWindow)` | Popout window created |
| `'window-close'` | `(win: WorkspaceWindow)` | Popout window closed |
| `'css-change'` | `()` | CSS changed |

## WorkspaceLeaf

A leaf hosts a single View.

### Properties

| Property | Type | Description |
|----------|------|-------------|
| `view` | `View` | The view in this leaf. Check `instanceof` before casting. |
| `parent` | `WorkspaceTabs \| WorkspaceMobileDrawer` | Parent container |
| `isDeferred` | `boolean` (readonly) | Whether leaf is deferred/background (v1.7.2) |

### Methods

| Method | Description |
|--------|-------------|
| `openFile(file, openState?)` | Open a file in this leaf |
| `setViewState(viewState, eState?)` | Set the view state (type, state, active) |
| `getViewState()` | Get current view state |
| `getDisplayText()` | Display text for tab |
| `detach()` | Remove this leaf |
| `setPinned(pinned)` / `togglePinned()` | Pin/unpin leaf |
| `setGroup(group)` | Set link group |
| `loadIfDeferred()` | Load deferred leaf. Await for full load. (v1.7.2) |

## Custom Views

### ItemView (for custom panels)

```typescript
import { ItemView, WorkspaceLeaf } from 'obsidian';

export const VIEW_TYPE = 'my-view';

export class MyView extends ItemView {
  constructor(leaf: WorkspaceLeaf) {
    super(leaf);
  }

  getViewType(): string { return VIEW_TYPE; }
  getDisplayText(): string { return 'My View'; }

  async onOpen() {
    const container = this.contentEl;
    container.empty();
    container.createEl('h4', { text: 'Hello' });
  }

  async onClose() {
    // Cleanup
  }
}
```

### View Properties

| Property | Type | Description |
|----------|------|-------------|
| `app` | `App` | App reference |
| `leaf` | `WorkspaceLeaf` | Parent leaf |
| `containerEl` | `HTMLElement` | Container element |
| `contentEl` | `HTMLElement` | Content area (ItemView) |
| `icon` | `IconName` | View icon |
| `navigation` | `boolean` | `true` if navigable (file views). `false` for static panels (explorer, calendar). |
| `scope` | `Scope \| null` | Optional hotkey scope |

### View Methods

| Method | Description |
|--------|-------------|
| `getViewType()` | Return unique view type string (abstract) |
| `getDisplayText()` | Return human-readable name (abstract) |
| `onOpen()` | Build view content |
| `onClose()` | Cleanup resources |
| `onResize()` | Handle size changes |
| `onPaneMenu(menu, source)` | Populate pane context menu |
| `addAction(icon, title, callback)` | Add action button to view header |
| `getState()` / `setState(state, result)` | Serialize/restore state |
| `getEphemeralState()` / `setEphemeralState(state)` | Non-persistent state (scroll position, etc.) |

### MarkdownView

Extends `TextFileView`. The built-in markdown editor view.

| Property | Type | Description |
|----------|------|-------------|
| `editor` | `Editor` | The editor instance |
| `file` | `TFile \| null` | Currently open file |
| `data` | `string` | In-memory file content |
| `currentMode` | `MarkdownSubView` | Current editing subview |
| `previewMode` | `MarkdownPreviewView` | Preview renderer |

| Method | Description |
|--------|-------------|
| `getMode()` | Get current mode |
| `getViewData()` | Get view data |
| `setViewData(data, clear)` | Set view data |
| `showSearch(replace?)` | Show search (and optionally replace) |

### Registering & Activating Views

```typescript
// Register in onload()
this.registerView(VIEW_TYPE, (leaf) => new MyView(leaf));

// Activate view
async activateView() {
  const { workspace } = this.app;
  let leaf: WorkspaceLeaf | null = null;
  const leaves = workspace.getLeavesOfType(VIEW_TYPE);

  if (leaves.length > 0) {
    leaf = leaves[0];
  } else {
    leaf = workspace.getRightLeaf(false);
    await leaf.setViewState({ type: VIEW_TYPE, active: true });
  }
  workspace.revealLeaf(leaf);
}
```

**Warning**: Never store view references in your plugin. Obsidian may call the factory multiple times. Use `getLeavesOfType()` to access views.

```typescript
this.app.workspace.getLeavesOfType(VIEW_TYPE).forEach((leaf) => {
  if (leaf.view instanceof MyView) {
    // Access view instance
  }
});
```

Plugins must remove their leaves when disabled. `detachLeavesOfType(viewType)` removes all.