Build out lib dir
This commit is contained in:
parent
b93e472f8d
commit
dea972cfda
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -20,3 +20,5 @@ data.json
|
||||||
|
|
||||||
# Exclude macOS Finder (System Explorer) View States
|
# Exclude macOS Finder (System Explorer) View States
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
dist
|
|
@ -6,9 +6,10 @@ This plugin manages a folder in your Obsidian Vault which contains "Scraps": fil
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
- `Scraps: Create new Scrap` - Creates a new file based on the plugin settings
|
- `Scraps: Create new Scrap` - Creates a new file based on the plugin settings.
|
||||||
- `Scraps: Convert current file to Scrap` - Moves and renames the current file to be a scrap
|
- `Scraps: Move current file to Scraps` - Moves and the current file into the current Scrap directory. Does not rename the file.
|
||||||
- `Scraps: Copy current file to Scraps` - Creates a copy of the current file as a scrap
|
- `Scraps: Copy current file to Scraps` - Creates a copy of the current file in the current Scrap directory. Does not rename the file.
|
||||||
|
- `Scraps: Rename current file as Scrap` - Renames the current file using the scrap file name settings and moves the file into the current Scrap directory.
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,28 @@
|
||||||
import { moment } from "obsidian";
|
import { moment } from "obsidian";
|
||||||
import { adjectives } from "adjectives.js";
|
|
||||||
import { capitalize, sample } from "lodash-es";
|
import { capitalize, sample } from "lodash-es";
|
||||||
import { nouns } from "nouns.js";
|
import { adjectives } from "./resource/adjectives.js";
|
||||||
|
import { nouns } from "./resource/nouns.js";
|
||||||
|
|
||||||
interface FormatOptions {
|
/*
|
||||||
ext?: string;
|
* A simple formatter/templater which uses a {tag} format for tokens.
|
||||||
}
|
*
|
||||||
|
* Supported tokens:
|
||||||
|
* - {N}: Random noun
|
||||||
|
* - {n}: Random noun (lower case)
|
||||||
|
* - {A}: Random adjective
|
||||||
|
* - {a}: Random adjective (lower case)
|
||||||
|
* - {MW}: Month week (1-5)
|
||||||
|
* - {time <moment format>}: Current date/time in the specified moment format
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* format("Note {N} created at {time HH:mm}") -> "Note Apple created at 14:30"
|
||||||
|
* format("Note {n} created at {time HH:mm}") -> "Note apple created at 14:30"
|
||||||
|
* format("Scrap from {time YYYY-MM-DD}") -> "Scrap from 2024-06-01"
|
||||||
|
*/
|
||||||
|
|
||||||
export function format(str: string, options?: FormatOptions): string {
|
export function format(str: string): string {
|
||||||
let formatted = str.trim();
|
let formatted = str.trim();
|
||||||
|
|
||||||
let extension = options?.ext ?? "";
|
|
||||||
if (extension.length > 0 && !extension.startsWith(".")) {
|
|
||||||
extension = `.${extension}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace {N} with a random noun
|
// Replace {N} with a random noun
|
||||||
formatted = formatted.replace(/{N}/g, () => {
|
formatted = formatted.replace(/{N}/g, () => {
|
||||||
return capitalize(sample(nouns));
|
return capitalize(sample(nouns));
|
||||||
|
@ -47,5 +55,5 @@ export function format(str: string, options?: FormatOptions): string {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return formatted + extension;
|
return formatted;
|
||||||
}
|
}
|
6
lib/resource/icons.ts
Normal file
6
lib/resource/icons.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export const ICON = {
|
||||||
|
New: "file-plus",
|
||||||
|
Rename: "pen-line",
|
||||||
|
Move: "replace",
|
||||||
|
Copy: "copy-plus",
|
||||||
|
} as const;
|
27
lib/util.ts
Normal file
27
lib/util.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Vault } from "obsidian";
|
||||||
|
|
||||||
|
export async function mkdirp(vault: Vault, folderPath: string): Promise<void> {
|
||||||
|
// Noop if the path exists
|
||||||
|
if (vault.getAbstractFileByPath(folderPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start building the path incrementally
|
||||||
|
const pathParts = folderPath.split("/");
|
||||||
|
let currentPath = "";
|
||||||
|
|
||||||
|
for (const part of pathParts) {
|
||||||
|
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if folder exists
|
||||||
|
if (!vault.getAbstractFileByPath(currentPath)) {
|
||||||
|
// Create the folder if it doesn't exist
|
||||||
|
await vault.createFolder(currentPath);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error creating folder ${currentPath}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
261
main.ts
261
main.ts
|
@ -1,90 +1,125 @@
|
||||||
import {
|
import { App, Plugin, PluginSettingTab, Setting, Notice } from "obsidian";
|
||||||
App,
|
import { format } from "./lib/format.js";
|
||||||
Plugin,
|
import { mkdirp } from "./lib/util.js";
|
||||||
PluginSettingTab,
|
import { ICON } from "./lib/resource/icons.js";
|
||||||
Setting,
|
|
||||||
Vault,
|
|
||||||
Notice,
|
|
||||||
} from "obsidian";
|
|
||||||
import { format } from "./format.js";
|
|
||||||
|
|
||||||
interface ScrapsPluginSettings {
|
type NewScrapBehaviorOption = "default" | "tab" | "split" | "window";
|
||||||
|
|
||||||
|
export interface ScrapsPluginSettings {
|
||||||
scrapsRootDir: string;
|
scrapsRootDir: string;
|
||||||
scrapsPathFormat: string;
|
scrapsPathFormat: string;
|
||||||
scrapsFileName: string;
|
scrapsFileName: string;
|
||||||
|
newScrapBehavior: NewScrapBehaviorOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_SETTINGS: ScrapsPluginSettings = {
|
const DEFAULT_SETTINGS: ScrapsPluginSettings = {
|
||||||
scrapsRootDir: "_Scraps",
|
scrapsRootDir: "_Scraps",
|
||||||
scrapsPathFormat: "{time YYYY} Week {time WW MMM}",
|
scrapsPathFormat: "{time YYYY} Week {time WW MMM}",
|
||||||
scrapsFileName: "{time DDDD} {A} {N}",
|
scrapsFileName: "{time DDDD} {A} {N}",
|
||||||
|
newScrapBehavior: "default",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ICON = {
|
type CommandId = "New" | "Move" | "Copy" | "Rename";
|
||||||
New: "file-plus",
|
interface CommandDef {
|
||||||
Convert: "shuffle",
|
id: string;
|
||||||
Move: "replace",
|
icon: string;
|
||||||
Copy: "copy-plus",
|
name: string;
|
||||||
} as const;
|
|
||||||
|
|
||||||
async function mkdirp(vault: Vault, folderPath: string): Promise<void> {
|
|
||||||
const pathParts = folderPath.split("/");
|
|
||||||
|
|
||||||
// Start building the path incrementally
|
|
||||||
let currentPath = "";
|
|
||||||
|
|
||||||
for (const part of pathParts) {
|
|
||||||
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check if folder exists
|
|
||||||
if (!vault.getAbstractFileByPath(currentPath)) {
|
|
||||||
// Create the folder if it doesn't exist
|
|
||||||
await vault.createFolder(currentPath);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error creating folder ${currentPath}:`, error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const COMMANDS: Record<CommandId, CommandDef> = {
|
||||||
|
New: {
|
||||||
|
id: "scraps-new",
|
||||||
|
icon: ICON.New,
|
||||||
|
name: "Scraps: Create new Scrap",
|
||||||
|
},
|
||||||
|
Move: {
|
||||||
|
id: "scraps-move",
|
||||||
|
icon: ICON.Move,
|
||||||
|
name: "Scraps: Move current file to Scraps",
|
||||||
|
},
|
||||||
|
Copy: {
|
||||||
|
id: "scraps-copy",
|
||||||
|
icon: ICON.Copy,
|
||||||
|
name: "Scraps: Copy current file to Scraps",
|
||||||
|
},
|
||||||
|
Rename: {
|
||||||
|
id: "scraps-rename",
|
||||||
|
icon: ICON.Rename,
|
||||||
|
name: "Scraps: Rename current file as Scrap",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default class ScrapsPlugin extends Plugin {
|
export default class ScrapsPlugin extends Plugin {
|
||||||
settings: ScrapsPluginSettings;
|
settings: ScrapsPluginSettings;
|
||||||
|
|
||||||
async ensureScrapDir(): Promise<string> {
|
/**
|
||||||
const pathname = `${this.getScrapDir()}`;
|
* Ensures that the scrap directory exists by creating it if it does not
|
||||||
|
* already exist.
|
||||||
await mkdirp(this.app.vault, pathname);
|
*
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the directory
|
||||||
return pathname;
|
* has been ensured.
|
||||||
|
*/
|
||||||
|
async ensureScrapDir(): Promise<void> {
|
||||||
|
await mkdirp(this.app.vault, this.scrapsDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
getScrapDir(): string {
|
/**
|
||||||
|
* The current scrap directory path based on the configured root
|
||||||
|
* directory and path format.
|
||||||
|
*
|
||||||
|
* @returns {string} The full path to the current scrap directory.
|
||||||
|
*/
|
||||||
|
get scrapsDirectory(): string {
|
||||||
return `${this.settings.scrapsRootDir}/${format(
|
return `${this.settings.scrapsRootDir}/${format(
|
||||||
this.settings.scrapsPathFormat
|
this.settings.scrapsPathFormat
|
||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getScrapFileName(): string {
|
/**
|
||||||
return format(this.settings.scrapsFileName, { ext: "md" });
|
* A formatted scrap file name based on the current settings.
|
||||||
|
*
|
||||||
|
* @note This will generate a new name each time it is accessed.
|
||||||
|
* @returns {string} The formatted scrap file name.
|
||||||
|
*/
|
||||||
|
get nextScrapFileName(): string {
|
||||||
|
return format(this.settings.scrapsFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
getScrapFilePath(): string {
|
get newScrapLeafType(): false | "tab" | "split" | "window" {
|
||||||
return `${this.getScrapDir()}/${this.getScrapFileName()}`;
|
switch (this.settings.newScrapBehavior) {
|
||||||
|
case "tab":
|
||||||
|
return "tab";
|
||||||
|
case "split":
|
||||||
|
return "split";
|
||||||
|
case "window":
|
||||||
|
return "window";
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createScrap() {
|
/**
|
||||||
|
* Creates a new scrap file in the designated scrap directory.
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the scrap file is created and opened.
|
||||||
|
*/
|
||||||
|
async createNewMarkdownScrap(): Promise<void> {
|
||||||
await this.ensureScrapDir();
|
await this.ensureScrapDir();
|
||||||
const newScrap = await this.app.vault.create(
|
const newScrap = await this.app.vault.create(
|
||||||
this.getScrapFilePath(),
|
`${this.scrapsDirectory}/${this.nextScrapFileName}.md`,
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.app.workspace.getLeaf(false).openFile(newScrap);
|
await this.app.workspace
|
||||||
|
.getLeaf(this.newScrapLeafType)
|
||||||
|
.openFile(newScrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
async convertToScrap(rename = true) {
|
/**
|
||||||
|
* Moves the currently active file to the Scraps directory.
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the file has been converted to a scrap.
|
||||||
|
*/
|
||||||
|
async moveCurrentFileToScraps(): Promise<void> {
|
||||||
const currentFile = this.app.workspace.getActiveFile();
|
const currentFile = this.app.workspace.getActiveFile();
|
||||||
|
|
||||||
if (currentFile === null) {
|
if (currentFile === null) {
|
||||||
|
@ -94,13 +129,38 @@ export default class ScrapsPlugin extends Plugin {
|
||||||
|
|
||||||
await this.ensureScrapDir();
|
await this.ensureScrapDir();
|
||||||
|
|
||||||
const filename = rename ? this.getScrapFileName() : currentFile.name;
|
const renamePath = `${this.scrapsDirectory}/${currentFile.name}`;
|
||||||
const renamePath = `${this.getScrapDir()}/${filename}`;
|
|
||||||
|
|
||||||
await this.app.fileManager.renameFile(currentFile, renamePath);
|
await this.app.fileManager.renameFile(currentFile, renamePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
async copyToScrap() {
|
/**
|
||||||
|
* Renames the currently active file to use a generated Scrap title
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the file has been renamed.
|
||||||
|
*/
|
||||||
|
async renameCurrentFileAsScrap(): Promise<void> {
|
||||||
|
const currentFile = this.app.workspace.getActiveFile();
|
||||||
|
|
||||||
|
if (currentFile === null) {
|
||||||
|
new Notice("No file is currently open");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.ensureScrapDir();
|
||||||
|
|
||||||
|
const renamePath = `${this.scrapsDirectory}/${this.nextScrapFileName}.${currentFile.extension}`;
|
||||||
|
|
||||||
|
await this.app.fileManager.renameFile(currentFile, renamePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the currently active file to a designated "scrap" directory and opens the copied file.
|
||||||
|
* If no file is currently open, a notice is displayed to the user.
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>} A promise that resolves when the file has been copied and opened.
|
||||||
|
*/
|
||||||
|
async copyCurrentFileToScraps(): Promise<void> {
|
||||||
const currentFile = this.app.workspace.getActiveFile();
|
const currentFile = this.app.workspace.getActiveFile();
|
||||||
|
|
||||||
if (currentFile === null) {
|
if (currentFile === null) {
|
||||||
|
@ -112,61 +172,71 @@ export default class ScrapsPlugin extends Plugin {
|
||||||
|
|
||||||
const newScrap = await this.app.vault.copy(
|
const newScrap = await this.app.vault.copy(
|
||||||
currentFile,
|
currentFile,
|
||||||
this.getScrapFilePath()
|
`${this.scrapsDirectory}/${currentFile.name}`
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.app.workspace.getLeaf(false).openFile(newScrap);
|
await this.app.workspace
|
||||||
|
.getLeaf(this.newScrapLeafType)
|
||||||
|
.openFile(newScrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onload() {
|
async onload() {
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
this.addSettingTab(new ScrapsSettingTab(this.app, this));
|
this.addSettingTab(new ScrapsSettingTab(this.app, this));
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
// Command: Create New Scrap //
|
||||||
|
///////////////////////////////
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "scraps-new",
|
...COMMANDS.New,
|
||||||
name: "Scraps: Create new Scrap",
|
|
||||||
icon: ICON.New,
|
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
this.createScrap();
|
this.createNewMarkdownScrap();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addRibbonIcon(ICON.New, "Create new Scrap", async () => {
|
this.addRibbonIcon(COMMANDS.New.icon, COMMANDS.New.name, async () => {
|
||||||
this.createScrap();
|
this.createNewMarkdownScrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Command: Move to Scraps //
|
||||||
|
/////////////////////////////
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "scraps-convert",
|
...COMMANDS.Move,
|
||||||
name: "Scraps: Convert current file to Scrap",
|
editorCallback: async () => this.moveCurrentFileToScraps(),
|
||||||
icon: ICON.Convert,
|
|
||||||
editorCallback: async () => this.convertToScrap(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addRibbonIcon(ICON.Convert, "Convert file to Scrap", async () => {
|
this.addRibbonIcon(COMMANDS.Move.icon, COMMANDS.Move.name, async () => {
|
||||||
this.convertToScrap();
|
this.moveCurrentFileToScraps();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Command: Copy to Scraps //
|
||||||
|
/////////////////////////////
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "scraps-move",
|
...COMMANDS.Copy,
|
||||||
name: "Scraps: Move current file to Scraps",
|
editorCallback: () => this.copyCurrentFileToScraps(),
|
||||||
icon: ICON.Move,
|
|
||||||
editorCallback: async () => this.convertToScrap(false),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addRibbonIcon(ICON.Move, "Move file to Scraps", async () => {
|
this.addRibbonIcon(COMMANDS.Copy.icon, COMMANDS.Copy.name, async () => {
|
||||||
this.convertToScrap(false);
|
this.copyCurrentFileToScraps();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
// Command: Rename to Scraps //
|
||||||
|
///////////////////////////////
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "scraps-copy",
|
...COMMANDS.Rename,
|
||||||
name: "Scraps: Copy current file to Scraps",
|
editorCallback: () => this.copyCurrentFileToScraps(),
|
||||||
icon: ICON.Copy,
|
|
||||||
editorCallback: () => this.copyToScrap(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addRibbonIcon(ICON.Copy, "Copy file to Scraps", async () => {
|
this.addRibbonIcon(
|
||||||
this.copyToScrap();
|
COMMANDS.Rename.icon,
|
||||||
});
|
COMMANDS.Rename.name,
|
||||||
|
async () => {
|
||||||
|
this.renameCurrentFileAsScrap();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onunload() {}
|
onunload() {}
|
||||||
|
@ -210,6 +280,25 @@ class ScrapsSettingTab extends PluginSettingTab {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName("New Scrap Behavior")
|
||||||
|
.setDesc("The behavior when creating a new scrap")
|
||||||
|
.addDropdown((dropdown) =>
|
||||||
|
dropdown
|
||||||
|
.addOptions({
|
||||||
|
default: "Default",
|
||||||
|
tab: "Open in new tab",
|
||||||
|
split: "Split the current pane",
|
||||||
|
window: "Open in new window",
|
||||||
|
})
|
||||||
|
.setValue(this.plugin.settings.newScrapBehavior)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.plugin.settings.newScrapBehavior =
|
||||||
|
value as NewScrapBehaviorOption;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const pathFormatSetting = new Setting(containerEl)
|
const pathFormatSetting = new Setting(containerEl)
|
||||||
.setName("Scraps Path Format")
|
.setName("Scraps Path Format")
|
||||||
.setDesc(
|
.setDesc(
|
||||||
|
@ -229,9 +318,9 @@ class ScrapsSettingTab extends PluginSettingTab {
|
||||||
const fileFormatSetting = new Setting(containerEl)
|
const fileFormatSetting = new Setting(containerEl)
|
||||||
.setName("Scraps File Name Format")
|
.setName("Scraps File Name Format")
|
||||||
.setDesc(
|
.setDesc(
|
||||||
`Preview: ${format(this.plugin.settings.scrapsFileName, {
|
`Preview: ${
|
||||||
ext: "md",
|
format(this.plugin.settings.scrapsFileName) + ".md"
|
||||||
})}`
|
}`
|
||||||
)
|
)
|
||||||
.addText((text) =>
|
.addText((text) =>
|
||||||
text
|
text
|
||||||
|
@ -240,7 +329,7 @@ class ScrapsSettingTab extends PluginSettingTab {
|
||||||
.onChange(async (value) => {
|
.onChange(async (value) => {
|
||||||
this.plugin.settings.scrapsFileName = value;
|
this.plugin.settings.scrapsFileName = value;
|
||||||
fileFormatSetting.setDesc(
|
fileFormatSetting.setDesc(
|
||||||
`Preview: ${format(value, { ext: "md" })}`
|
`Preview: ${format(value) + ".md"}`
|
||||||
);
|
);
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
|
|
5
pack.sh
Executable file
5
pack.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
rm -rf ./dist
|
||||||
|
mkdir dist
|
||||||
|
cp main.js ./dist
|
||||||
|
cp manifest.json ./dist
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "obsidian-sample-plugin",
|
"name": "obsidian-scraps",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
"description": "Create and manage scraps of information in your vault.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "Endeavorance <hello@endeavorance.camp>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
This CSS file will be included with your plugin, and
|
|
||||||
available in the app when your plugin is enabled.
|
|
||||||
|
|
||||||
If your plugin does not need CSS, delete this file.
|
|
||||||
|
|
||||||
*/
|
|
Loading…
Reference in a new issue