mirror of
https://github.com/lukasbach/obsidian-code-files.git
synced 2026-07-22 07:40:30 +00:00
create file modal
This commit is contained in:
parent
dfe0e5a7b7
commit
5206272008
9 changed files with 428 additions and 294 deletions
|
|
@ -16,7 +16,7 @@ const context = await esbuild.context({
|
|||
banner: {
|
||||
js: banner,
|
||||
},
|
||||
entryPoints: ["main.ts"],
|
||||
entryPoints: ["src/main.ts"],
|
||||
bundle: true,
|
||||
// plugins: [monacoPlugin({languages: ["typescript", "json", "css", "html"]})],
|
||||
// loader: {
|
||||
|
|
|
|||
286
main.ts
286
main.ts
|
|
@ -1,286 +0,0 @@
|
|||
import {
|
||||
App, DropdownComponent,
|
||||
Editor,
|
||||
MarkdownView,
|
||||
Modal,
|
||||
Notice,
|
||||
Plugin,
|
||||
PluginSettingTab,
|
||||
Setting, TAbstractFile, TextComponent,
|
||||
TextFileView, TFile,
|
||||
View
|
||||
} from 'obsidian';
|
||||
import * as path from "path";
|
||||
import {EditorState, Compartment} from "@codemirror/state"
|
||||
import {EditorView, keymap} from "@codemirror/view"
|
||||
import {defaultKeymap} from "@codemirror/commands"
|
||||
import { javascript } from "@codemirror/lang-javascript";
|
||||
import { json } from "@codemirror/lang-json";
|
||||
import { python } from "@codemirror/lang-python";
|
||||
|
||||
// Remember to rename these classes and interfaces!
|
||||
|
||||
|
||||
|
||||
interface MyPluginSettings {
|
||||
mySetting: string;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: MyPluginSettings = {
|
||||
mySetting: 'default'
|
||||
}
|
||||
|
||||
const viewType = "code-editor";
|
||||
|
||||
let i = 0;
|
||||
class CodeEditorView extends TextFileView {
|
||||
id = i++;
|
||||
value: string = "";
|
||||
iframe: HTMLIFrameElement;
|
||||
|
||||
getDisplayText(): string {
|
||||
console.log("!!getDisplayText", this.id)
|
||||
return this.file?.name;
|
||||
}
|
||||
|
||||
getViewType(): string {
|
||||
return viewType;
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
console.log("!!onClose", this.id)
|
||||
this.iframe.remove();
|
||||
}
|
||||
|
||||
async onLoadFile(file: TFile) {
|
||||
await super.onLoadFile(file);
|
||||
console.log("!!onLoadFile", this.id, file.name)
|
||||
this.send("change-language", { language: this.getLanguage() });
|
||||
}
|
||||
|
||||
async onUnloadFile(file: TFile) {
|
||||
console.log("!!onUnloadFile", this.id, file.name)
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
console.log("!!onOpen", this.id)
|
||||
this.iframe = document.createElement("iframe");
|
||||
this.iframe.src = `https://embeddable-monaco.lukasbach.com?lang=${this.getLanguage()}`;
|
||||
this.iframe.style.width = "100%";
|
||||
this.iframe.style.height = "100%";
|
||||
(this.containerEl.getElementsByClassName("view-content")[0] as HTMLElement).style.overflow = "hidden";
|
||||
this.containerEl.getElementsByClassName("view-content")[0].append(this.iframe);
|
||||
window.addEventListener("message", ({ data }) => {
|
||||
switch(data.type) {
|
||||
case "ready": {
|
||||
this.send("change-value", { value: this.value });
|
||||
this.send("change-background", { background: "transparent", theme: "vs-dark" });
|
||||
break;
|
||||
}
|
||||
case "change": {
|
||||
this.value = data.value;
|
||||
this.requestSave();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
console.log("!!clear", this.id)
|
||||
this.value = "";
|
||||
this.send("change-value", { value: "" });
|
||||
}
|
||||
|
||||
getViewData(): string {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
setViewData(data: string, clear = false): void {
|
||||
console.log("!!set view data", this.id, data, clear)
|
||||
this.value = data;
|
||||
this.send("change-value", { value: data });
|
||||
}
|
||||
|
||||
getLanguage() {
|
||||
switch (this.file?.extension) {
|
||||
case "js":
|
||||
case "jsx":
|
||||
return "javascript";
|
||||
case "ts":
|
||||
case "tsx":
|
||||
return "typescript";
|
||||
case "json":
|
||||
return "json";
|
||||
case "py":
|
||||
return "python";
|
||||
case "css":
|
||||
return "css";
|
||||
case "html":
|
||||
return "html";
|
||||
case "cpp":
|
||||
return "cpp";
|
||||
case "graphql":
|
||||
return "graphql";
|
||||
case "java":
|
||||
return "java";
|
||||
case "php":
|
||||
return "php";
|
||||
case "sql":
|
||||
return "sql";
|
||||
case "yaml":
|
||||
case "yml":
|
||||
return "yaml";
|
||||
default:
|
||||
return "plaintext";
|
||||
}
|
||||
}
|
||||
|
||||
send(type: string, payload: any) {
|
||||
console.log("!!send", !!this.iframe.contentWindow)
|
||||
this.iframe.contentWindow?.postMessage({
|
||||
type,
|
||||
...payload
|
||||
}, "*");
|
||||
}
|
||||
}
|
||||
|
||||
export default class CodeFilesPlugin extends Plugin {
|
||||
settings: MyPluginSettings;
|
||||
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
|
||||
this.registerView(viewType, leaf => new CodeEditorView(leaf));
|
||||
this.registerExtensions(["ts", "tsx", "txt"], viewType);
|
||||
|
||||
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("file-menu", (menu, file) => {
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle("Print file path 👈")
|
||||
.setIcon("document")
|
||||
.onClick(async () => {
|
||||
new SampleModal(this.app, file).open();
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
|
||||
// This creates an icon in the left ribbon.
|
||||
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
|
||||
// Called when the user clicks the icon.
|
||||
new Notice('This is a notice!');
|
||||
});
|
||||
// Perform additional things with the ribbon
|
||||
ribbonIconEl.addClass('my-plugin-ribbon-class');
|
||||
|
||||
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
|
||||
const statusBarItemEl = this.addStatusBarItem();
|
||||
statusBarItemEl.setText('Status Bar Text');
|
||||
|
||||
// This adds a simple command that can be triggered anywhere
|
||||
this.addCommand({
|
||||
id: 'open-sample-modal-simple',
|
||||
name: 'Open sample modal (simple)',
|
||||
callback: () => {
|
||||
}
|
||||
});
|
||||
// This adds an editor command that can perform some operation on the current editor instance
|
||||
this.addCommand({
|
||||
id: 'sample-editor-command',
|
||||
name: 'Sample editor command',
|
||||
editorCallback: (editor: Editor, view: MarkdownView) => {
|
||||
console.log(editor.getSelection());
|
||||
editor.replaceSelection('Sample Editor Command');
|
||||
}
|
||||
});
|
||||
// This adds a complex command that can check whether the current state of the app allows execution of the command
|
||||
this.addCommand({
|
||||
id: 'open-sample-modal-complex',
|
||||
name: 'Open sample modal (complex)',
|
||||
checkCallback: (checking: boolean) => {
|
||||
// Conditions to check
|
||||
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (markdownView) {
|
||||
// If checking is true, we're simply "checking" if the command can be run.
|
||||
// If checking is false, then we want to actually perform the operation.
|
||||
if (!checking) {
|
||||
}
|
||||
|
||||
// This command will only show up in Command Palette when the check function returns true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// This adds a settings tab so the user can configure various aspects of the plugin
|
||||
this.addSettingTab(new CodeFilesSettingsTab(this.app, this));
|
||||
}
|
||||
|
||||
onunload() {
|
||||
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
|
||||
class SampleModal extends Modal {
|
||||
constructor(app: App, private file: TAbstractFile) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
contentEl.style.display = "flex"
|
||||
const fileNameInput = new TextComponent(contentEl);
|
||||
|
||||
const fileExtensionInput = new DropdownComponent(contentEl);
|
||||
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
class CodeFilesSettingsTab extends PluginSettingTab {
|
||||
plugin: CodeFilesPlugin;
|
||||
|
||||
constructor(app: App, plugin: CodeFilesPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Setting #1')
|
||||
.setDesc('It\'s a secret')
|
||||
.addText(text => text
|
||||
.setPlaceholder('Enter your secret')
|
||||
.setValue(this.plugin.settings.mySetting)
|
||||
.onChange(async (value) => {
|
||||
console.log('Secret: ' + value);
|
||||
this.plugin.settings.mySetting = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"id": "obsidian-sample-plugin",
|
||||
"name": "Sample Plugin",
|
||||
"id": "obsidian-code-files-plugin",
|
||||
"name": "Code Files",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.",
|
||||
"author": "Obsidian",
|
||||
"authorUrl": "https://obsidian.md",
|
||||
"fundingUrl": "https://obsidian.md/pricing",
|
||||
"isDesktopOnly": false
|
||||
"description": "Edit Code Files in Obsidian with VSCode's powerful Monaco Editor",
|
||||
"author": "Lukas Bach",
|
||||
"authorUrl": "https://lukasbach.com",
|
||||
"fundingUrl": "https://github.com/lukasbach/.github/blob/main/funding.md",
|
||||
"isDesktopOnly": true
|
||||
}
|
||||
|
|
|
|||
121
src/codeEditorView.ts
Normal file
121
src/codeEditorView.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import {TextFileView, TFile, WorkspaceLeaf} from "obsidian";
|
||||
import {viewType} from "./common";
|
||||
import CodeFilesPlugin from "./codeFilesPlugin";
|
||||
|
||||
export class CodeEditorView extends TextFileView {
|
||||
static i = 0;
|
||||
id = CodeEditorView.i++;
|
||||
value: string = "";
|
||||
iframe: HTMLIFrameElement;
|
||||
|
||||
constructor(leaf: WorkspaceLeaf, private plugin: CodeFilesPlugin) {
|
||||
super(leaf);
|
||||
}
|
||||
|
||||
getDisplayText(): string {
|
||||
console.log("!!getDisplayText", this.id)
|
||||
return this.file?.name;
|
||||
}
|
||||
|
||||
getViewType(): string {
|
||||
return viewType;
|
||||
}
|
||||
|
||||
async onClose() {
|
||||
console.log("!!onClose", this.id)
|
||||
this.iframe.remove();
|
||||
}
|
||||
|
||||
async onLoadFile(file: TFile) {
|
||||
await super.onLoadFile(file);
|
||||
console.log("!!onLoadFile", this.id, file.name)
|
||||
this.send("change-language", {language: this.getLanguage()});
|
||||
}
|
||||
|
||||
async onUnloadFile(file: TFile) {
|
||||
console.log("!!onUnloadFile", this.id, file.name)
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
console.log("!!onOpen", this.id)
|
||||
const theme = this.plugin.settings.isDark ? "vs-dark" : "vs-light";
|
||||
this.iframe = document.createElement("iframe");
|
||||
this.iframe.src = `https://embeddable-monaco.lukasbach.com?lang=${this.getLanguage()}&theme=${theme}&background=transparent`;
|
||||
this.iframe.style.width = "100%";
|
||||
this.iframe.style.height = "100%";
|
||||
(this.containerEl.getElementsByClassName("view-content")[0] as HTMLElement).style.overflow = "hidden";
|
||||
this.containerEl.getElementsByClassName("view-content")[0].append(this.iframe);
|
||||
window.addEventListener("message", ({data}) => {
|
||||
switch (data.type) {
|
||||
case "ready": {
|
||||
this.send("change-value", {value: this.value});
|
||||
this.send("change-background", {background: "transparent", theme: "vs-dark"});
|
||||
break;
|
||||
}
|
||||
case "change": {
|
||||
this.value = data.value;
|
||||
this.requestSave();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
console.log("!!clear", this.id)
|
||||
this.value = "";
|
||||
this.send("change-value", {value: ""});
|
||||
}
|
||||
|
||||
getViewData(): string {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
setViewData(data: string, clear = false): void {
|
||||
console.log("!!set view data", this.id, data, clear)
|
||||
this.value = data;
|
||||
this.send("change-value", {value: data});
|
||||
}
|
||||
|
||||
getLanguage() {
|
||||
switch (this.file?.extension) {
|
||||
case "js":
|
||||
case "jsx":
|
||||
return "javascript";
|
||||
case "ts":
|
||||
case "tsx":
|
||||
return "typescript";
|
||||
case "json":
|
||||
return "json";
|
||||
case "py":
|
||||
return "python";
|
||||
case "css":
|
||||
return "css";
|
||||
case "html":
|
||||
return "html";
|
||||
case "cpp":
|
||||
return "cpp";
|
||||
case "graphql":
|
||||
return "graphql";
|
||||
case "java":
|
||||
return "java";
|
||||
case "php":
|
||||
return "php";
|
||||
case "sql":
|
||||
return "sql";
|
||||
case "yaml":
|
||||
case "yml":
|
||||
return "yaml";
|
||||
default:
|
||||
return "plaintext";
|
||||
}
|
||||
}
|
||||
|
||||
send(type: string, payload: any) {
|
||||
console.log("!!send", !!this.iframe.contentWindow)
|
||||
this.iframe.contentWindow?.postMessage({
|
||||
type,
|
||||
...payload
|
||||
}, "*");
|
||||
}
|
||||
}
|
||||
95
src/codeFilesPlugin.ts
Normal file
95
src/codeFilesPlugin.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import {Editor, MarkdownView, Notice, Plugin} from "obsidian";
|
||||
import {DEFAULT_SETTINGS, MyPluginSettings} from "./common";
|
||||
import {CodeEditorView} from "./codeEditorView";
|
||||
import {CreateCodeFileModal} from "./createCodeFileModal";
|
||||
import {CodeFilesSettingsTab} from "./codeFilesSettingsTab";
|
||||
import {viewType} from "./common";
|
||||
export default class CodeFilesPlugin extends Plugin {
|
||||
settings: MyPluginSettings;
|
||||
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
|
||||
this.registerView(viewType, leaf => new CodeEditorView(leaf, this));
|
||||
this.registerExtensions(this.settings.extensions, viewType);
|
||||
|
||||
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("file-menu", (menu, file) => {
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle("Create Code File")
|
||||
.setIcon("file-json")
|
||||
.onClick(async () => {
|
||||
new CreateCodeFileModal(this, file).open();
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
// This creates an icon in the left ribbon.
|
||||
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
|
||||
// Called when the user clicks the icon.
|
||||
new Notice('This is a notice!');
|
||||
});
|
||||
// Perform additional things with the ribbon
|
||||
ribbonIconEl.addClass('my-plugin-ribbon-class');
|
||||
|
||||
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
|
||||
const statusBarItemEl = this.addStatusBarItem();
|
||||
statusBarItemEl.setText('Status Bar Text');
|
||||
|
||||
// This adds a simple command that can be triggered anywhere
|
||||
this.addCommand({
|
||||
id: 'open-sample-modal-simple',
|
||||
name: 'Open sample modal (simple)',
|
||||
callback: () => {
|
||||
}
|
||||
});
|
||||
// This adds an editor command that can perform some operation on the current editor instance
|
||||
this.addCommand({
|
||||
id: 'sample-editor-command',
|
||||
name: 'Sample editor command',
|
||||
editorCallback: (editor: Editor, view: MarkdownView) => {
|
||||
console.log(editor.getSelection());
|
||||
editor.replaceSelection('Sample Editor Command');
|
||||
}
|
||||
});
|
||||
// This adds a complex command that can check whether the current state of the app allows execution of the command
|
||||
this.addCommand({
|
||||
id: 'open-sample-modal-complex',
|
||||
name: 'Open sample modal (complex)',
|
||||
checkCallback: (checking: boolean) => {
|
||||
// Conditions to check
|
||||
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (markdownView) {
|
||||
// If checking is true, we're simply "checking" if the command can be run.
|
||||
// If checking is false, then we want to actually perform the operation.
|
||||
if (!checking) {
|
||||
}
|
||||
|
||||
// This command will only show up in Command Palette when the check function returns true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// This adds a settings tab so the user can configure various aspects of the plugin
|
||||
this.addSettingTab(new CodeFilesSettingsTab(this.app, this));
|
||||
}
|
||||
|
||||
onunload() {
|
||||
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
92
src/codeFilesSettingsTab.ts
Normal file
92
src/codeFilesSettingsTab.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import {App, PluginSettingTab, Setting} from "obsidian";
|
||||
import CodeFilesPlugin from "./codeFilesPlugin";
|
||||
|
||||
export class CodeFilesSettingsTab extends PluginSettingTab {
|
||||
plugin: CodeFilesPlugin;
|
||||
|
||||
constructor(app: App, plugin: CodeFilesPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'});
|
||||
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('File Extensions')
|
||||
.setDesc('Files with these extensions will show up in the sidebar, and will ' +
|
||||
'be available to create new files from. Seperated by commas.')
|
||||
.addText(text => text
|
||||
.setPlaceholder('js,ts')
|
||||
.setValue(this.plugin.settings.extensions.join(","))
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.extensions = value.split(",");
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Folding')
|
||||
.setDesc('Editor will support code block folding.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.folding)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.folding = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Line Numbers')
|
||||
.setDesc('Editor will show line numbers.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.lineNumbers)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.lineNumbers = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Minimap')
|
||||
.setDesc('Editor will show a minimap.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.minimap)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.minimap = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Semantic Validation')
|
||||
.setDesc('Editor will show semantic validation errors.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.semanticValidation)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.semanticValidation = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Syntax Validation')
|
||||
.setDesc('Editor will show syntax validation errors.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.syntaxValidation)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.syntaxValidation = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Dark Mode')
|
||||
.setDesc('Is obsidian running with a dark theme?')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.isDark)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.isDark = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
||||
23
src/common.ts
Normal file
23
src/common.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
export interface MyPluginSettings {
|
||||
extensions: string[];
|
||||
folding: boolean;
|
||||
lineNumbers: boolean;
|
||||
minimap: boolean;
|
||||
semanticValidation: boolean;
|
||||
syntaxValidation: boolean;
|
||||
isDark: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: MyPluginSettings = {
|
||||
extensions: ["ts", "tsx", "js", "jsx", "json", "html", "py"],
|
||||
folding: true,
|
||||
lineNumbers: true,
|
||||
minimap: true,
|
||||
semanticValidation: true,
|
||||
syntaxValidation: true,
|
||||
isDark: true,
|
||||
}
|
||||
|
||||
|
||||
export const viewType = "code-editor";
|
||||
86
src/createCodeFileModal.ts
Normal file
86
src/createCodeFileModal.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import {
|
||||
App,
|
||||
ButtonComponent,
|
||||
DropdownComponent,
|
||||
Modal,
|
||||
Notice,
|
||||
TAbstractFile,
|
||||
TextComponent,
|
||||
TFile,
|
||||
TFolder
|
||||
} from "obsidian";
|
||||
import CodeFilesPlugin from "./codeFilesPlugin";
|
||||
|
||||
export class CreateCodeFileModal extends Modal {
|
||||
fileName = "My Code File";
|
||||
fileExtension = this.plugin.settings.extensions[0];
|
||||
|
||||
constructor(private plugin: CodeFilesPlugin, private parent: TAbstractFile) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
contentEl.style.display = "flex"
|
||||
contentEl.style.alignItems = "center";
|
||||
const fileNameInput = new TextComponent(contentEl);
|
||||
fileNameInput.inputEl.style.flexGrow = "1";
|
||||
fileNameInput.inputEl.style.marginRight = "10px";
|
||||
fileNameInput.setValue(this.fileName);
|
||||
fileNameInput.inputEl.addEventListener("keypress", e => {
|
||||
if (e.key === "Enter") {
|
||||
this.complete();
|
||||
}
|
||||
});
|
||||
fileNameInput.onChange(value => this.fileName = value);
|
||||
|
||||
const fileExtensionInput = new DropdownComponent(contentEl);
|
||||
fileExtensionInput.selectEl.style.marginRight = "10px";
|
||||
fileExtensionInput.addOptions(this.plugin.settings.extensions.reduce((acc, ext) => {
|
||||
acc[ext] = ext;
|
||||
return acc;
|
||||
}, {} as any));
|
||||
fileExtensionInput.setValue(this.fileExtension);
|
||||
fileExtensionInput.onChange(value => this.fileExtension = value);
|
||||
|
||||
fileExtensionInput.selectEl.addEventListener("keypress", e => {
|
||||
if (e.key === "Enter") {
|
||||
this.complete();
|
||||
}
|
||||
});
|
||||
|
||||
const submitButton = new ButtonComponent(contentEl);
|
||||
submitButton.setCta();
|
||||
submitButton.setButtonText("Create");
|
||||
submitButton.onClick(() => this.complete());
|
||||
|
||||
fileNameInput.inputEl.focus();
|
||||
}
|
||||
|
||||
async complete() {
|
||||
this.close();
|
||||
const parent = (this.parent instanceof TFile ? this.parent.parent : this.parent) as TFolder;
|
||||
const newPath = `${parent.path}/${this.fileName}.${this.fileExtension}`;
|
||||
|
||||
const existingFile = this.app.vault.getAbstractFileByPath(newPath);
|
||||
if (existingFile) {
|
||||
new Notice("File already exists");
|
||||
const leaf = this.app.workspace.getLeaf(true);
|
||||
leaf.openFile(existingFile as any);
|
||||
return;
|
||||
}
|
||||
|
||||
const newFile = await this.app.vault.create(
|
||||
newPath,
|
||||
"",
|
||||
{}
|
||||
);
|
||||
const leaf = this.app.workspace.getLeaf(true);
|
||||
leaf.openFile(newFile);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
3
src/main.ts
Normal file
3
src/main.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import plugin from "./codeFilesPlugin";
|
||||
|
||||
export default plugin;
|
||||
Loading…
Reference in a new issue