@mdzip/editor-ng 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,250 +1,256 @@
1
- [![MDZip logo][mdzip-logo]][mdzip-url]
2
-
3
- [mdzip-logo]: https://raw.githubusercontent.com/mdzip-project/mdzip-editor/main/resources/mdzip-mark.svg
4
- [mdzip-url]: https://mdzip.org
5
-
6
- # @mdzip/editor-ng
7
-
8
- [![npm](https://img.shields.io/npm/v/@mdzip/editor-ng?logo=npm)](https://www.npmjs.com/package/@mdzip/editor-ng)
9
- [![license](https://img.shields.io/npm/l/@mdzip/editor-ng)](https://github.com/mdzip-project/mdzip-editor/blob/main/LICENSE)
10
-
11
- Angular component wrapper for the MDZip workspace editor.
12
-
13
- `@mdzip/editor-ng` provides a standalone `MdzipWorkspaceComponent` that embeds the full MDZip workspace UI — document preview, editor, package navigator, and asset manager — as a native Angular component.
14
-
15
- ## Install
16
-
17
- ```sh
18
- npm install @mdzip/editor @mdzip/editor-ng
19
- ```
20
-
21
- Peer dependencies: `@angular/common` and `@angular/core` >=20.
22
-
23
- ## Basic Usage
24
-
25
- ```ts
26
- import { MdzipWorkspaceComponent } from '@mdzip/editor-ng';
27
-
28
- @Component({
29
- imports: [MdzipWorkspaceComponent],
30
- template: `
31
- <mdzip-workspace
32
- [bytes]="fileBytes"
33
- fileName="document.mdz"
34
- mode="read-only"
35
- controls="viewer"
36
- (failed)="onError($event)"
37
- />
38
- `,
39
- styles: [':host { display: block; height: 600px; }']
40
- })
41
- export class AppComponent {
42
- fileBytes: Uint8Array | null = null;
43
- }
44
- ```
45
-
46
- The host element must have an explicit height. The component expands to fill it.
47
-
48
- ## Archive Diff
49
-
50
- ```ts
51
- import { MdzipDiffComponent } from '@mdzip/editor-ng';
52
-
53
- @Component({
54
- imports: [MdzipDiffComponent],
55
- template: `
56
- <mdzip-diff
57
- [before]="{ bytes: baseBytes, label: 'Git base' }"
58
- [after]="{ bytes: workingBytes, label: 'Working tree' }"
59
- [toolbarActions]="diffActions"
60
- initialPath="index.md"
61
- />
62
- `,
63
- styles: [':host { display:block; height:600px; }']
64
- })
65
- export class DiffComponent {}
66
- ```
67
-
68
- `MdzipDiffComponent` is read-only. It emits `selectionChanged` and `failed`,
69
- and exposes `openPath`, `openPreviousChange`, `openNextChange`,
70
- `setShowUnchanged`, `setNavigationVisible`, and `setToolbarActions`. The
71
- built-in toolbar adds Previous/Next change buttons and a Show-unchanged
72
- toggle; bind a `controls` input (`{ navigation?, changeTraversal?,
73
- showUnchanged? }`) to opt any of them out.
74
-
75
- ## Editor Mode
76
-
77
- ```ts
78
- <mdzip-workspace
79
- [bytes]="fileBytes"
80
- fileName="document.mdz"
81
- mode="editable"
82
- controls="standalone-editor"
83
- (saved)="onSaved($event)"
84
- (changed)="onChanged($event)"
85
- />
86
- ```
87
-
88
- ## Inputs
89
-
90
- | Input | Type | Default | Description |
91
- |-------|------|---------|-------------|
92
- | `bytes` | `Uint8Array \| null` | `null` | Raw archive bytes to open |
93
- | `workspace` | `MdzWorkspace \| null` | `null` | Pre-built workspace object |
94
- | `fileName` | `string` | `'document.mdz'` | Filename used for format detection and Save dialogs |
95
- | `mode` | `MdzipWorkspaceMode` | `'read-only'` | `'read-only'` or `'editable'` |
96
- | `sourceFormat` | `MdzipSourceFormat` | — | Override format detection: `'mdz'` or `'markdown'` |
97
- | `controls` | `MdzipControlPreset \| MdzipControlPolicy` | `'viewer'` | `'preview'`, `'viewer'`, `'standalone-editor'`, `'hosted-editor'`, or a policy object |
98
- | `toolbarDensity` | `'comfortable' \| 'compact' \| 'dense'` | `'comfortable'` | Semantic sizing preset for built-in toolbar controls |
99
- | `contentDensity` | `'comfortable' \| 'compact'` | `'comfortable'` | Semantic padding preset for editor and preview content |
100
- | `imageHydrationAnimation` | `'auto' \| 'initial' \| 'off'` | `'auto'` | Control preview image reveal animation: every render, first render per document path, or never |
101
- | `initialLayout` | `MdzipWorkspaceLayout` | — | Starting layout: `'preview'`, `'editor'`, `'split'` |
102
- | `initialColorScheme` | `MdzipColorScheme` | — | `'light'` or `'dark'` |
103
- | `navigationMode` | `MdzipNavigationMode` | `'editor'` | Package navigation mode |
104
- | `navigationButtonActive` | `boolean` | `true` | Whether the navigation button is shown |
105
- | `markdownRenderer` | `MdzipMarkdownRenderer` | — | Custom markdown renderer (keep the reference stable) |
106
- | `markdownExtensions` | `readonly MdzipMarkdownRenderExtension[]` | `[]` | Markdown pipeline extensions, diffed by `name` — new array identities with the same names are safe |
107
- | `entryRenderers` | `readonly MdzipEntryRenderer[]` | `[]` | Entry renderers claiming the content area for matching entries, diffed by `id` — new array identities with the same ids are safe |
108
- | `frontMatter` | `MdzipFrontMatterOptions` | `{}` | Front matter panel config (`enabled`, `display`, `collapsible`, `label`) — always registered, diffed by deep equality; new object identities with equal contents are safe |
109
- | `externalLinks` | `MdzipExternalLinkPolicy` | — | External-link policy, e.g. `{ target: '_blank' }` (adds `rel="noopener noreferrer"`); optional `rel` and `predicate(href)`. Re-applies without a re-render; diffed by reference — keep the object stable |
110
-
111
- `controls`, density, `imageHydrationAnimation`, and rendering input changes apply in
112
- place — they never recreate the workspace view. Line-number visibility changes
113
- preserve the current CodeMirror document and selection. See the `@mdzip/editor`
114
- Rendering Extensibility docs for the contracts and lifecycle rules.
115
-
116
- ### Native entry rendering (template directives)
117
-
118
- ```html
119
- <mdzip-workspace [bytes]="bytes" mode="editable">
120
- <ng-template mdzipEntryRenderer="manifest.json" let-context>
121
- <app-internals
122
- [manifest]="context.manifest"
123
- [editable]="context.mode === 'editable'"
124
- (manifestChange)="context.updateManifest($event)"
125
- />
126
- </ng-template>
127
-
128
- <ng-template [mdzipEntryRendererMatch]="isDrawioEntry" let-context>
129
- <app-drawio-viewer [entry]="context" />
130
- </ng-template>
131
- </mdzip-workspace>
132
- ```
133
-
134
- Import `MdzipEntryRendererDirective` alongside the component.
135
- `mdzipEntryRenderer` matches exact archive paths (string or array,
136
- case-insensitive); `[mdzipEntryRendererMatch]` takes a predicate. Optional
137
- `mdzipEntryRendererId` / `mdzipEntryRendererPriority` inputs control diffing
138
- and ordering. Embedded views are created in the component's view container —
139
- change detection and DI work normally — and are destroyed on selection
140
- change.
141
-
142
- ## Outputs
143
-
144
- | Output | Payload | Description |
145
- |--------|---------|-------------|
146
- | `changed` | `MdzipWorkspaceChange` | Emitted when archive bytes change |
147
- | `saved` | `MdzipWorkspaceSave` | Emitted on Save |
148
- | `snapshotChanged` | `MdzipWorkspaceSnapshot` | Emitted on any state change |
149
- | `selectionChanged` | `MdzipWorkspaceSnapshot` | Emitted when the editor selection changes |
150
- | `dirtyChanged` | `MdzipWorkspaceSnapshot` | Emitted when the dirty flag changes |
151
- | `validationChanged` | `MdzipWorkspaceSnapshot` | Emitted when validation state changes |
152
- | `colorSchemeChanged` | `MdzipColorScheme` | Emitted when the color scheme changes |
153
- | `previewRendered` | `MdzipWorkspaceSnapshot` | Emitted when the preview HTML is mounted |
154
- | `assetsHydrated` | `MdzipWorkspaceSnapshot` | Emitted once the mounted preview's images have loaded |
155
- | `linkActivated` | `MdzipLinkActivatedEvent` | Emitted for every preview link click before built-in handling; call `event.preventDefault()` to take over |
156
- | `failed` | `unknown` | Emitted on unrecoverable errors |
157
-
158
- ## Conversion hook
159
-
160
- `onConversionRequested` is an **input function** (not an output, because it must return a
161
- value): bind `[onConversionRequested]="handler"` where `handler` is
162
- `(action: MdzipConversionAction) => boolean | Promise<boolean>`. It fires when the user
163
- triggers the markdown→MDZ conversion flow (nav button, Insert Image, or image paste on a
164
- plain `.md`). Return/resolve `true` to take over and suppress the built-in conversion dialog.
165
-
166
- `onPackRequested` is the same kind of input function for the folder→.mdz packing
167
- decision surfaced by the exposed `packFilesAsWorkspace(files, options)` method — fires
168
- only when the file list contains more than one Markdown file, with the same
169
- return contract as `onConversionRequested`.
170
-
171
- ## Image insertion
172
-
173
- Use `[imageInsertMode]="'markdown' | 'ask' | 'html'"` for the built-in image
174
- markup flow. Bind `[imageInsertHandler]="handler"` for host-owned image UI; the
175
- handler can return Markdown or HTML sizing/alignment settings, return `null` to
176
- cancel, or return `undefined` to fall back to `imageInsertMode`.
177
-
178
- Bind `[imageEditHandler]="handler"` to let users edit an *existing* image by
179
- clicking it. Fully opt-in — with no handler bound, clicking an image does
180
- nothing and no edit affordance appears; there is no built-in fallback dialog.
181
-
182
- ## File management (nav pane)
183
-
184
- With `controls="standalone-editor"` or `"hosted-editor"` (or `fileActions: true` in a custom
185
- policy), the navigation pane offers a right-click context menu: new `.md` file, new folder,
186
- rename/move, duplicate, replace, download, copy markdown link, set entry point, set cover
187
- image, and delete. Files can also be dragged between folders, and OS files can be dropped
188
- onto the pane. Copy and Download remain available in read-only mode. The same operations are
189
- exposed as component methods: `removeFile`, `renameFile`, `setEntryPoint`, and `setCoverImage`.
190
- `whenRendered()` resolves once the current preview (including its images) is mounted — useful
191
- for revealing or animating read-only preview content.
192
-
193
- ## Links and anchors
194
-
195
- The `linkActivated` output fires for every preview link click, before the view's own handling, with the
196
- parsed link (`kind`: `'anchor' | 'document' | 'relative' | 'external'`, plus `path`,
197
- `anchor`, `targetPath`, `sourcePath`, modifier keys). Call `event.preventDefault()` in the
198
- handler to route the link yourself; unprevented clicks keep the built-in behavior.
199
- The component also exposes `scrollToAnchor(anchor)` (waits for the preview to mount, resolves whether it
200
- scrolled; safe straight after load) and `getAvailableAnchors()`. See the
201
- [`@mdzip/editor` README](https://www.npmjs.com/package/@mdzip/editor#links-and-anchors).
202
-
203
- ## Imperative API
204
-
205
- All imperative methods are public instance methods on `MdzipWorkspaceComponent`,
206
- so any reference to the component instance works. No view query is required: a
207
- template reference variable can hand the instance directly to an event handler.
208
-
209
- ```ts
210
- @Component({
211
- imports: [MdzipWorkspaceComponent],
212
- template: `
213
- <mdzip-workspace #workspace [bytes]="bytes" mode="editable" controls="hosted-editor" />
214
- <button (click)="save(workspace)">Save</button>
215
- `,
216
- })
217
- export class AppComponent {
218
- async save(workspace: MdzipWorkspaceComponent): Promise<void> {
219
- // Flush pending edits and persist
220
- const snapshot = await workspace.flush();
221
- if (snapshot) {
222
- await persist(snapshot.bytes);
223
- workspace.markPersisted();
224
- }
225
-
226
- // Execute editor commands
227
- await workspace.executeCommand('bold');
228
- }
229
- }
230
- ```
231
-
232
- To call methods outside an event handler, use a view query — the signal-based
233
- `viewChild` or the classic `@ViewChild` decorator:
234
-
235
- ```ts
236
- private readonly workspace = viewChild.required(MdzipWorkspaceComponent);
237
-
238
- async save(): Promise<void> {
239
- const snapshot = await this.workspace().flush();
240
- if (snapshot) {
241
- await persist(snapshot.bytes);
242
- this.workspace().markPersisted();
243
- }
244
- }
245
- ```
246
-
247
- Methods called before the view initializes are safe: they no-op and resolve to
248
- `false`/`null`/empty rather than throwing.
249
-
250
- See the [`@mdzip/editor`](https://www.npmjs.com/package/@mdzip/editor) package for the full API reference, theming guide, and framework-agnostic usage.
1
+ [![MDZip logo][mdzip-logo]][mdzip-url]
2
+
3
+ [mdzip-logo]: https://raw.githubusercontent.com/mdzip-project/mdzip-editor/main/resources/mdzip-mark.svg
4
+ [mdzip-url]: https://mdzip.org
5
+
6
+ # @mdzip/editor-ng
7
+
8
+ [![npm](https://img.shields.io/npm/v/@mdzip/editor-ng?logo=npm)](https://www.npmjs.com/package/@mdzip/editor-ng)
9
+ [![license](https://img.shields.io/npm/l/@mdzip/editor-ng)](https://github.com/mdzip-project/mdzip-editor/blob/main/LICENSE)
10
+
11
+ Angular component wrapper for the MDZip workspace editor.
12
+
13
+ `@mdzip/editor-ng` provides a standalone `MdzipWorkspaceComponent` that embeds the full MDZip workspace UI — document preview, editor, package navigator, and asset manager — as a native Angular component.
14
+
15
+ ## Install
16
+
17
+ ```sh
18
+ npm install @mdzip/editor @mdzip/editor-ng
19
+ ```
20
+
21
+ Peer dependencies: `@angular/common` and `@angular/core` >=20.
22
+
23
+ ## Basic Usage
24
+
25
+ ```ts
26
+ import { MdzipWorkspaceComponent } from '@mdzip/editor-ng';
27
+
28
+ @Component({
29
+ imports: [MdzipWorkspaceComponent],
30
+ template: `
31
+ <mdzip-workspace
32
+ [bytes]="fileBytes"
33
+ fileName="document.mdz"
34
+ mode="read-only"
35
+ controls="viewer"
36
+ (failed)="onError($event)"
37
+ />
38
+ `,
39
+ styles: [':host { display: block; height: 600px; }']
40
+ })
41
+ export class AppComponent {
42
+ fileBytes: Uint8Array | null = null;
43
+ }
44
+ ```
45
+
46
+ The host element must have an explicit height. The component expands to fill it.
47
+
48
+ ## Archive Diff
49
+
50
+ ```ts
51
+ import { MdzipDiffComponent } from '@mdzip/editor-ng';
52
+
53
+ @Component({
54
+ imports: [MdzipDiffComponent],
55
+ template: `
56
+ <mdzip-diff
57
+ [before]="{ bytes: baseBytes, label: 'Git base' }"
58
+ [after]="{ bytes: workingBytes, label: 'Working tree' }"
59
+ [toolbarActions]="diffActions"
60
+ initialPath="index.md"
61
+ />
62
+ `,
63
+ styles: [':host { display:block; height:600px; }']
64
+ })
65
+ export class DiffComponent {}
66
+ ```
67
+
68
+ `MdzipDiffComponent` is read-only. It emits `selectionChanged` and `failed`,
69
+ and exposes `openPath`, `openPreviousChange`, `openNextChange`,
70
+ `setShowUnchanged`, `setNavigationVisible`, and `setToolbarActions`. The
71
+ built-in toolbar adds Previous/Next change buttons and a Show-unchanged
72
+ toggle; bind a `controls` input (`{ navigation?, changeTraversal?,
73
+ showUnchanged? }`) to opt any of them out.
74
+
75
+ ## Editor Mode
76
+
77
+ ```ts
78
+ <mdzip-workspace
79
+ [bytes]="fileBytes"
80
+ fileName="document.mdz"
81
+ mode="editable"
82
+ controls="standalone-editor"
83
+ (saved)="onSaved($event)"
84
+ (changed)="onChanged($event)"
85
+ />
86
+ ```
87
+
88
+ ## Inputs
89
+
90
+ | Input | Type | Default | Description |
91
+ |-------|------|---------|-------------|
92
+ | `bytes` | `Uint8Array \| null` | `null` | Raw archive bytes to open |
93
+ | `workspace` | `MdzWorkspace \| null` | `null` | Pre-built workspace object |
94
+ | `fileName` | `string` | `'document.mdz'` | Filename used for format detection and Save dialogs |
95
+ | `mode` | `MdzipWorkspaceMode` | `'read-only'` | `'read-only'` or `'editable'` |
96
+ | `sourceFormat` | `MdzipSourceFormat` | — | Override format detection: `'mdz'` or `'markdown'` |
97
+ | `controls` | `MdzipControlPreset \| MdzipControlPolicy` | `'viewer'` | `'preview'`, `'viewer'`, `'standalone-editor'`, `'hosted-editor'`, or a policy object |
98
+ | `toolbarDensity` | `'comfortable' \| 'compact' \| 'dense'` | `'comfortable'` | Semantic sizing preset for built-in toolbar controls |
99
+ | `contentDensity` | `'comfortable' \| 'compact'` | `'comfortable'` | Semantic padding preset for editor and preview content |
100
+ | `imageHydrationAnimation` | `'auto' \| 'initial' \| 'off'` | `'auto'` | Control preview image reveal animation: every render, first render per document path, or never |
101
+ | `initialLayout` | `MdzipWorkspaceLayout` | — | Starting layout: `'preview'`, `'editor'`, `'split'` |
102
+ | `initialColorScheme` | `MdzipColorSchemePreference` | `'system'` | `'light'`, `'dark'` or `'system'` (follows the OS setting live) |
103
+ | `previewMaxWidth` | `MdzipPreviewMaxWidth` | `'default'` | Preview reading-column width: `'narrow'`, `'default'`, `'wide'`, `'full'` or a pixel number; the user can change it from the toolbar's page-width menu |
104
+ | `navigationMode` | `MdzipNavigationMode` | `'editor'` | Package navigation mode |
105
+ | `navigationButtonActive` | `boolean` | `true` | Whether the navigation button is shown |
106
+ | `markdownRenderer` | `MdzipMarkdownRenderer` | — | Custom markdown renderer (keep the reference stable) |
107
+ | `markdownExtensions` | `readonly MdzipMarkdownRenderExtension[]` | `[]` | Markdown pipeline extensions, diffed by `name` — new array identities with the same names are safe |
108
+ | `entryRenderers` | `readonly MdzipEntryRenderer[]` | `[]` | Entry renderers claiming the content area for matching entries, diffed by `id` — new array identities with the same ids are safe |
109
+ | `frontMatter` | `MdzipFrontMatterOptions` | `{}` | Front matter panel config (`enabled`, `display`, `collapsible`, `label`) — always registered, diffed by deep equality; new object identities with equal contents are safe |
110
+ | `externalLinks` | `MdzipExternalLinkPolicy` | — | External-link policy, e.g. `{ target: '_blank' }` (adds `rel="noopener noreferrer"`); optional `rel` and `predicate(href)`. Re-applies without a re-render; diffed by reference — keep the object stable |
111
+
112
+ `controls`, density, `imageHydrationAnimation`, and rendering input changes apply in
113
+ place — they never recreate the workspace view. Line-number visibility changes
114
+ preserve the current CodeMirror document and selection. See the `@mdzip/editor`
115
+ Rendering Extensibility docs for the contracts and lifecycle rules.
116
+
117
+ ### Native entry rendering (template directives)
118
+
119
+ ```html
120
+ <mdzip-workspace [bytes]="bytes" mode="editable">
121
+ <ng-template mdzipEntryRenderer="manifest.json" let-context>
122
+ <app-internals
123
+ [manifest]="context.manifest"
124
+ [editable]="context.mode === 'editable'"
125
+ (manifestChange)="context.updateManifest($event)"
126
+ />
127
+ </ng-template>
128
+
129
+ <ng-template [mdzipEntryRendererMatch]="isDrawioEntry" let-context>
130
+ <app-drawio-viewer [entry]="context" />
131
+ </ng-template>
132
+ </mdzip-workspace>
133
+ ```
134
+
135
+ Import `MdzipEntryRendererDirective` alongside the component.
136
+ `mdzipEntryRenderer` matches exact archive paths (string or array,
137
+ case-insensitive); `[mdzipEntryRendererMatch]` takes a predicate. Optional
138
+ `mdzipEntryRendererId` / `mdzipEntryRendererPriority` inputs control diffing
139
+ and ordering. Embedded views are created in the component's view container —
140
+ change detection and DI work normally — and are destroyed on selection
141
+ change.
142
+
143
+ ## Outputs
144
+
145
+ | Output | Payload | Description |
146
+ |--------|---------|-------------|
147
+ | `changed` | `MdzipWorkspaceChange` | Emitted when archive bytes change |
148
+ | `saved` | `MdzipWorkspaceSave` | Emitted on Save |
149
+ | `snapshotChanged` | `MdzipWorkspaceSnapshot` | Emitted on any state change |
150
+ | `selectionChanged` | `MdzipWorkspaceSnapshot` | Emitted when the editor selection changes |
151
+ | `dirtyChanged` | `MdzipWorkspaceSnapshot` | Emitted when the dirty flag changes |
152
+ | `validationChanged` | `MdzipWorkspaceSnapshot` | Emitted when validation state changes |
153
+ | `colorSchemeChanged` | `MdzipColorScheme` | Emitted when the color scheme changes |
154
+ | `colorSchemePreferenceChanged` | `MdzipColorSchemePreference` | Emitted when the user picks Light, Dark or System in the toolbar menu |
155
+ | `previewWidthChanged` | `MdzipPreviewWidthChoice` | Emitted when the user picks a width in the toolbar's page-width menu |
156
+ | `previewRendered` | `MdzipWorkspaceSnapshot` | Emitted when the preview HTML is mounted |
157
+ | `assetsHydrated` | `MdzipWorkspaceSnapshot` | Emitted once the mounted preview's images have loaded |
158
+ | `linkActivated` | `MdzipLinkActivatedEvent` | Emitted for every preview link click before built-in handling; call `event.preventDefault()` to take over |
159
+ | `failed` | `unknown` | Emitted on unrecoverable errors |
160
+
161
+ ## Conversion hook
162
+
163
+ `onConversionRequested` is an **input function** (not an output, because it must return a
164
+ value): bind `[onConversionRequested]="handler"` where `handler` is
165
+ `(action: MdzipConversionAction) => boolean | Promise<boolean>`. It fires when the user
166
+ triggers the markdown→MDZ conversion flow (nav button, Insert Image, or image paste on a
167
+ plain `.md`). Return/resolve `true` to take over and suppress the built-in conversion dialog.
168
+
169
+ `onPackRequested` is the same kind of input function for the folder→.mdz packing
170
+ decision surfaced by the exposed `packFilesAsWorkspace(files, options)` method — fires
171
+ only when the file list contains more than one Markdown file, with the same
172
+ return contract as `onConversionRequested`.
173
+
174
+ ## Image insertion
175
+
176
+ Use `[imageInsertMode]="'markdown' | 'ask' | 'html'"` for the built-in image
177
+ markup flow. Bind `[imageInsertHandler]="handler"` for host-owned image UI; the
178
+ handler can return Markdown or HTML sizing/alignment settings, return `null` to
179
+ cancel, or return `undefined` to fall back to `imageInsertMode`.
180
+
181
+ Bind `[imageEditHandler]="handler"` to let users edit an *existing* image by
182
+ clicking it. Fully opt-in — with no handler bound, clicking an image does
183
+ nothing and no edit affordance appears; there is no built-in fallback dialog.
184
+
185
+ ## File management (nav pane)
186
+
187
+ With `controls="standalone-editor"` or `"hosted-editor"` (or `fileActions: true` in a custom
188
+ policy), the navigation pane offers a right-click context menu: new `.md` file, new folder,
189
+ rename/move, duplicate, replace, download, copy markdown link, set entry point, set cover
190
+ image, and delete. Files can also be dragged between folders, and OS files can be dropped
191
+ onto the pane. Copy and Download remain available in read-only mode. The same operations are
192
+ exposed as component methods: `removeFile`, `renameFile`, `setEntryPoint`, and `setCoverImage`.
193
+ `whenRendered()` resolves once the current preview (including its images) is mounted — useful
194
+ for revealing or animating read-only preview content.
195
+
196
+ ## Links and anchors
197
+
198
+ The `linkActivated` output fires for every preview link click, before the view's own handling, with the
199
+ parsed link (`kind`: `'anchor' | 'document' | 'relative' | 'external'`, plus `path`,
200
+ `anchor`, `targetPath`, `sourcePath`, modifier keys). Call `event.preventDefault()` in the
201
+ handler to route the link yourself; unprevented clicks keep the built-in behavior.
202
+ The component also exposes `scrollToAnchor(anchor)` (waits for the preview to mount, resolves whether it
203
+ scrolled; safe straight after load), `getAvailableAnchors()`, `openPath(path, { anchor })` (opens another
204
+ file, optionally at an anchor) and `getCurrentPath()`. Changing only `fileName` (e.g. after Save As)
205
+ renames in place without reopening; set `preserveSelectionOnReopen` to stay on the current file when
206
+ the archive content changes. See the
207
+ [`@mdzip/editor` README](https://www.npmjs.com/package/@mdzip/editor#links-and-anchors).
208
+
209
+ ## Imperative API
210
+
211
+ All imperative methods are public instance methods on `MdzipWorkspaceComponent`,
212
+ so any reference to the component instance works. No view query is required: a
213
+ template reference variable can hand the instance directly to an event handler.
214
+
215
+ ```ts
216
+ @Component({
217
+ imports: [MdzipWorkspaceComponent],
218
+ template: `
219
+ <mdzip-workspace #workspace [bytes]="bytes" mode="editable" controls="hosted-editor" />
220
+ <button (click)="save(workspace)">Save</button>
221
+ `,
222
+ })
223
+ export class AppComponent {
224
+ async save(workspace: MdzipWorkspaceComponent): Promise<void> {
225
+ // Flush pending edits and persist
226
+ const snapshot = await workspace.flush();
227
+ if (snapshot) {
228
+ await persist(snapshot.bytes);
229
+ workspace.markPersisted();
230
+ }
231
+
232
+ // Execute editor commands
233
+ await workspace.executeCommand('bold');
234
+ }
235
+ }
236
+ ```
237
+
238
+ To call methods outside an event handler, use a view query — the signal-based
239
+ `viewChild` or the classic `@ViewChild` decorator:
240
+
241
+ ```ts
242
+ private readonly workspace = viewChild.required(MdzipWorkspaceComponent);
243
+
244
+ async save(): Promise<void> {
245
+ const snapshot = await this.workspace().flush();
246
+ if (snapshot) {
247
+ await persist(snapshot.bytes);
248
+ this.workspace().markPersisted();
249
+ }
250
+ }
251
+ ```
252
+
253
+ Methods called before the view initializes are safe: they no-op and resolve to
254
+ `false`/`null`/empty rather than throwing.
255
+
256
+ See the [`@mdzip/editor`](https://www.npmjs.com/package/@mdzip/editor) package for the full API reference, theming guide, and framework-agnostic usage.