Hello!
When you create a note, Obsidian will highlight the “Untitled” in the header of the file view so the user can already type a name for that Note (this is the behavior if you disable Inline title). Also, you can always click on the title to rename the file.
I’m developing a plugin for Obsidian that extends from the file TextFileView, and I would like to reproduce this behavior given that my view doesn’t have any line title and also because it’s a perfect user flow, from my opinion.
I tried to find in the documentation and here how to reproduce this behavior, but without success. Can someone share some light on how I could build this?
Thank you!
Ok, I did a more deep research and I want to share the solution I found. The main problem was that since my extension uses a file that’s not in Markdown format, some built-in behavior is not available by default.
There are 2 things that enable this behavior:
- Calling `await super.onOpen();` and `await super.onClose();` on their respectives extended methods in your View class, if they are extended.
- Calling the `createNewMarkdownFile` method when opening the the View
A good example can be found in obsidian-kanban plugin, check their main.ts file in Github.
However, if your plugin doesn’t work with Markdown, you need to implement this behavior directly in your method that creates a new file:
// The header element becomes contenteditable asynchronously after setViewState,
// so defer one tick before selecting it.
setTimeout(() => {
const titleEl = leaf.view.containerEl
.closest(".workspace-leaf")
?.querySelector<HTMLElement>(".view-header-title");
if (titleEl) {
titleEl.focus();
const range = document.createRange();
range.selectNodeContents(titleEl);
const sel = window.getSelection();
sel?.removeAllRanges();
sel?.addRange(range);
}
}, 0);
Would be great if this behavior gets exposed in the API so we can reuse it, as there’s a risk that this might fail in the future. But it worked for now.
I found that the key code lies in:
this.app.workspace.getLeaf().openFile(newFile, { eState: { rename: "all" } });
Complete test code to register a View for .test files:
import { Plugin, TextFileView } from "obsidian";
export default class Test extends Plugin {
async onload() {
this.registerView("test-view", (leaf) => new TestView(leaf));
this.registerExtensions(["test"], "test-view");
this.app.fileManager.registerFileParentCreator("test", () => this.app.vault.getRoot());
this.addRibbonIcon("dice", "Test", async () => {
const newFile = await this.app.fileManager.createNewFile(null, "New Test File", "test", "Random content for test");
// open new file and rename
this.app.workspace.getLeaf().openFile(newFile, { eState: { rename: "all" } });
});
}
}
class TestView extends TextFileView {
getViewData(): string {
throw new Error("Method not implemented.");
}
setViewData(data: string, clear: boolean): void {
this.contentEl.innerHTML = `<h1>${data}</h1>`;
}
clear(): void {
}
getViewType(): string {
return "test"
}
}
1 Like
Thank you @the_tree, that’s a pretty good finding!
I tested and can confirm that this is the correct solution.