Skip to content

Commit 7906e81

Browse files
committed
fix #205
1 parent ebbedb7 commit 7906e81

File tree

11 files changed

+138
-3
lines changed

11 files changed

+138
-3
lines changed

exampleVault/Button Example.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,21 @@ actions:
168168
169169
```
170170

171+
```meta-bind-button
172+
label: Create new Note
173+
hidden: false
174+
class: ""
175+
tooltip: ""
176+
id: ""
177+
style: default
178+
actions:
179+
- type: createNote
180+
folderPath: templates
181+
fileName: asdasd
182+
openNote: false
183+
184+
```
185+
171186
### Modifying Front-matter
172187

173188
```meta-bind-button

packages/core/src/api/InternalAPI.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ export abstract class InternalAPI<Plugin extends IPlugin> {
212212
*/
213213
abstract readFilePath(filePath: string): Promise<string>;
214214

215+
abstract createFile(folderPath: string, fileName: string, extension: string, open?: boolean): Promise<string>;
216+
215217
abstract createContextMenu(items: ContextMenuItemDefinition[]): IContextMenu;
216218

217219
openCommandSelectModal(selectCallback: (selected: Command) => void): void {

packages/core/src/config/ButtonConfig.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export enum ButtonActionType {
1313
SLEEP = 'sleep',
1414
TEMPLATER_CREATE_NOTE = 'templaterCreateNote',
1515
UPDATE_METADATA = 'updateMetadata',
16+
CREATE_NOTE = 'createNote',
1617
}
1718

1819
export interface CommandButtonAction {
@@ -57,14 +58,22 @@ export interface UpdateMetadataButtonAction {
5758
value: string;
5859
}
5960

61+
export interface CreateNoteButtonAction {
62+
type: ButtonActionType.CREATE_NOTE;
63+
folderPath?: string;
64+
fileName: string;
65+
openNote?: boolean;
66+
}
67+
6068
export type ButtonAction =
6169
| CommandButtonAction
6270
| JSButtonAction
6371
| OpenButtonAction
6472
| InputButtonAction
6573
| SleepButtonAction
6674
| TemplaterCreateNoteButtonAction
67-
| UpdateMetadataButtonAction;
75+
| UpdateMetadataButtonAction
76+
| CreateNoteButtonAction;
6877

6978
export interface ButtonConfig {
7079
label: string;

packages/core/src/config/ButtonConfigValidators.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
type ButtonConfig,
55
ButtonStyleType,
66
type CommandButtonAction,
7+
type CreateNoteButtonAction,
78
type InputButtonAction,
89
type JSButtonAction,
910
type OpenButtonAction,
@@ -69,6 +70,15 @@ export const V_UpdateMetadataButtonAction = schemaForType<UpdateMetadataButtonAc
6970
}),
7071
);
7172

73+
export const V_CreateNoteButtonAction = schemaForType<CreateNoteButtonAction>()(
74+
z.object({
75+
type: z.literal(ButtonActionType.CREATE_NOTE),
76+
folderPath: z.string().optional(),
77+
fileName: z.string(),
78+
openNote: z.boolean().optional(),
79+
}),
80+
);
81+
7282
export const V_ButtonAction = schemaForType<ButtonAction>()(
7383
z.union([
7484
V_CommandButtonAction,
@@ -78,6 +88,7 @@ export const V_ButtonAction = schemaForType<ButtonAction>()(
7888
V_SleepButtonAction,
7989
V_TemplaterCreateNoteButtonAction,
8090
V_UpdateMetadataButtonAction,
91+
V_CreateNoteButtonAction,
8192
]),
8293
);
8394

packages/core/src/fields/button/ButtonActionRunner.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
type ButtonConfig,
66
ButtonStyleType,
77
type CommandButtonAction,
8+
type CreateNoteButtonAction,
89
type InputButtonAction,
910
type JSButtonAction,
1011
type OpenButtonAction,
@@ -14,7 +15,7 @@ import {
1415
} from 'packages/core/src/config/ButtonConfig';
1516
import { MDLinkParser } from 'packages/core/src/parsers/MarkdownLinkParser';
1617
import { Signal } from 'packages/core/src/utils/Signal';
17-
import { getUUID, openURL } from 'packages/core/src/utils/Utils';
18+
import { expectType, getUUID, openURL } from 'packages/core/src/utils/Utils';
1819

1920
export class ButtonActionRunner {
2021
plugin: IPlugin;
@@ -81,7 +82,7 @@ export class ButtonActionRunner {
8182
return {
8283
type: ButtonActionType.TEMPLATER_CREATE_NOTE,
8384
templateFile: '',
84-
folderPath: '',
85+
folderPath: '/',
8586
fileName: '',
8687
openNote: true,
8788
} satisfies TemplaterCreateNoteButtonAction;
@@ -92,8 +93,17 @@ export class ButtonActionRunner {
9293
evaluate: false,
9394
value: '',
9495
} satisfies UpdateMetadataButtonAction;
96+
} else if (type === ButtonActionType.CREATE_NOTE) {
97+
return {
98+
type: ButtonActionType.CREATE_NOTE,
99+
folderPath: '/',
100+
fileName: 'Untitled',
101+
openNote: true,
102+
} satisfies CreateNoteButtonAction;
95103
}
96104

105+
expectType<never>(type);
106+
97107
throw new Error(`Unknown button action type: ${type}`);
98108
}
99109

@@ -127,8 +137,13 @@ export class ButtonActionRunner {
127137
} else if (action.type === ButtonActionType.UPDATE_METADATA) {
128138
await this.runUpdateMetadataAction(action, filePath);
129139
return;
140+
} else if (action.type === ButtonActionType.CREATE_NOTE) {
141+
await this.runCreateNoteAction(action);
142+
return;
130143
}
131144

145+
expectType<never>(action);
146+
132147
throw new Error(`Unknown button action type`);
133148
}
134149

@@ -181,4 +196,8 @@ export class ButtonActionRunner {
181196
});
182197
subscription.unsubscribe();
183198
}
199+
200+
async runCreateNoteAction(action: CreateNoteButtonAction): Promise<void> {
201+
await this.plugin.internal.createFile(action.folderPath ?? '', action.fileName, 'md', action.openNote ?? false);
202+
}
184203
}

