diff --git a/manifest.json b/manifest.json index 11f79c8..5923f09 100644 --- a/manifest.json +++ b/manifest.json @@ -1,9 +1,9 @@ { "id": "runjs", "name": "RunJS", - "version": "0.3.0", + "version": "0.4.0", "minAppVersion": "0.15.0", - "description": "RunJS is a plugin for running JavaScript code in Obsidian (https://obsidian.md).", + "description": "Run easily JavaScript codes for managing Obsidian and its notes.", "author": "eoureo", "authorUrl": "https://github.com/eoureo", "fundingUrl": "https://buymeacoffee.com/eoureo", diff --git a/package-lock.json b/package-lock.json index 35d793c..99737d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "runjs", - "version": "0.3.0", + "version": "0.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "runjs", - "version": "0.3.0", + "version": "0.4.0", "license": "MIT", "devDependencies": { "@types/node": "^16.11.6", diff --git a/package.json b/package.json index 12b7097..f621c74 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "runjs", - "version": "0.3.0", - "description": "RunJS is a plugin for running JavaScript code in Obsidian (https://obsidian.md).", + "version": "0.4.0", + "description": "Run easily JavaScript codes for managing Obsidian and its notes.", "main": "main.js", "scripts": { "dev": "node esbuild.config.mjs", diff --git a/src/code_modal.ts b/src/codelist_modal.ts similarity index 94% rename from src/code_modal.ts rename to src/codelist_modal.ts index 74c385f..38f1ccb 100644 --- a/src/code_modal.ts +++ b/src/codelist_modal.ts @@ -9,7 +9,7 @@ import { import { Code } from "./main"; import { LIST_ICON } from "./constants"; -export class RunJSCodeModal extends SuggestModal { +export class RunJSCodeListModal extends SuggestModal { codes: Code[]; onSubmit: (code: Code) => void; plugin: Plugin; @@ -68,9 +68,13 @@ export class RunJSCodeModal extends SuggestModal { this.gotoUpperGroup.bind(this) ); - this.containerEl.addClass("runjs-code-modal"); - const promptEl = this.containerEl.querySelector(".prompt"); - promptEl?.insertBefore(this.titleEl, promptEl.firstChild); + this.containerEl.addClass("runjs-codelist-modal"); + + // const promptEl = this.containerEl.querySelector(".prompt"); + // promptEl?.insertBefore(this.titleEl, promptEl.firstChild); + if (this.titleEl && this.titleEl.parentElement == null) { + this.modalEl?.insertBefore(this.titleEl, this.modalEl.firstChild); + } if (this.promptInputContainerEl) this.promptInputContainerEl.dataset.parent = this.groupParent; this.promptInputContainerEl?.addEventListener("click", (e) => { diff --git a/src/codelist_view.ts b/src/codelist_view.ts index a94f448..063766b 100644 --- a/src/codelist_view.ts +++ b/src/codelist_view.ts @@ -12,7 +12,7 @@ import { LIST_ICON, RunJS_LISTVIEW_ICON } from "./constants"; export const RunJSCodeListViewType = "runjs-codelist-view"; -export class RunJSListView extends ItemView { +export class RunJSCodeListView extends ItemView { private readonly plugin: RunJSPlugin; table: HTMLTableElement; menuFilter: Menu; @@ -52,7 +52,7 @@ export class RunJSListView extends ItemView { // } public onload() { - this.containerEl.classList.add("runjs-listview-container"); + this.containerEl.classList.add("runjs-codelist-view-container"); const navHeader = createDiv({ cls: "nav-header" }); this.containerEl.insertBefore(navHeader, this.containerEl.firstChild); @@ -103,7 +103,7 @@ export class RunJSListView extends ItemView { // @ts-ignore const menuFilterDom = this.menuFilter.dom; - menuFilterDom.classList.add("runjs-listview-menu"); + menuFilterDom.classList.add("runjs-codelist-view-menu"); const filters = this.plugin.settings.listviewFilters; @@ -131,7 +131,7 @@ export class RunJSListView extends ItemView { .setTitle(option) .setIcon(LIST_ICON[option]) .onClick(() => { - if (filters[group].contains(option)) { + if (filters[group]?.contains(option)) { filters[group].remove(option); if ( options.length > 1 && @@ -140,6 +140,7 @@ export class RunJSListView extends ItemView { filters[group] = options.slice(); } } else { + if (!(group in filters)) filters[group] = []; filters[group].push(option); } @@ -187,11 +188,12 @@ export class RunJSListView extends ItemView { const option = "favorite"; const ft_op = "ft-" + option; - if (filters[group].contains(option)) { + if (filters[group]?.contains(option)) { filters[group].remove(option); this.containerEl.classList.remove(ft_op); menuFilterDom.classList.remove(ft_op); } else { + if (!(group in filters)) filters[group] = []; filters[group].push(option); this.containerEl.classList.add(ft_op); menuFilterDom.classList.add(ft_op); @@ -215,7 +217,7 @@ export class RunJSListView extends ItemView { this.menuOther = new Menu(); let menuItemAutoRefresh: MenuItem; // @ts-ignore - this.menuOther.dom.classList.add("runjs-listview-menu"); + this.menuOther.dom.classList.add("runjs-codelist-view-menu"); this.menuOther.addItem((item) => { menuItemAutoRefresh = item; item @@ -516,14 +518,14 @@ export class RunJSListView extends ItemView { openFileContextMenu(event: MouseEvent, code: Code, treeItem: HTMLDivElement) { const menu = new Menu(); // @ts-ignore - menu.dom?.classList.add("runjs-listview-menu"); + menu.dom?.classList.add("runjs-codelist-view-menu"); if (code.type == "script") { menu.addItem((item) => item .setTitle("Run code") .setIcon("lucide-scroll") - .setSection("runjs-listview") + .setSection("runjs-codelist-view") .onClick(() => { this.plugin.runCode(code); }) @@ -534,7 +536,7 @@ export class RunJSListView extends ItemView { item .setTitle("Toggle Favorite code") .setIcon(LIST_ICON["favorite"]) - .setSection("runjs-listview") + .setSection("runjs-codelist-view") .onClick(() => { treeItem.classList.toggle("favorite"); this.plugin.toggleFavoriteCode(code); @@ -557,7 +559,7 @@ export class RunJSListView extends ItemView { item .setTitle("Open code file") .setIcon("lucide-edit") - .setSection("runjs-listview") + .setSection("runjs-codelist-view") .onClick(() => { this.focusFile( code, @@ -606,7 +608,7 @@ export class RunJSListView extends ItemView { }) } - private readonly focusFile = async ( + focusFile = async ( code: Code, shouldSplit = false ): Promise => { @@ -615,28 +617,26 @@ export class RunJSListView extends ItemView { .find((f) => f.path === code.file); if (targetFile) { - let leaf: WorkspaceLeaf = ( - this.app.workspace.getMostRecentLeaf() - ); - - const createLeaf = shouldSplit || leaf.getViewState().pinned; - if (createLeaf) { - if (this.plugin.settings.listviewOpenType == "split") - leaf = this.app.workspace.getLeaf("split"); - else if (this.plugin.settings.listviewOpenType == "window") - leaf = this.app.workspace.getLeaf("window"); - else leaf = this.app.workspace.getLeaf("tab"); - } - await leaf.openFile(targetFile); - - if (code.form != "codeblock") return; + let leaf = this.app.workspace.getMostRecentLeaf(); + + if (leaf) { + const createLeaf = shouldSplit || leaf.getViewState().pinned; + if (createLeaf) { + if (this.plugin.settings.listviewOpenType == "split") + leaf = this.app.workspace.getLeaf("split"); + else if (this.plugin.settings.listviewOpenType == "window") + leaf = this.app.workspace.getLeaf("window"); + else leaf = this.app.workspace.getLeaf("tab"); + } + await leaf.openFile(targetFile); - const viewState = leaf.getViewState(); - viewState.state.mode = "source"; - viewState.state.source = true; - await leaf.setViewState(viewState); + if (code.form != "codeblock") return; - sleep(50); + const viewState = leaf.getViewState(); + viewState.state.mode = "source"; + viewState.state.source = true; + await leaf.setViewState(viewState); + } // @ts-ignore const editor = this.app.workspace.getActiveViewOfType(MarkdownView)?.sourceMode.cmEditor; diff --git a/src/confirm_modal.ts b/src/confirm_modal.ts index e6c4e2a..af7d2e5 100644 --- a/src/confirm_modal.ts +++ b/src/confirm_modal.ts @@ -28,7 +28,7 @@ export class ConfirmModal extends Modal { const buttonDiv = contentEl.createDiv({cls: "modal-button-container"}); new ButtonComponent(buttonDiv) - .setButtonText("OK") + .setButtonText("Yes") .setCta() .onClick(() => { this.callback(true); @@ -36,23 +36,20 @@ export class ConfirmModal extends Modal { }) .setCta(); - new ButtonComponent(buttonDiv).setButtonText("Cancel").onClick(() => { + new ButtonComponent(buttonDiv).setButtonText("No").onClick(() => { this.callback(false); this.close(); }); } onClose() { - this.callback(false); const { contentEl } = this; contentEl.empty(); } } export async function openConfirmModal (app: App, title: string | null, message: string, callback?: ConfirmCallback) { - let return_value; - - const promise = new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { new ConfirmModal( app, title, @@ -62,12 +59,9 @@ export async function openConfirmModal (app: App, title: string | null, message: }) ).open(); }); - - await promise.then(value => {return_value = value}); - return return_value; } -export default class ConfirmDeleteModal extends Modal { +export class ConfirmDeleteModal extends Modal { title: string | null; message: string; callback: ConfirmCallback; @@ -125,4 +119,3 @@ export function openConfirmDeleteModal (app: App, title: string, message: string callback ).open(); } - diff --git a/src/main.ts b/src/main.ts index 6419ed1..179a3d4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,26 +8,27 @@ import { TFolder, TAbstractFile, EventRef, + WorkspaceLeaf, } from "obsidian"; import * as obsidian from "obsidian"; import * as Module from "module"; -import { posix as path } from "path"; import { - RunJSListView, + RunJSCodeListView, RunJSCodeListViewType, } from "./codelist_view"; -import { addIcons, getTagInJSDocComments } from "./utils"; +import { addIcons, getDirname, getTagInJSDocComments, joinPath } from "./utils"; import { RunJSSettingTab } from "./settingtab"; -import { RunJSCodeModal } from "./code_modal"; +import { RunJSCodeListModal } from "./codelist_modal"; import { IconModal } from "./icon_modal"; import { ObjectModal } from "./object_modal"; import { RunJS_ICON, RunJS_ICONS } from "./constants"; import { MessageModal, openMessageModal } from "./message_modal"; import { openConfirmModal } from "./confirm_modal"; import { openPromptModal } from "./prompt_modal"; +import { openSuggestListModal } from "./suggest_list_modal"; -const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor; +const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor; export interface CommandSetting { name: string; @@ -135,8 +136,7 @@ export default class RunJSPlugin extends Plugin { codesModule: CodesModule; modulesLoaded: ModulesLoaded; settingTab: RunJSSettingTab; - listview: RunJSListView; - private _iconsObsidian: string[]; + listview: RunJSCodeListView; state: string; runJSSymbol: symbol; refreshId: number; @@ -165,7 +165,7 @@ export default class RunJSPlugin extends Plugin { this.refreshId = Date.now(); let oldSymbols = Object.getOwnPropertySymbols(window).filter(elem => elem.toString() == this.runJSSymbol.toString()); - for(let oldSymbol of oldSymbols) { + for (let oldSymbol of oldSymbols) { delete window[(oldSymbol as unknown) as keyof Window]; } @@ -189,7 +189,7 @@ export default class RunJSPlugin extends Plugin { this.manifest.name, async (evt: MouseEvent) => { // Called when the user clicks the icon. - this.openCodeModal(); + this.openCodeListModal(); } ); // Perform additional things with the ribbon @@ -201,18 +201,18 @@ export default class RunJSPlugin extends Plugin { // This adds a simple command that can be triggered anywhere this.addCommand({ - id: "runjs-open-modal", - name: "Open RunJS Code modal", + id: "open-codelist-modal", + name: "Open codelist modal", callback: () => { - this.openCodeModal(); + this.openCodeListModal(); }, }); this.addCommand({ - id: "runjs-open-listview", - name: "Open RunJS ListView", + id: "open-codelist-view", + name: "Open codelist view", callback: () => { - this.openListview(); + this.openCodeListView(); }, }); @@ -222,7 +222,7 @@ export default class RunJSPlugin extends Plugin { this.app.workspace.onLayoutReady(async () => { await this.refreshCodes(); - + let changed = false; // remove delete items only once when the plugin is loaded @@ -234,17 +234,12 @@ export default class RunJSPlugin extends Plugin { } } - // this.registerView( - // RunJSCodeListViewType, - // (leaf) => (this.listview = new RunJSListView(leaf, this)) - // ); - this.registerView( RunJSCodeListViewType, - (leaf) => new RunJSListView(leaf, this) + (leaf) => new RunJSCodeListView(leaf, this) ); - await this.renderListview(); + await this.renderCodeListView(); for (let autostart of this.settings.autostarts) { if (autostart[1] === true) { @@ -348,30 +343,99 @@ export default class RunJSPlugin extends Plugin { delete this.refreshJobs[jobName]; this.applyModifyFile(file); }, this.refreshLimitTime); -} + } async applyModifyFile(file: TAbstractFile) { - await this.handleDeleteFile(file, false); - await sleep(500); - await this.handleCreateFile(file); + if (!(file instanceof TFile)) return; + + let isAddedCode = false; + let isRefreshedCode = false; + const codesTarget = []; + + for (let code of this.codes) { + if (code.file == file.path) { + codesTarget.push(code); + } + } + + if (file.extension == "md") { + for (let i = 0; i < 10; i++) { + // wait - max 100ms x 10 + // @ts-ignore + if (this.app.metadataCache?.getCachedFiles().contains(file.path)) break; + await sleep(100); + } + + const codes = await this.getCodesInCodeblock(file.path); + + if (codes != null && codes.length > 0) { + for (let code_new of codes) { + let isNewCode = true; + for (let code_i = 0; code_i < codesTarget.length; code_i++) { + const code = codesTarget[code_i]; + if (code.name == code_new.name) { + Object.assign(code, code_new); + codesTarget.splice(code_i, 1); + isNewCode = false; + isRefreshedCode = true; + break; + } + } + + if (isNewCode) { + this.appendCode(code_new); + isAddedCode = true; + } + } + } + } else { + const code_new = await this.getCodeInFile(file); + if (code_new != null) { + let isNewCode = true; + for (let code_i = 0; code_i < codesTarget.length; code_i++) { + const code = codesTarget[code_i]; + if (code.name == code_new.name) { + Object.assign(code, code_new); + codesTarget.splice(code_i, 1); + isNewCode = false; + isRefreshedCode = true; + break; + } + } + + if (isNewCode) { + this.appendCode(code_new); + isAddedCode = true; + } + } + } + + for (let code of codesTarget) { + if ( + code.form == "m" && + this.codesModule[code.name].file == file.path + ) { + delete this.codesModule[code.name]; + } else { + this.codesScript.splice(this.codesScript.indexOf(code), 1); + } + this.codes.splice(this.codes.indexOf(code), 1); + } + + if (codesTarget.length > 0 || isAddedCode) { + new Notice(this.manifest.name + ": codes updated"); + this.listview.update(); + } else if (isRefreshedCode) { + new Notice(this.manifest.name + ": codes refreshed"); + } } async handleCreateFile(file: TAbstractFile) { if (!(file instanceof TFile)) return; - - let checkChanged = false; + let checkChanged = false; if (file.extension == "md") { - // // @ts-ignore - // const fileCache = this.app.metadataCache.fileCache; - - // for (let i = 0; i < 10; i++) { - // // wait - max 100ms x 10 - // if (fileCache[file.path].hash != "") break; - // await sleep(100); - // } - for (let i = 0; i < 10; i++) { // wait - max 100ms x 10 // @ts-ignore @@ -429,6 +493,14 @@ export default class RunJSPlugin extends Plugin { onunload() { // this.app.workspace.detachLeavesOfType(RunJSCodeListViewType); + + for (let jobName in this.refreshJobs) { + clearTimeout(this.refreshJobs[jobName].timeoutId); + } + Object.keys(this.refreshJobs).forEach( + (jobName) => delete this.refreshJobs[jobName] + ); + delete window[(this.runJSSymbol as unknown) as keyof Window]; this.log("info", "unloaded."); } @@ -445,30 +517,30 @@ export default class RunJSPlugin extends Plugin { await this.saveData(this.settings); } - async openCodeModal(groupRoot?: string) { + async openCodeListModal(groupRoot?: string) { if (this.codes.length == 0) { await this.refreshCodes(); } - const runJSCodeModal = new RunJSCodeModal( + const runJSCodeListModal = new RunJSCodeListModal( this.app, this, this.codesScript, this.runCode.bind(this) ); - if (groupRoot) runJSCodeModal.groupRoot = groupRoot; - runJSCodeModal.open(); + if (groupRoot) runJSCodeListModal.groupRoot = groupRoot; + runJSCodeListModal.open(); } - async openListview() { - const leaf = await this.renderListview(); - + async openCodeListView() { + const leaf = await this.renderCodeListView(); + this.app.workspace.revealLeaf(leaf); } - async renderListview(): Promise { - let [ leaf ] = this.app.workspace.getLeavesOfType(RunJSCodeListViewType); - + async renderCodeListView(): Promise { + let [leaf] = this.app.workspace.getLeavesOfType(RunJSCodeListViewType); + if (!leaf) { leaf = this.app.workspace.getLeftLeaf(false); await leaf.setViewState({ type: RunJSCodeListViewType }); @@ -504,7 +576,7 @@ export default class RunJSPlugin extends Plugin { const tFile = this.app.vault.getAbstractFileByPath(code.file); if (tFile instanceof TFile) { text = await this.app.vault.read(tFile); - folder = path.dirname( + folder = getDirname( code.file.replace( new RegExp("^" + this.settings.scriptsFolder + "\\/"), "./" @@ -520,10 +592,10 @@ export default class RunJSPlugin extends Plugin { const F = new AsyncFunction(m_text); await F.apply(this); } - + async refresh() { const changed = await this.refreshCodes(); - + if (changed) { new Notice(this.manifest.name + ": codes updated"); this.listview.update(); @@ -532,8 +604,8 @@ export default class RunJSPlugin extends Plugin { async refreshCodes(): Promise { let changed: boolean = true; - - for ( let jobName in this.refreshJobs) { + + for (let jobName in this.refreshJobs) { clearTimeout(this.refreshJobs[jobName].timeoutId); } Object.keys(this.refreshJobs).forEach( @@ -553,15 +625,14 @@ export default class RunJSPlugin extends Plugin { await this.iterateCodeblocks(this.appendCode.bind(codesDataNew)); - const tFolder: TFolder = ( - this.app.vault.getAbstractFileByPath(this.settings.scriptsFolder) - ); - if (refreshId < this.refreshId) return false; - await this.iterateScriptsFolder(tFolder, this.appendCode.bind(codesDataNew)); + const tFolder = this.app.vault.getAbstractFileByPath(this.settings.scriptsFolder); + if (tFolder instanceof TFolder) { + await this.iterateScriptsFolder(tFolder, this.appendCode.bind(codesDataNew)); - if (refreshId < this.refreshId) return false; + if (refreshId < this.refreshId) return false; + } // initialize data this.codes.splice(0, this.codes.length); @@ -655,7 +726,7 @@ export default class RunJSPlugin extends Plugin { if (!(sections?.length > 0)) { return null; } - + let contents: string[] = []; try { @@ -666,9 +737,9 @@ export default class RunJSPlugin extends Plugin { if (contents.length <= 0) { const tFile = this.app.vault.getAbstractFileByPath(filePath); - if(tFile instanceof TFile) contents = (await this.app.vault.read(tFile)).split("\n"); + if (tFile instanceof TFile) contents = (await this.app.vault.read(tFile)).split("\n"); } - + const position = sections[i].position; const match = contents[position.start.line].match(this.regexpCodeblockIndicator); @@ -690,7 +761,7 @@ export default class RunJSPlugin extends Plugin { if (codeText) { const { n: name, t: type, o: order, d: desc } = codeSetting; - + if (name == undefined) continue; const code: Code = Object.assign({}, DEFAULT_CODE); @@ -705,8 +776,8 @@ export default class RunJSPlugin extends Plugin { type == "s" ? "script" : type == "m" - ? "module" - : type; + ? "module" + : type; } if (order) { @@ -729,7 +800,7 @@ export default class RunJSPlugin extends Plugin { async getCodeInFile(child: TFile): Promise { const filePath = child.path; - + const content = await this.app.vault.read(child); if (content) { @@ -816,7 +887,7 @@ export default class RunJSPlugin extends Plugin { const code = this.codesModule[codeName]; if (code) { const text = await this.getCodeText(code); - + if (text) { if ( this.modulesLoaded[code.name]?.codeText == text && @@ -827,13 +898,13 @@ export default class RunJSPlugin extends Plugin { const text_modified = this.modifyImport( text, - path.dirname(codeName) + getDirname(codeName) ); - + // const blob = new Blob([text_modified], { type: "text/javascript" }); const blob = new File([text_modified], "codeName", { type: "text/javascript" - }); + }); const url = URL.createObjectURL(blob); const module = await import(url); @@ -918,7 +989,7 @@ export default class RunJSPlugin extends Plugin { const match_mName = item .trim() .match(/^\*\s+as\s+(.*)$/); // "* as name"이면 - + if (match_mName) { // 빼버리자. mName = match_mName[1]; @@ -968,21 +1039,21 @@ export default class RunJSPlugin extends Plugin { if (mName) { commands.push( import_or_export + - `const ${mName} = ${runjs_import}("${p3}")` + `const ${mName} = ${runjs_import}("${p3}")` ); } if (values.length > 0) { if (mName) { commands.push( import_or_export + - `const {${values.join(", ")}} = ${mName}` + `const {${values.join(", ")}} = ${mName}` ); } else { commands.push( import_or_export + - `const {${values.join( - ", " - )}} = ${runjs_import}("${p3}")` + `const {${values.join( + ", " + )}} = ${runjs_import}("${p3}")` ); } } @@ -1000,11 +1071,11 @@ export default class RunJSPlugin extends Plugin { return filePath; } - let p3: string = path.join(folder, filePath); - if (folder.match(/^\./) && p3.match(/^[^\.]/)) { - p3 = "./" + p3; + let path: string = joinPath(folder, filePath); + if (folder.match(/^\./) && path.match(/^[^\.]/)) { + path = "./" + path; } - return p3; + return path; } getCodeByName(name: string): Code | null { @@ -1020,7 +1091,8 @@ export default class RunJSPlugin extends Plugin { let text = ""; if (code.form == "file") { - text = await this.app.vault.read( this.app.vault.getAbstractFileByPath(code.file) ); + const tFile = this.app.vault.getAbstractFileByPath(code.file); + if (tFile instanceof TFile) text = await this.app.vault.read(tFile); } else { text = code.text; } @@ -1062,7 +1134,7 @@ export default class RunJSPlugin extends Plugin { removeRibbonIcon(setting: RibbonIconSetting) { let ribbonItem; const ribbonId = this.manifest.id + ":" + setting.name; - + // @ts-ignore for (let item of this.app.workspace.leftRibbon.items) { if (item.id == ribbonId) { @@ -1077,28 +1149,7 @@ export default class RunJSPlugin extends Plugin { } } - get iconsObsidian(): string[] { - if (this._iconsObsidian == undefined) { - const fs = require("fs"); - const path = require("path"); - const f_path = path.join(__dirname, "../../obsidian.asar/app.js"); - const f = fs.readFileSync(f_path, { encoding: "utf8", flag: "r" }); - - // let match = f.match(/const \w+=({.*?accessibility:.*?})/); - const match = f.match(/const \w+=({accessibility:.*?})/); - - if (match) { - this._iconsObsidian = match[1] - .match(/[\w-"]+\:/g) - .map((m: string) => m.replace(/["\:]/g, "")); - } - } - - return this._iconsObsidian; - } - async openIconModal(callback?: (icon: string) => void) { - // let icons = RunJS_ICONS.concat(this.iconsObsidian); const icons = obsidian.getIconIds(); if (icons.length <= 0) { @@ -1110,17 +1161,18 @@ export default class RunJSPlugin extends Plugin { this.app, icons, callback ?? - ((icon) => { - this.alert(icon); - }) + ((icon) => { + this.log("info", icon); + this.alert(icon); + }) ); iconModal.open(); } - async openObjectModal(object?: {[key: string]: any }, callback?: (key: string) => void) { + async openObjectModal(object?: { [key: string]: any }, callback?: (key: string) => void) { if (object == undefined) { object = {}; - + object["obsidian"] = obsidian; object["app"] = this.app; object["RunJS"] = this; @@ -1131,7 +1183,7 @@ export default class RunJSPlugin extends Plugin { const dataview = appPlugins.plugins["dataview"]; if (dataview) object["dataview"] = dataview; - + const templater = appPlugins.plugins["templater-obsidian"]; if (templater) object["templater"] = templater; @@ -1149,10 +1201,10 @@ export default class RunJSPlugin extends Plugin { this, object, callback ?? - ((key) => { - this.log("info", key); - this.alert(key); - }) + ((key) => { + this.log("info", key); + this.alert(key); + }) ); objectModal.open(); } @@ -1165,8 +1217,12 @@ export default class RunJSPlugin extends Plugin { return await openConfirmModal(this.app, this.manifest.name, message); } - async prompt(message: string, messagDefault: string = "", placeholder: string = "") { - return await openPromptModal(this.app, this.manifest.name, message, messagDefault, placeholder); + async prompt(message: string, messagDefault: string = "", placeholder: string = "", multiLine: boolean = false) { + return await openPromptModal(this.app, this.manifest.name, message, messagDefault, placeholder, multiLine); + } + + async suggest(message: string, list: string[], placeholder: string = "") { + return await openSuggestListModal(this.app, this.manifest.name, message, list, placeholder); } toggleFavoriteCode(code: Code) { @@ -1199,11 +1255,11 @@ export default class RunJSPlugin extends Plugin { if (this.settings.logConsole) { // @ts-ignore const console_func = console[type]; - if(console_func) console_func(this.manifest.name + ":", ...args); + if (console_func) console_func(this.manifest.name + ":", ...args); } if (this.settings.logFile && this.settings.logFilePath != "") { const tFile = this.app.vault.getAbstractFileByPath(this.settings.logFilePath); - if(tFile instanceof TFile) this.app.vault.append(tFile, "- " + timezoneDateISOSting + " [" + type + "] " + args.join(" ") + "\n"); + if (tFile instanceof TFile) this.app.vault.append(tFile, "- " + timezoneDateISOSting + " [" + type + "] " + args.join(" ") + "\n"); } } catch (e) { console.error(this.manifest.name + ":", "log - error.") diff --git a/src/message_modal.ts b/src/message_modal.ts index 7c05de6..6b1c48f 100644 --- a/src/message_modal.ts +++ b/src/message_modal.ts @@ -12,7 +12,7 @@ export class MessageModal extends Modal { this.message = message; this.title = title; this.message = message; - if(callback) this.callback = callback; + if (callback) this.callback = callback; } onOpen() { @@ -24,18 +24,18 @@ export class MessageModal extends Modal { contentEl.setText(this.message); - const buttonDiv = contentEl.createDiv({cls: "modal-button-container"}); + const buttonDiv = contentEl.createDiv({ cls: "modal-button-container" }); new ButtonComponent(buttonDiv).setButtonText("OK") .setCta() .onClick(() => { - if(this.callback) this.callback(true); + if (this.callback) this.callback(true); this.close(); }); } onClose() { - if(this.callback) this.callback(true); + if (this.callback) this.callback(true); this.contentEl.empty(); } diff --git a/src/object_modal.ts b/src/object_modal.ts index 5f0881c..3feafe9 100644 --- a/src/object_modal.ts +++ b/src/object_modal.ts @@ -98,8 +98,12 @@ export class ObjectModal extends SuggestModal { instructionEls[2].addClass("event_click"); this.containerEl.addClass("runjs-object-modal"); - const promptEl = this.containerEl.querySelector(".prompt"); - promptEl?.insertBefore(this.titleEl, promptEl.firstChild); + + // const promptEl = this.containerEl.querySelector(".prompt"); + // promptEl?.insertBefore(this.titleEl, promptEl.firstChild); + if (this.titleEl && this.titleEl.parentElement == null) { + this.modalEl?.insertBefore(this.titleEl, this.modalEl.firstChild); + } this.promptInputContainerEl?.addEventListener("click", (e) => { if (e.target === this.promptInputContainerEl) { diff --git a/src/prompt_modal.ts b/src/prompt_modal.ts index 8e99136..10dd4a6 100644 --- a/src/prompt_modal.ts +++ b/src/prompt_modal.ts @@ -1,4 +1,4 @@ -import { App, Modal, ButtonComponent, TextComponent } from "obsidian"; +import { App, Modal, ButtonComponent, TextComponent, TextAreaComponent } from "obsidian"; type PromptCallback = (text: string | null) => void; @@ -7,8 +7,9 @@ export class PromptModal extends Modal { message: string; messagDefault: string; placeholder: string; + multiLine: boolean; callback: PromptCallback; - textComponent: TextComponent; + textComponent: TextComponent | TextAreaComponent; constructor( app: App, @@ -16,6 +17,7 @@ export class PromptModal extends Modal { message: string, messagDefault: string = "", placeholder: string = "", + multiLine: boolean = false, callback: PromptCallback ) { super(app); @@ -23,6 +25,7 @@ export class PromptModal extends Modal { this.message = message; this.messagDefault = messagDefault; this.placeholder = placeholder; + this.multiLine = multiLine; this.callback = callback; } @@ -35,13 +38,25 @@ export class PromptModal extends Modal { if (this.title) this.titleEl.setText(this.title); - contentEl.createEl("p").setText(this.message); + if (this.message) contentEl.createEl("p").setText(this.message); - this.textComponent = new TextComponent(contentEl) + if (this.multiLine) { + this.textComponent = new TextAreaComponent(contentEl); + } else { + this.textComponent = new TextComponent(contentEl); + } + + this.textComponent .setValue(this.messagDefault) .setPlaceholder(this.placeholder) .then((cb) => { cb.inputEl.addClass("prompt-input"); + cb.inputEl.addEventListener("keydown", (event: KeyboardEvent) => { + if (!event.shiftKey && event.key === "Enter") { + this.onOK(); + event.preventDefault(); // Prevents the addition of a new line in the text field + } + }); }); const buttonDiv = contentEl.createDiv({ @@ -52,22 +67,24 @@ export class PromptModal extends Modal { .setButtonText("OK") .setCta() .onClick(() => { - this.callback(this.textComponent.getValue()); - this.close(); + this.onOK(); }) .setCta(); new ButtonComponent(buttonDiv).setButtonText("Cancel").onClick(() => { - this.callback(null); this.close(); }); } onClose() { - this.callback(null); let { contentEl } = this; contentEl.empty(); } + + onOK() { + this.callback(this.textComponent.getValue()); + this.close(); + } } export async function openPromptModal( @@ -76,26 +93,21 @@ export async function openPromptModal( message: string, messagDefault: string = "", placeholder: string = "", + multiLine: boolean = false, callback?: PromptCallback ) { - let return_value; - - const promise = new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { new PromptModal( app, title, message, messagDefault, placeholder, + multiLine, callback ?? ((text: string | null) => { resolve(text); }) ).open(); }); - - await promise.then((value) => { - return_value = value; - }); - return return_value; } diff --git a/src/settingtab.ts b/src/settingtab.ts index 04649b6..ea0f57f 100644 --- a/src/settingtab.ts +++ b/src/settingtab.ts @@ -26,7 +26,7 @@ import { LIST_ICON, RIBBON_ICON_DEFAULT_ICON, } from "./constants"; -import { RunJSCodeModal } from "./code_modal"; +import { RunJSCodeListModal } from "./codelist_modal"; import { openConfirmDeleteModal } from "./confirm_modal"; import { openMessageModal } from "./message_modal"; @@ -58,23 +58,23 @@ export class RunJSSettingTab extends PluginSettingTab { .setName(this.plugin.manifest.name) .setDesc("(v" + this.plugin.manifest.version + ")") .setClass("setting-item-heading") // setting-head - .addExtraButton((component) => { - component - .setIcon("refresh-ccw") - .setTooltip("Refresh codelist") - .onClick(() => { - this.plugin.refresh(); - }); - }) - .addExtraButton((component) => { - component - .setIcon("refresh-cw") - .setTooltip("Reload plugin") - .onClick(() => { - this.plugin.reload(); - }); - component.extraSettingsEl.classList.add("mod-warning"); - }) + // .addExtraButton((component) => { + // component + // .setIcon("refresh-ccw") + // .setTooltip("Refresh codelist") + // .onClick(() => { + // this.plugin.refresh(); + // }); + // }) + // .addExtraButton((component) => { + // component + // .setIcon("refresh-cw") + // .setTooltip("Reload plugin") + // .onClick(() => { + // this.plugin.reload(); + // }); + // component.extraSettingsEl.classList.add("mod-warning"); + // }) .then(cb => { cb.settingEl.classList.add("setting-head"); }); @@ -104,18 +104,18 @@ export class RunJSSettingTab extends PluginSettingTab { .onChange(async (new_folder) => { this.plugin.settings.scriptsFolder = new_folder; this.plugin.saveSettings(); - if((await app.vault.adapter.stat(this.plugin.settings.scriptsFolder))?.type == "folder") { + if ((await this.app.vault.adapter.stat(this.plugin.settings.scriptsFolder))?.type == "folder") { cb.inputEl.removeClass("mod-warning"); } else { cb.inputEl.addClass("mod-warning"); } }); - if((await app.vault.adapter.stat(this.plugin.settings.scriptsFolder))?.type == "folder") { - cb.inputEl.removeClass("mod-warning"); - } else { - cb.inputEl.addClass("mod-warning"); - } + if ((await this.app.vault.adapter.stat(this.plugin.settings.scriptsFolder))?.type == "folder") { + cb.inputEl.removeClass("mod-warning"); + } else { + cb.inputEl.addClass("mod-warning"); + } new FolderTextInputPopoverSuggest(this.app, cb.inputEl); }); @@ -126,7 +126,7 @@ export class RunJSSettingTab extends PluginSettingTab { .addExtraButton((component) => { component .setIcon("check-circle-2") - .setTooltip("console") + .setTooltip("Developer Tools - Console") .onClick(() => { this.plugin.settings.logConsole = !this.plugin.settings.logConsole; this.plugin.saveSettings(); @@ -134,7 +134,8 @@ export class RunJSSettingTab extends PluginSettingTab { else component.setIcon("circle"); }) .then(cb => { - cb.extraSettingsEl.addClass("setting-log-check") + cb.extraSettingsEl.addClass("setting-log-check"); + cb.extraSettingsEl.setAttr("data-label", "Console"); if (this.plugin.settings.logConsole) cb.setIcon("check-circle-2"); else cb.setIcon("circle"); }); @@ -142,7 +143,7 @@ export class RunJSSettingTab extends PluginSettingTab { .addExtraButton((component) => { component .setIcon("check-circle-2") - .setTooltip("notice") + .setTooltip("Obsidian - Notice") .onClick(() => { this.plugin.settings.logNotice = !this.plugin.settings.logNotice; this.plugin.saveSettings(); @@ -150,7 +151,8 @@ export class RunJSSettingTab extends PluginSettingTab { else component.setIcon("circle"); }) .then(cb => { - cb.extraSettingsEl.addClass("setting-log-check") + cb.extraSettingsEl.addClass("setting-log-check"); + cb.extraSettingsEl.setAttr("data-label", "Notice"); if (this.plugin.settings.logNotice) cb.setIcon("check-circle-2"); else cb.setIcon("circle"); }); @@ -158,7 +160,7 @@ export class RunJSSettingTab extends PluginSettingTab { .addExtraButton((component) => { component .setIcon("circle") - .setTooltip("file") + .setTooltip("Append to file") .onClick(() => { this.plugin.settings.logFile = !this.plugin.settings.logFile; this.plugin.saveSettings(); @@ -166,7 +168,8 @@ export class RunJSSettingTab extends PluginSettingTab { else component.setIcon("circle"); }) .then(cb => { - cb.extraSettingsEl.addClass("setting-log-check") + cb.extraSettingsEl.addClass("setting-log-check"); + cb.extraSettingsEl.setAttr("data-label", "File"); if (this.plugin.settings.logFile) cb.setIcon("check-circle-2"); else cb.setIcon("circle"); }); @@ -193,15 +196,13 @@ export class RunJSSettingTab extends PluginSettingTab { .setIcon("plus") .setTooltip("Add code") .onClick(async (evt: MouseEvent) => { - let app = this.app; - - let runJSCodeModal = new RunJSCodeModal( - app, + let runJSCodeListModal = new RunJSCodeListModal( + this.app, this.plugin, this.plugin.codesScript, this.addAutostartSetting.bind(this) ); - runJSCodeModal.open(); + runJSCodeListModal.open(); }) ); @@ -224,14 +225,13 @@ export class RunJSSettingTab extends PluginSettingTab { .setIcon("plus") .setTooltip("Add Command") .onClick(async (evt: MouseEvent) => { - const app = this.app; - const runJSCodeModal = new RunJSCodeModal( - app, + const runJSCodeListModal = new RunJSCodeListModal( + this.app, this.plugin, this.plugin.codesScript, this.addCommandSetting.bind(this) ); - runJSCodeModal.open(); + runJSCodeListModal.open(); }) ); @@ -251,14 +251,13 @@ export class RunJSSettingTab extends PluginSettingTab { .setIcon("plus") .setTooltip("Add ribbon icon") .onClick(async (evt: MouseEvent) => { - const app = this.app; - const runJSCodeModal = new RunJSCodeModal( - app, + const runJSCodeListModal = new RunJSCodeListModal( + this.app, this.plugin, this.plugin.codesScript, this.addRibbonIconSetting.bind(this) ); - runJSCodeModal.open(); + runJSCodeListModal.open(); }) ); @@ -291,20 +290,31 @@ export class RunJSSettingTab extends PluginSettingTab { cls: "setting-item-container interval-container", }); - - new Setting(containerEl) - .setName('Donate') - .setDesc('If you like this plugin, consider donating to support continued development:') - .setClass("setting-donate") - // .addButton((bt) => { - // bt.buttonEl.outerHTML = `Buy Me A Coffee`; - // }) - .addButton((bt) => { - bt.buttonEl.outerHTML = `Buy Me A Coffee`; - }) - .addButton((bt) => { - bt.buttonEl.outerHTML = `Buy Me A Coffee`; - }); + new Setting(containerEl) + .setName('Donate') + .setDesc('If you like this plugin, consider donating to support continued development:') + .setClass("setting-donate") + .then(cb => { + cb.controlEl.createEl("a", { attr: { href: "https://www.buymeacoffee.com/eoureo", target: "_blank" } }, el => { + el.createEl("img", { + attr: { + src: BUY_ME_A_COFFEE_YELLOW, + "data-src": "https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png", + alt: "Buy Me A Coffee", + style: "height: 40px !important;width: 144px !important;" + } + }); + }); + cb.controlEl.createEl("a", { attr: { href: "https://www.buymeacoffee.com/eoureo", target: "_blank" } }, el => { + el.createEl("img", { + attr: { + src: BUY_ME_A_COFFEE_QR_BOX, + alt: "Buy Me A Coffee", + style: "height: 144px !important;width: 144px !important;" + } + }); + }); + }); } addAutostartSetting(code: Code) { @@ -360,7 +370,7 @@ export class RunJSSettingTab extends PluginSettingTab { renderAutostartSetting(autostart: [string, boolean], settings: [string, boolean][]) { const codeName = autostart[0]; let code = this.plugin.getCodeByName(codeName); - + let checkError = false; if (code == null) { this.plugin.log("error", "renderAutostartSetting", codeName); @@ -374,7 +384,7 @@ export class RunJSSettingTab extends PluginSettingTab { .setName( createFragment((e) => { const icon = e.createSpan({ text: "icon", cls: "icon" }); - if(code && Object.keys(LIST_ICON).contains(code.form)) setIcon(icon, LIST_ICON[code.form]); + if (code && Object.keys(LIST_ICON).contains(code.form)) setIcon(icon, LIST_ICON[code.form]); e.createSpan({ text: autostart[0] }); }) ) @@ -453,7 +463,7 @@ export class RunJSSettingTab extends PluginSettingTab { // @ts-ignore title: code.form, }); - if(code && Object.keys(LIST_ICON).contains(code.form)) setIcon(icon, LIST_ICON[code.form]); + if (code && Object.keys(LIST_ICON).contains(code.form)) setIcon(icon, LIST_ICON[code.form]); e.createSpan({ text: commandSetting.codeName, cls: "code-name" }); }) ) @@ -566,7 +576,7 @@ export class RunJSSettingTab extends PluginSettingTab { title: code.form, }); // setIcon(icon, LIST_ICON[code.form]); - if(code && Object.keys(LIST_ICON).contains(code.form)) setIcon(icon, LIST_ICON[code.form]); + if (code && Object.keys(LIST_ICON).contains(code.form)) setIcon(icon, LIST_ICON[code.form]); e.createSpan({ text: ribbonIconSetting.codeName, cls: "code-name", @@ -712,7 +722,7 @@ export class RunJSSettingTab extends PluginSettingTab { settings.splice(index + 1, 0, settings.splice(index, 1)[0]); } else { if (index <= 0) return; - + parentEl.insertBefore(settingEl, settingEl.previousElementSibling); settings.splice(index - 1, 0, settings.splice(index, 1)[0]); } @@ -737,11 +747,11 @@ export class RunJSSettingTab extends PluginSettingTab { // if (setting[this.key_sym]) delete settings[setting[this.key_sym]]; const key = setting[(this.key_sym as unknown) as keyof Setting]; - + // @ts-ignore if (key) delete settings[key]; } - + settingEl.remove(); this.plugin.saveSettings(); } diff --git a/src/suggest_list_modal.ts b/src/suggest_list_modal.ts new file mode 100644 index 0000000..53ec4e8 --- /dev/null +++ b/src/suggest_list_modal.ts @@ -0,0 +1,72 @@ +import { App, SuggestModal } from "obsidian"; + +type SuggestCallback = (text: string | null) => void; + +export class SuggestListModal extends SuggestModal { + title: string | null; + message: string; + list: Array; + defaultItem: string; + onSubmit: SuggestCallback; + + constructor(app: App, title: string | null, message: string, list: Array, placeHolder: string, onSubmit?: SuggestCallback) { + super(app); + if (title) this.titleEl.setText(title); + this.message = message; + this.list = list; + if (placeHolder) this.emptyStateText = placeHolder; + if (onSubmit != undefined) this.onSubmit = onSubmit; + + this.containerEl.addClass("runjs-suggest-modal"); + + if (title != null && this.titleEl && this.titleEl.parentElement == null) { + this.modalEl?.insertBefore(this.titleEl, this.modalEl.firstChild); + } + + if (this.message) { + if (this.contentEl && this.contentEl.parentElement == null) { + if (this.titleEl?.parentElement != null) { + this.modalEl?.insertBefore(this.contentEl, this.titleEl.nextSibling); + } else { + this.modalEl?.insertBefore(this.contentEl, this.modalEl.firstChild); + } + } + + this.contentEl?.setText(this.message); + } + } + + // Returns all available suggestions. + getSuggestions(query: string): Array { + return this.list.filter((item) => + item.toLowerCase().includes(query.toLowerCase()) + ); + } + + // Renders each suggestion item. + renderSuggestion(item: string, el: HTMLElement) { + el.setText(item); + } + + // Perform action on the selected suggestion. + onChooseSuggestion(item: string, evt: Event) { + // new Notice(`Selected ${item}`); + // console.log(`Selected ${item}`); + this.onSubmit(item); + } +} + +export async function openSuggestListModal(app: App, title: string, message: string, list: Array, placeHolder: string, callback?: SuggestCallback) { + return await new Promise((resolve, reject) => { + new SuggestListModal( + app, + title, + message, + list, + placeHolder, + callback ?? ((item: string) => { + resolve(item); + }) + ).open(); + }); +} diff --git a/src/utils.ts b/src/utils.ts index 207abcf..712e855 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -50,3 +50,54 @@ export function addIcons() { `` ); } + +export function getDirname(filePath: string): string { + // 마지막 경로 구분자를 찾습니다. + var lastSeparatorIndex = filePath.lastIndexOf('/'); + + // 경로 구분자 이후의 문자열을 제거하여 디렉토리 경로를 얻습니다. + var dirname = filePath.substring(0, lastSeparatorIndex); + + return dirname; +} + +export function joinPath(folder: string, filePath: string): string { + if (filePath.startsWith("/")) folder = "/"; + + // 폴더 경로와 파일 경로를 연결합니다. + var fullPath = folder + "/" + filePath; + + // 경로 구분자를 단일 슬래시('/')로 통일합니다. + fullPath = fullPath.replace(/\/+/g, '/'); + + // 상대 경로를 해석합니다. + var parts = fullPath.split('/'); + var resolvedParts = []; + + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + + if (i != 0 && part === '.') { + // 현재 디렉토리를 무시합니다. + continue; + } else if (part === '..') { + // 상위 디렉토리로 이동합니다. + if (resolvedParts.length == 0 || resolvedParts[resolvedParts.length -1] === "..") resolvedParts.push(part) + else if (resolvedParts[resolvedParts.length -1] === ".") { + resolvedParts.pop(); + resolvedParts.push(part); + } else resolvedParts.pop(); + } else { + // 디렉토리 이름을 추가합니다. + resolvedParts.push(part); + } + } + + // 최종 경로를 반환합니다. + var resolvedPath = resolvedParts.join('/'); + + // 상대 경로에서 앞에 붙은 "./"을 제거합니다. + // resolvedPath = resolvedPath.replace(/^\.\//, ''); + + return resolvedPath; +} diff --git a/styles.css b/styles.css index ef23da2..a1c3483 100644 --- a/styles.css +++ b/styles.css @@ -1,34 +1,34 @@ -.runjs-listview-container +.runjs-codelist-view-container { font-size: var(--nav-item-size); font-weight: var(--nav-item-weight); color: var(--nav-item-color); } -.runjs-listview-container .nav-header .right { +.runjs-codelist-view-container .nav-header .right { float: right; } -.runjs-listview-container .nav-header .sort.clickable-icon { +.runjs-codelist-view-container .nav-header .sort.clickable-icon { padding: 0; } -.runjs-listview-container .nav-header .sort .icon.sort_asc { +.runjs-codelist-view-container .nav-header .sort .icon.sort_asc { display: inline-block; } -.runjs-listview-container .nav-header .sort .icon.sort_desc { +.runjs-codelist-view-container .nav-header .sort .icon.sort_desc { display: none; } -.runjs-listview-container .nav-header .sort.descend .icon.sort_asc { +.runjs-codelist-view-container .nav-header .sort.descend .icon.sort_asc { display: none; } -.runjs-listview-container .nav-header .sort.descend .icon.sort_desc { +.runjs-codelist-view-container .nav-header .sort.descend .icon.sort_desc { display: inline-block; } -.runjs-listview-container .icon { +.runjs-codelist-view-container .icon { display: inline-block; align-items: center; padding: 0; @@ -37,114 +37,114 @@ color: var(--icon-color); } -.runjs-listview-container .icon .svg-icon:not(.right-triangle) { +.runjs-codelist-view-container .icon .svg-icon:not(.right-triangle) { /* --icon-size: var(--icon-xs); */ --icon-stroke: var(--icon-s-stroke-width); } -.runjs-listview-container .view-content .icon .svg-icon:not(.right-triangle) { +.runjs-codelist-view-container .view-content .icon .svg-icon:not(.right-triangle) { --icon-size: var(--icon-xs); } -.runjs-listview-container .view-content .icon { +.runjs-codelist-view-container .view-content .icon { opacity: var(--icon-opacity); opacity: calc(var(--icon-opacity) * 0.6); } -.runjs-listview-menu .menu-item, -.runjs-listview-container .is-clickable { +.runjs-codelist-view-menu .menu-item, +.runjs-codelist-view-container .is-clickable { cursor: pointer; } @media (hover: hover) { -.runjs-listview-menu .menu-item:active, -.runjs-listview-menu .menu-item:active .svg-icon:not(.right-triangle), +.runjs-codelist-view-menu .menu-item:active, +.runjs-codelist-view-menu .menu-item:active .svg-icon:not(.right-triangle), body:not(.is-grabbing) -.runjs-listview-container .tree-item-self.is-clickable:active, -.runjs-listview-container .tree-item-self.is-clickable:active .svg-icon:not(.right-triangle), -.runjs-listview-container .icon.is-clickable:active .svg-icon:not(.right-triangle) { +.runjs-codelist-view-container .tree-item-self.is-clickable:active, +.runjs-codelist-view-container .tree-item-self.is-clickable:active .svg-icon:not(.right-triangle), +.runjs-codelist-view-container .icon.is-clickable:active .svg-icon:not(.right-triangle) { color: orange; background-color: rgba(255, 255, 255, 0.1); } } -.runjs-listview-container .tree-item.module .tree-item-self.is-clickable { +.runjs-codelist-view-container .tree-item.module .tree-item-self.is-clickable { cursor: initial; } -.runjs-listview-container .view-content .tree-item.favorite .tree-item-self:after { +.runjs-codelist-view-container .view-content .tree-item.favorite .tree-item-self:after { content: "❤️"; font-size: var(--nav-item-size); margin-left: auto; opacity: 0.3; } -.runjs-listview-container .view-content .tree-item.favorite:after svg{ +.runjs-codelist-view-container .view-content .tree-item.favorite:after svg{ stroke: var(--nav-item-color); } -.runjs-listview-container .nav-header .ft-item.favorite, -.runjs-listview-menu .ft-item.favorite +.runjs-codelist-view-container .nav-header .ft-item.favorite, +.runjs-codelist-view-menu .ft-item.favorite { opacity: 0.3; } -.runjs-listview-container.ft-favorite .nav-header .ft-item.favorite, -.runjs-listview-menu.ft-favorite .ft-item.favorite { +.runjs-codelist-view-container.ft-favorite .nav-header .ft-item.favorite, +.runjs-codelist-view-menu.ft-favorite .ft-item.favorite { opacity: 1; } -.runjs-listview-menu .ft-item +.runjs-codelist-view-menu .ft-item { opacity: 0.3; } -.runjs-listview-menu.ft-script .ft-item.script, -.runjs-listview-menu.ft-module .ft-item.module, -.runjs-listview-menu.ft-file .ft-item.file, -.runjs-listview-menu.ft-codeblock .ft-item.codeblock +.runjs-codelist-view-menu.ft-script .ft-item.script, +.runjs-codelist-view-menu.ft-module .ft-item.module, +.runjs-codelist-view-menu.ft-file .ft-item.file, +.runjs-codelist-view-menu.ft-codeblock .ft-item.codeblock { opacity: 1; } -.runjs-listview-container .view-content .tree-item { +.runjs-codelist-view-container .view-content .tree-item { white-space: nowrap; } -.runjs-listview-container .view-content .tree-item .tree-item-self:not(.nav-folder-title) .icon:first-child, -.runjs-listview-container.ft-script:not(.ft-module) .view-content .icon.form, -.runjs-listview-container.ft-module:not(.ft-script) .view-content .icon.form +.runjs-codelist-view-container .view-content .tree-item .tree-item-self:not(.nav-folder-title) .icon:first-child, +.runjs-codelist-view-container.ft-script:not(.ft-module) .view-content .icon.form, +.runjs-codelist-view-container.ft-module:not(.ft-script) .view-content .icon.form { margin-left: calc(-1 * var(--size-4-5)); } -.runjs-listview-container.ft-script:not(.ft-module) .view-content .icon.type, -.runjs-listview-container.ft-module:not(.ft-script) .view-content .icon.type { +.runjs-codelist-view-container.ft-script:not(.ft-module) .view-content .icon.type, +.runjs-codelist-view-container.ft-module:not(.ft-script) .view-content .icon.type { display: none; } -.runjs-listview-container .view-content .tree-item .tree-item-self .name-node { +.runjs-codelist-view-container .view-content .tree-item .tree-item-self .name-node { width: 100%; } -.runjs-listview-container .view-content .tree-item.is-collapsed .tree-item-children { +.runjs-codelist-view-container .view-content .tree-item.is-collapsed .tree-item-children { display: none; } -.runjs-listview-container .view-content .tree-item .tree-item-self .group-name { +.runjs-codelist-view-container .view-content .tree-item .tree-item-self .group-name { display: none; } -.runjs-listview-container.ft-favorite .view-content .tree-item:not(.nav-folder):not(.favorite), -.runjs-listview-container.ft-favorite .view-content .tree-item.nav-folder:not(.has-favorite), -.runjs-listview-container.ft-script:not(.ft-module) .view-content .tree-item.module, -.runjs-listview-container.ft-module:not(.ft-script) .view-content .tree-item.script, -.runjs-listview-container.ft-file:not(.ft-codeblock) .view-content .tree-item.codeblock, -.runjs-listview-container.ft-codeblock:not(.ft-file) .view-content .tree-item.file { +.runjs-codelist-view-container.ft-favorite .view-content .tree-item:not(.nav-folder):not(.favorite), +.runjs-codelist-view-container.ft-favorite .view-content .tree-item.nav-folder:not(.has-favorite), +.runjs-codelist-view-container.ft-script:not(.ft-module) .view-content .tree-item.module, +.runjs-codelist-view-container.ft-module:not(.ft-script) .view-content .tree-item.script, +.runjs-codelist-view-container.ft-file:not(.ft-codeblock) .view-content .tree-item.codeblock, +.runjs-codelist-view-container.ft-codeblock:not(.ft-file) .view-content .tree-item.file { display: none; } -.runjs-listview-container .view-content .tree-item.nav-folder > .tree-item-self::after { +.runjs-codelist-view-container .view-content .tree-item.nav-folder > .tree-item-self::after { content: '(' attr(data-item_len) ')'; white-space: nowrap; margin-left: auto; @@ -153,7 +153,6 @@ body:not(.is-grabbing) .runjs-settingtab .setting-head .setting-item-info > * { display: inline-block; - float: left; } .runjs-settingtab .setting-head .setting-item-info .setting-item-description { @@ -204,7 +203,7 @@ body:not(.is-grabbing) } .runjs-settingtab .setting-log-check::after { - content: attr(aria-label); + content: attr(data-label); margin-left: 0.5em; } @@ -225,18 +224,18 @@ body:not(.is-grabbing) margin: 0 auto; } -.runjs-code-modal .modal-title { +.runjs-codelist-modal .modal-title { padding: var(--size-4-3); } -.runjs-code-modal .suggestion-icon { +.runjs-codelist-modal .suggestion-icon { margin-right: 0.5em; vertical-align: middle; opacity: 0.5; } .runjs-object-modal .folder .suggestion-content::after, -.runjs-code-modal .folder .suggestion-content::after { +.runjs-codelist-modal .folder .suggestion-content::after { content: "▶"; font-size: 0.5em; margin-top: 0.5em; @@ -244,20 +243,20 @@ body:not(.is-grabbing) opacity: 0.5; } -.runjs-code-modal .icon .svg-icon:not(.right-triangle) { +.runjs-codelist-modal .icon .svg-icon:not(.right-triangle) { --icon-size: var(--icon-m); --icon-stroke: var(--icon-s-stroke-width); } -.runjs-code-modal[data-is_group=true] .prompt-instruction:not(.group){ +.runjs-codelist-modal[data-is_group=true] .prompt-instruction:not(.group){ display: none; } -.runjs-code-modal[data-is_group=false] .prompt-instruction:not(.normal){ +.runjs-codelist-modal[data-is_group=false] .prompt-instruction:not(.normal){ display: none; } -.runjs-code-modal[data-is_group=true] .prompt-input-container::before { +.runjs-codelist-modal[data-is_group=true] .prompt-input-container::before { content: attr(data-parent); margin: auto calc( 0.2em - var(--size-4-6)) auto 0; z-index: 9999; @@ -269,18 +268,18 @@ body:not(.is-grabbing) padding-left: calc( 0.2em + var(--size-4-6)); } -.runjs-code-modal .prompt-input-container:not([data-parent='']):hover::before { +.runjs-codelist-modal .prompt-input-container:not([data-parent='']):hover::before { background-color: var(--background-modifier-hover); cursor: pointer; } .runjs-object-modal .prompt-instruction.event_click, -.runjs-code-modal .prompt-instruction { +.runjs-codelist-modal .prompt-instruction { cursor: pointer; } .runjs-object-modal .prompt-instruction.event_click:hover, -.runjs-code-modal .prompt-instruction:hover { +.runjs-codelist-modal .prompt-instruction:hover { background-color: var(--background-modifier-hover); } @@ -303,3 +302,37 @@ body:not(.is-grabbing) user-select: text; -webkit-user-select: text; } + +.runjs-prompt-modal textarea.prompt-input { + width: 100%; + /* padding: var(--size-4-6); */ + background-color: var(--background-primary); + font-size: var(--font-ui-medium); + border: none; + height: 240px; + border-radius: 0; + border-bottom: 1px solid var(--background-secondary); +} + +.runjs-prompt-modal input.prompt-input, +.runjs-prompt-modal textarea.prompt-input { + padding: inherit; +} + +.runjs-prompt-modal textarea.prompt-input:hover, +.runjs-prompt-modal textarea.prompt-input:focus, +.runjs-prompt-modal textarea.prompt-input:focus-visible { + border-bottom: 1px solid var(--background-secondary); + box-shadow: none; +} + +.runjs-suggest-modal .modal-title { + padding: var(--size-4-3); + padding-bottom: 0; +} + +.runjs-suggest-modal .modal-content { + padding: var(--size-4-3); + padding-top: 0; + padding-bottom: 0; +} diff --git a/versions.json b/versions.json index 4693b21..9721f69 100644 --- a/versions.json +++ b/versions.json @@ -4,5 +4,6 @@ "0.1.1": "0.15.0", "0.1.2": "0.15.0", "0.2.0": "0.15.0", - "0.3.0": "0.15.0" + "0.3.0": "0.15.0", + "0.4.0": "0.15.0" } \ No newline at end of file