packages/core/src/modals/modalContents/ButtonBuilderModalComponent.svelte

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@
8686
});
8787
}
8888
89+
function createNoteActionChangeFolderPath(action: ButtonAction) {
90+
if (action.type !== ButtonActionType.CREATE_NOTE) {
91+
return;
92+
}
93+
94+
plugin.internal.openFolderSelectModal((folder: string) => {
95+
action.folderPath = folder;
96+
buttonConfig.actions = buttonConfig.actions;
97+
});
98+
}
99+
89100
function getActionLabel(actionType: ButtonActionType): string {
90101
if (actionType === ButtonActionType.COMMAND) {
91102
return 'Run a Command';
@@ -101,6 +112,8 @@
101112
return 'Create a New Note Using Templater';
102113
} else if (actionType === ButtonActionType.UPDATE_METADATA) {
103114
return 'Update Metadata';
115+
} else if (actionType === ButtonActionType.CREATE_NOTE) {
116+
return 'Create a New Note';
104117
}
105118
106119
return 'CHANGE ME';
@@ -254,6 +267,27 @@ Add action of type
254267
<Toggle bind:checked={action.evaluate}></Toggle>
255268
</SettingComponent>
256269
{/if}
270+
271+
{#if action.type === ButtonActionType.CREATE_NOTE}
272+
<Button variant={ButtonStyleType.DESTRUCTIVE} on:click={() => removeAction(i)}>Remove Action</Button>
273+
274+
<SettingComponent
275+
name="Folder: {action.folderPath || 'none'}"
276+
description="The folder to create a new note in."
277+
>
278+
<Button variant={ButtonStyleType.PRIMARY} on:click={() => createNoteActionChangeFolderPath(action)}
279+
>Change</Button
280+
>
281+
</SettingComponent>
282+
283+
<SettingComponent name="File Name: {action.fileName || 'default'}" description="The file name of the new note.">
284+
<input type="text" bind:value={action.fileName} placeholder="some name" />
285+
</SettingComponent>
286+
287+
<SettingComponent name="Open Note" description="Whether to open the new note after this action ran.">
288+
<Toggle bind:checked={action.openNote}></Toggle>
289+
</SettingComponent>
290+
{/if}
257291
{/each}
258292

259293
<h4>Preview</h4>

packages/core/src/utils/Utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,11 @@ export class DomHelpers {
250250
el.innerHTML = '';
251251
}
252252
}
253+
254+
export function getFolderPathFromFilePath(filePath: string): string {
255+
const pathSeparator = filePath.lastIndexOf('/');
256+
if (pathSeparator === -1) {
257+
return '';
258+
}
259+
return filePath.substring(0, pathSeparator);
260+
}

packages/obsidian/extraTypes/obsidian-ex.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ declare module 'obsidian' {
7070
disabled: boolean;
7171
setWarning: (warning: boolean) => void;
7272
}
73+
74+
interface Vault {
75+
/**
76+
* @internal Get path for file that does not conflict with other existing files
77+
*/
78+
getAvailablePath(path: string, extension: string): string;
79+
80+
/**
81+
* Get an abstract file by path, insensitive to case
82+
*/
83+
getAbstractFileByPathInsensitive(path: string): TAbstractFile | null;
84+
}
7385
}
7486

7587
declare global {

packages/obsidian/src/ObsidianInternalAPI.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
type App,
33
Component,
44
MarkdownRenderer,
5+
normalizePath,
56
Notice,
67
parseYaml,
78
setIcon,
@@ -199,4 +200,20 @@ export class ObsidianInternalAPI extends InternalAPI<MetaBindPlugin> {
199200
menu.setItems(items);
200201
return menu;
201202
}
203+
204+
public async createFile(folderPath: string, fileName: string, extension: string, open?: boolean): Promise<string> {
205+
const path = this.app.vault.getAvailablePath(normalizePath(folderPath + '/' + fileName), extension);
206+
const newFile = await this.app.vault.create(path, '');
207+
208+
if (open) {
209+
const activeLeaf = this.app.workspace.getLeaf(false);
210+
if (activeLeaf) {
211+
await activeLeaf.openFile(newFile, {
212+
state: { mode: 'source' },
213+
});
214+
}
215+
}
216+
217+
return newFile.path;
218+
}
202219
}

packages/publish/src/PublishInternalAPI.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ export class PublishInternalAPI extends InternalAPI<MetaBindPublishPlugin> {
105105
return Promise.resolve('');
106106
}
107107

108+
public createFile(_folderPath: string, _fileName: string, _extension: string, _open?: boolean): Promise<string> {
109+
return Promise.resolve('');
110+
}
111+
108112
public createContextMenu(_items: ContextMenuItemDefinition[]): IContextMenu {
109113
throw new Error('not implemented');
110114
}

0 commit comments

Comments
 (0)