From 4bad19f8f168f8780c92b7c7854799ac14e9bd66 Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Wed, 17 Jan 2024 13:44:22 +0800
Subject: [PATCH 01/12] fix: cannot use last state
---
manifest.json | 2 +-
package.json | 50 +-
src/floatSearchIndex.ts | 1534 ++++++++++++++++++++-------------------
versions.json | 3 +-
4 files changed, 806 insertions(+), 783 deletions(-)
diff --git a/manifest.json b/manifest.json
index 42ca387..885816e 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "float-search",
"name": "Floating Search",
- "version": "3.4.5",
+ "version": "3.4.6",
"minAppVersion": "0.15.0",
"description": "You can use search view in modal/leaf/popout window now.",
"author": "Boninall",
diff --git a/package.json b/package.json
index b865b46..b4454a4 100644
--- a/package.json
+++ b/package.json
@@ -1,27 +1,27 @@
{
- "name": "float-search",
- "version": "3.4.5",
- "description": "You can use search view in modal/leaf/popout window now.",
- "main": "main.js",
- "scripts": {
- "dev": "node esbuild.config.mjs",
- "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
- "version": "node version-bump.mjs && git add manifest.json versions.json"
- },
- "keywords": [],
- "author": "Boninall",
- "license": "GPL-3.0",
- "devDependencies": {
- "@types/node": "^16.18.50",
- "@typescript-eslint/eslint-plugin": "5.29.0",
- "@typescript-eslint/parser": "5.29.0",
- "builtin-modules": "3.3.0",
- "esbuild": "0.14.47",
- "obsidian": "latest",
- "tslib": "2.4.0",
- "typescript": "4.7.4"
- },
- "dependencies": {
- "monkey-around": "^2.3.0"
- }
+ "name": "float-search",
+ "version": "3.4.6",
+ "description": "You can use search view in modal/leaf/popout window now.",
+ "main": "main.js",
+ "scripts": {
+ "dev": "node esbuild.config.mjs",
+ "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
+ "version": "node version-bump.mjs && git add manifest.json versions.json"
+ },
+ "keywords": [],
+ "author": "Boninall",
+ "license": "GPL-3.0",
+ "devDependencies": {
+ "@types/node": "^16.18.50",
+ "@typescript-eslint/eslint-plugin": "5.29.0",
+ "@typescript-eslint/parser": "5.29.0",
+ "builtin-modules": "3.3.0",
+ "esbuild": "0.14.47",
+ "obsidian": "latest",
+ "tslib": "2.4.0",
+ "typescript": "4.7.4"
+ },
+ "dependencies": {
+ "monkey-around": "^2.3.0"
+ }
}
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index 5be5f21..46b09c1 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -1,891 +1,913 @@
import {
- addIcon,
- App,
- Editor, ExtraButtonComponent,
- Menu, MenuItem,
- Modal, OpenViewState, PaneType,
- Plugin, SearchView, setIcon,
- TAbstractFile,
- TFile, ViewStateResult,
- Workspace,
- WorkspaceContainer, WorkspaceItem,
- WorkspaceLeaf
+ addIcon,
+ App,
+ Editor, ExtraButtonComponent,
+ Menu, MenuItem,
+ Modal, OpenViewState, PaneType,
+ Plugin, Scope, SearchView, setIcon,
+ TAbstractFile,
+ TFile, ViewStateResult,
+ Workspace,
+ WorkspaceContainer, WorkspaceItem,
+ WorkspaceLeaf
} from 'obsidian';
import { EmbeddedView, isEmebeddedLeaf, spawnLeafView } from "./leafView";
import { around } from "monkey-around";
type sortOrder =
- "alphabetical"
- | "alphabeticalReverse"
- | "byModifiedTime"
- | "byModifiedTimeReverse"
- | "byCreatedTime"
- | "byCreatedTimeReverse";
+ "alphabetical"
+ | "alphabeticalReverse"
+ | "byModifiedTime"
+ | "byModifiedTimeReverse"
+ | "byCreatedTime"
+ | "byCreatedTimeReverse";
type searchType = "modal" | "sidebar" | PaneType;
interface viewType {
- type: searchType;
- icon: string;
+ type: searchType;
+ icon: string;
}
interface searchState {
- collapseAll?: boolean;
- explainSearch?: boolean;
- extraContext?: boolean;
- matchingCase?: boolean;
- query: string;
- sortOrder?: sortOrder;
- current?: boolean;
+ collapseAll?: boolean;
+ explainSearch?: boolean;
+ extraContext?: boolean;
+ matchingCase?: boolean;
+ query: string;
+ sortOrder?: sortOrder;
+ current?: boolean;
}
interface FloatSearchSettings {
- searchViewState: searchState;
+ searchViewState: searchState;
}
const DEFAULT_SETTINGS: FloatSearchSettings = {
- searchViewState: {
- collapseAll: false,
- explainSearch: false,
- extraContext: false,
- matchingCase: false,
- query: "",
- sortOrder: "alphabetical",
- }
-}
+ searchViewState: {
+ collapseAll: false,
+ explainSearch: false,
+ extraContext: false,
+ matchingCase: false,
+ query: "",
+ sortOrder: "alphabetical",
+ }
+};
const allViews: viewType[] = [{
- type: "modal",
- icon: "square-equal"
+ type: "modal",
+ icon: "square-equal"
}, {
- type: "sidebar",
- icon: "panel-left-inactive"
+ type: "sidebar",
+ icon: "panel-left-inactive"
}, {
- type: "split",
- icon: "split-square-horizontal"
+ type: "split",
+ icon: "split-square-horizontal"
}, {
- type: "tab",
- icon: "panel-top"
+ type: "tab",
+ icon: "panel-top"
}, {
- type: "window",
- icon: "app-window"
+ type: "window",
+ icon: "app-window"
}];
const initSearchViewWithLeaf = async (app: App, type: PaneType | 'sidebar', state?: searchState) => {
- const leaf = type === 'sidebar' ? app.workspace.getLeftLeaf(false) : app.workspace.getLeaf(type);
- leaf.setPinned(type !== 'sidebar');
- await leaf.setViewState({
- type: "search",
- active: true,
- state: state
- });
- setTimeout(() => {
- const inputEl = leaf.containerEl.getElementsByTagName("input")[0];
- inputEl.focus();
- }, 0);
-}
+ const leaf = type === 'sidebar' ? app.workspace.getLeftLeaf(false) : app.workspace.getLeaf(type);
+ leaf.setPinned(type !== 'sidebar');
+ await leaf.setViewState({
+ type: "search",
+ active: true,
+ state: state
+ });
+
+ setTimeout(() => {
+ const inputEl = leaf.containerEl.getElementsByTagName("input")[0];
+ inputEl.focus();
+ }, 0);
+};
export default class FloatSearchPlugin extends Plugin {
- settings: FloatSearchSettings;
- private state: any;
- private modal: FloatSearchModal;
+ settings: FloatSearchSettings;
+ private state: any;
+ private modal: FloatSearchModal;
- private applyStateDebounceTimer = 0;
- private applySettingsDebounceTimer = 0;
+ private applyStateDebounceTimer = 0;
+ private applySettingsDebounceTimer = 0;
- public applySettingsUpdate() {
- clearTimeout(this.applySettingsDebounceTimer);
- this.applySettingsDebounceTimer = window.setTimeout(async () => {
- await this.saveSettings();
- }, 1000);
- }
+ public applySettingsUpdate() {
+ clearTimeout(this.applySettingsDebounceTimer);
+ this.applySettingsDebounceTimer = window.setTimeout(async () => {
+ await this.saveSettings();
+ }, 1000);
+ }
- private applyStateUpdate() {
- this.applyStateDebounceTimer = window.setTimeout(() => {
- clearTimeout(this.applyStateDebounceTimer);
- this.state = {
- ...this.state,
- query: "",
- };
- }, 30000);
- }
+ private applyStateUpdate() {
+ this.applyStateDebounceTimer = window.setTimeout(() => {
+ clearTimeout(this.applyStateDebounceTimer);
+ this.state = {
+ ...this.state,
+ query: "",
+ };
+ }, 30000);
+ }
- async onload() {
- await this.loadSettings();
- this.initState();
- this.registerIcons();
+ async onload() {
+ await this.loadSettings();
+ this.initState();
+ this.registerIcons();
- this.patchWorkspace();
- this.patchWorkspaceLeaf();
- this.patchSearchView();
+ this.patchWorkspace();
+ this.patchWorkspaceLeaf();
+ this.patchSearchView();
- this.registerObsidianURIHandler();
- this.registerObsidianCommands();
- this.registerEditorMenuHandler();
- this.registerContextMenuHandler();
+ this.registerObsidianURIHandler();
+ this.registerObsidianCommands();
+ this.registerEditorMenuHandler();
+ this.registerContextMenuHandler();
- this.addRibbonIcon('search', 'Search Obsidian In Modal', () => this.initModal(this.state, true, true));
- }
+ this.addRibbonIcon('search', 'Search Obsidian In Modal', () => this.initModal(this.state, true, true));
+ }
- onunload() {
- this.state = undefined;
- this.modal?.close();
- }
+ onunload() {
+ this.state = undefined;
+ this.modal?.close();
+ }
- registerIcons() {
- addIcon('panel-left-inactive', ``)
- addIcon('app-window', ``);
- addIcon('panel-top', ``);
- addIcon('square-equal', ``);
- }
+ registerIcons() {
+ addIcon('panel-left-inactive', ``);
+ addIcon('app-window', ``);
+ addIcon('panel-top', ``);
+ addIcon('square-equal', ``);
+ }
- initState() {
- this.state = this.settings.searchViewState as searchState;
- }
+ initState() {
+ this.state = this.settings.searchViewState as searchState;
+ }
- initModal(state: searchState, stateSave: boolean = false, clearQuery: boolean = false) {
- this.modal = new FloatSearchModal((state) => {
- this.state = state;
- if (stateSave) this.applyStateUpdate();
- this.settings.searchViewState = state as searchState;
- this.applySettingsUpdate();
- }, this, {...state, query: clearQuery ? "" : state.query});
- this.modal.open();
- }
+ initModal(state: searchState, stateSave: boolean = false, clearQuery: boolean = false) {
+ this.modal = new FloatSearchModal((state) => {
+ this.state = state;
+ if (stateSave) this.applyStateUpdate();
+ this.settings.searchViewState = state as searchState;
+ this.applySettingsUpdate();
+ }, this, {...state, query: clearQuery ? "" : state.query});
+ this.modal.open();
+ }
- patchWorkspace() {
- let layoutChanging = false;
- const uninstaller = around(Workspace.prototype, {
- getLeaf: (next) =>
- function (...args) {
- const activeLeaf = this.activeLeaf;
- if (activeLeaf) {
- const fsCtnEl = (activeLeaf.parent.containerEl as HTMLElement).parentElement;
- if (fsCtnEl?.hasClass("fs-content")) {
- if (activeLeaf.view.getViewType() === "markdown") {
- return activeLeaf;
- }
- const newLeaf = app.workspace.getUnpinnedLeaf();
- if (newLeaf) {
- this.setActiveLeaf(newLeaf);
- }
- }
- return next.call(this, ...args);
- }
- return next.call(this, ...args);
- },
- changeLayout(old) {
- return async function (workspace: unknown) {
- layoutChanging = true;
- try {
- // Don't consider hover popovers part of the workspace while it's changing
- await old.call(this, workspace);
- } finally {
- layoutChanging = false;
- }
- };
- },
- iterateLeaves(old) {
- type leafIterator = (item: WorkspaceLeaf) => boolean | void;
- return function (arg1, arg2) {
- // Fast exit if desired leaf found
- if (old.call(this, arg1, arg2)) return true;
+ patchWorkspace() {
+ let layoutChanging = false;
+ const uninstaller = around(Workspace.prototype, {
+ getLeaf: (next) =>
+ function (...args) {
+ const activeLeaf = this.activeLeaf;
+ if (activeLeaf) {
+ const fsCtnEl = (activeLeaf.parent.containerEl as HTMLElement).parentElement;
+ if (fsCtnEl?.hasClass("fs-content")) {
+ if (activeLeaf.view.getViewType() === "markdown") {
+ return activeLeaf;
+ }
+ const newLeaf = app.workspace.getUnpinnedLeaf();
+ if (newLeaf) {
+ this.setActiveLeaf(newLeaf);
+ }
+ }
+ return next.call(this, ...args);
+ }
+ return next.call(this, ...args);
+ },
+ changeLayout(old) {
+ return async function (workspace: unknown) {
+ layoutChanging = true;
+ try {
+ // Don't consider hover popovers part of the workspace while it's changing
+ await old.call(this, workspace);
+ } finally {
+ layoutChanging = false;
+ }
+ };
+ },
+ iterateLeaves(old) {
+ type leafIterator = (item: WorkspaceLeaf) => boolean | void;
+ return function (arg1, arg2) {
+ // Fast exit if desired leaf found
+ if (old.call(this, arg1, arg2)) return true;
- // Handle old/new API parameter swap
- const cb: leafIterator = (typeof arg1 === "function" ? arg1 : arg2) as leafIterator;
- const parent: WorkspaceItem = (typeof arg1 === "function" ? arg2 : arg1) as WorkspaceItem;
+ // Handle old/new API parameter swap
+ const cb: leafIterator = (typeof arg1 === "function" ? arg1 : arg2) as leafIterator;
+ const parent: WorkspaceItem = (typeof arg1 === "function" ? arg2 : arg1) as WorkspaceItem;
- if (!parent) return false; // <- during app startup, rootSplit can be null
- if (layoutChanging) return false; // Don't let HEs close during workspace change
+ if (!parent) return false; // <- during app startup, rootSplit can be null
+ if (layoutChanging) return false; // Don't let HEs close during workspace change
- // 0.14.x doesn't have WorkspaceContainer; this can just be an instanceof check once 15.x is mandatory:
- if (parent === app.workspace.rootSplit || (WorkspaceContainer && parent instanceof WorkspaceContainer)) {
- for (const popover of EmbeddedView.popoversForWindow((parent as WorkspaceContainer).win)) {
- // Use old API here for compat w/0.14.x
- if (old.call(this, cb, popover.rootSplit)) return true;
- }
- }
- return false;
- };
- },
- onDragLeaf(old) {
- return function (event: MouseEvent, leaf: WorkspaceLeaf) {
- return old.call(this, event, leaf);
- };
- },
- pushUndoHistory(old: any) {
- return function (leaf: WorkspaceLeaf, id: string, ...args: any[]) {
- const viewState = leaf.getViewState();
- if (viewState.type === "search") {
- return;
- }
- return old.call(this, leaf, id, ...args);
- };
- }
- });
- this.register(uninstaller);
- }
+ // 0.14.x doesn't have WorkspaceContainer; this can just be an instanceof check once 15.x is mandatory:
+ if (parent === app.workspace.rootSplit || (WorkspaceContainer && parent instanceof WorkspaceContainer)) {
+ for (const popover of EmbeddedView.popoversForWindow((parent as WorkspaceContainer).win)) {
+ // Use old API here for compat w/0.14.x
+ if (old.call(this, cb, popover.rootSplit)) return true;
+ }
+ }
+ return false;
+ };
+ },
+ onDragLeaf(old) {
+ return function (event: MouseEvent, leaf: WorkspaceLeaf) {
+ return old.call(this, event, leaf);
+ };
+ },
+ pushUndoHistory(old: any) {
+ return function (leaf: WorkspaceLeaf, id: string, ...args: any[]) {
+ const viewState = leaf.getViewState();
+ if (viewState.type === "search") {
+ return;
+ }
+ return old.call(this, leaf, id, ...args);
+ };
+ }
+ });
+ this.register(uninstaller);
+ }
- // Used for patch workspaceleaf pinned behaviors
- patchWorkspaceLeaf() {
- this.register(
- around(WorkspaceLeaf.prototype, {
- getRoot(old) {
- return function () {
- const top = old.call(this);
- return top?.getRoot === this.getRoot ? top : top?.getRoot();
- };
- },
- setPinned(old) {
- return function (pinned: boolean) {
- old.call(this, pinned);
- if (isEmebeddedLeaf(this) && !pinned) this.setPinned(true);
- }
- },
- openFile(old) {
- return function (file: TFile, openState?: OpenViewState) {
- if (isEmebeddedLeaf(this)) {
- setTimeout(
- around(Workspace.prototype, {
- recordMostRecentOpenedFile(old) {
- return function (_file: TFile) {
- // Don't update the quick switcher's recent list
- if (_file !== file) {
- return old.call(this, _file);
- }
- };
- },
- }),
- 1,
- );
- const recentFiles = this.app.plugins.plugins["recent-files-obsidian"];
- if (recentFiles) {
- setTimeout(
- around(recentFiles, {
- shouldAddFile(old) {
- return function (_file: TFile) {
- // Don't update the Recent Files plugin
- return _file !== file && old.call(this, _file);
- };
- },
- }),
- 1,
- );
- }
+ // Used for patch workspaceleaf pinned behaviors
+ patchWorkspaceLeaf() {
+ this.register(
+ around(WorkspaceLeaf.prototype, {
+ getRoot(old) {
+ return function () {
+ const top = old.call(this);
+ return top?.getRoot === this.getRoot ? top : top?.getRoot();
+ };
+ },
+ setPinned(old) {
+ return function (pinned: boolean) {
+ old.call(this, pinned);
+ if (isEmebeddedLeaf(this) && !pinned) this.setPinned(true);
+ };
+ },
+ openFile(old) {
+ return function (file: TFile, openState?: OpenViewState) {
+ if (isEmebeddedLeaf(this)) {
+ setTimeout(
+ around(Workspace.prototype, {
+ recordMostRecentOpenedFile(old) {
+ return function (_file: TFile) {
+ // Don't update the quick switcher's recent list
+ if (_file !== file) {
+ return old.call(this, _file);
+ }
+ };
+ },
+ }),
+ 1,
+ );
+ const recentFiles = this.app.plugins.plugins["recent-files-obsidian"];
+ if (recentFiles) {
+ setTimeout(
+ around(recentFiles, {
+ shouldAddFile(old) {
+ return function (_file: TFile) {
+ // Don't update the Recent Files plugin
+ return _file !== file && old.call(this, _file);
+ };
+ },
+ }),
+ 1,
+ );
+ }
- }
+ }
- const view = old.call(this, file, openState);
- setTimeout(() => {
- const fsCtnEl = (this.parent.containerEl as HTMLElement).parentElement;
- if (!(fsCtnEl?.classList.contains("fs-content"))) return;
- if (file.extension != "canvas") return;
+ const view = old.call(this, file, openState);
+ setTimeout(() => {
+ const fsCtnEl = (this.parent.containerEl as HTMLElement).parentElement;
+ if (!(fsCtnEl?.classList.contains("fs-content"))) return;
+ if (file.extension != "canvas") return;
- const canvas = this.view.canvas;
- setTimeout(() => {
- if (canvas && openState?.eState?.match) {
- let node = canvas.data.nodes?.find((e: any) => e.text === openState.eState.match.content);
- node = canvas.nodes.get(node.id);
+ const canvas = this.view.canvas;
+ setTimeout(() => {
+ if (canvas && openState?.eState?.match) {
+ let node = canvas.data.nodes?.find((e: any) => e.text === openState.eState.match.content);
+ node = canvas.nodes.get(node.id);
- canvas.selectOnly(node);
- canvas.zoomToSelection();
- }
- }, 20);
- }, 1);
+ canvas.selectOnly(node);
+ canvas.zoomToSelection();
+ }
+ }, 20);
+ }, 1);
- return view;
- }
- },
- }),
- );
- }
+ return view;
+ };
+ },
+ }),
+ );
+ }
- patchSearchView() {
- const updateCurrentState = (state: searchState) => {
- this.state = state;
- this.settings.searchViewState = state as searchState;
- this.applySettingsUpdate();
- }
+ patchSearchView() {
+ const updateCurrentState = (state: searchState) => {
+ this.state = state;
+ this.settings.searchViewState = state as searchState;
+ this.applySettingsUpdate();
+ };
- const checkCurrentViewType = (leaf: WorkspaceLeaf) => {
- const isModal = document.querySelector('.float-search-modal') !== null;
- const currentLeafRoot = leaf.getRoot();
- if (currentLeafRoot?.side && (currentLeafRoot?.side === "left" || currentLeafRoot?.side === 'right')) return "sidebar";
- if (leaf.getContainer()?.type === "window") return "window";
- return isModal ? "modal" : "split";
- }
+ const checkCurrentViewType = (leaf: WorkspaceLeaf) => {
+ const isModal = document.querySelector('.float-search-modal') !== null;
+ const currentLeafRoot = leaf.getRoot();
+ if (currentLeafRoot?.side && (currentLeafRoot?.side === "left" || currentLeafRoot?.side === 'right')) return "sidebar";
+ if (leaf.getContainer()?.type === "window") return "window";
+ return isModal ? "modal" : "split";
+ };
- const initViewMenu = (menu: Menu, current: searchType, originLeaf?: WorkspaceLeaf) => {
- menu.dom.toggleClass("float-search-view-menu", true);
- let availableViews = allViews.filter((view) => {
- if (current === "split") {
- return view.type !== "tab";
- } else {
- return view.type !== current;
- }
- });
- for (const view of availableViews) {
- menu.addItem((item: MenuItem) => {
- item.setTitle(`${view.type} view`).setIcon(`${view.icon}`).onClick(async () => {
- if (view.type === "modal") {
- originLeaf?.detach();
- setTimeout(() => {
- this.initModal(this.state, true, false);
- }, 10);
- } else if (view.type === "sidebar") {
- await initSearchViewWithLeaf(this.app, view.type, this.state);
- } else {
- if (current === "window") {
- originLeaf?.detach();
- setTimeout(async () => {
- await initSearchViewWithLeaf(this.app, <"tab" | "split">view.type, this.state);
- }, 10);
- } else {
- await initSearchViewWithLeaf(this.app, view.type, this.state);
- }
+ const initViewMenu = (menu: Menu, current: searchType, originLeaf?: WorkspaceLeaf) => {
+ menu.dom.toggleClass("float-search-view-menu", true);
+ let availableViews = allViews.filter((view) => {
+ if (current === "split") {
+ return view.type !== "tab";
+ } else {
+ return view.type !== current;
+ }
+ });
+ for (const view of availableViews) {
+ menu.addItem((item: MenuItem) => {
+ item.setTitle(`${view.type} view`).setIcon(`${view.icon}`).onClick(async () => {
+ if (view.type === "modal") {
+ originLeaf?.detach();
+ setTimeout(() => {
+ this.initModal(this.state, true, false);
+ }, 10);
+ } else if (view.type === "sidebar") {
+ await initSearchViewWithLeaf(this.app, view.type, this.state);
+ } else {
+ if (current === "window") {
+ originLeaf?.detach();
+ setTimeout(async () => {
+ await initSearchViewWithLeaf(this.app, <"tab" | "split">view.type, this.state);
+ }, 10);
+ } else {
+ await initSearchViewWithLeaf(this.app, view.type, this.state);
+ }
- }
- if (current === "modal") {
- this.modal.close();
- } else {
- originLeaf?.detach();
- }
- });
- });
- }
- return menu;
- }
+ }
+ if (current === "modal") {
+ this.modal.close();
+ } else {
+ originLeaf?.detach();
+ }
+ });
+ });
+ }
+ return menu;
+ };
- const patchSearch = () => {
- const searchView = this.app.workspace.getLeavesOfType("search")[0]?.view as any;
+ const patchSearch = () => {
+ const searchView = this.app.workspace.getLeavesOfType("search")[0]?.view as any;
- if (!searchView) return false;
+ if (!searchView) return false;
- const searchViewConstructor = searchView.constructor;
+ const searchViewConstructor = searchView.constructor;
- this.register(
- around(searchViewConstructor.prototype, {
- onOpen(old) {
- return function () {
- old.call(this);
- const viewSwitchEl = createDiv({cls: "float-search-view-switch"});
- const targetEl = this.filterSectionToggleEl;
- const viewSwitchButton = new ExtraButtonComponent(viewSwitchEl);
- viewSwitchButton.setIcon('layout-template').setTooltip("Switch to File View");
- viewSwitchButton.onClick(() => {
- const currentType = checkCurrentViewType(this.leaf);
- const layoutMenu = initViewMenu(new Menu(), currentType, this.leaf);
- const viewSwitchButtonPos = viewSwitchEl.getBoundingClientRect();
- layoutMenu.showAtPosition({x: viewSwitchButtonPos.x, y: viewSwitchButtonPos.y + 30});
- });
- targetEl.parentElement.insertBefore(viewSwitchEl, targetEl);
- }
- },
- startSearch(old) {
- return function () {
- old.call(this);
- const viewState = this.getState();
- updateCurrentState({
- ...viewState,
- query: this.searchComponent.getValue(),
- });
- }
- }
- })
- );
- searchView.leaf?.rebuildView();
- console.log("Metadata-Style: all property view get patched");
- return true;
- };
- this.app.workspace.onLayoutReady(() => {
- if (!patchSearch()) {
- const evt = this.app.workspace.on("layout-change", () => {
- patchSearch() && this.app.workspace.offref(evt);
- });
- this.registerEvent(evt);
- }
- });
- }
+ this.register(
+ around(searchViewConstructor.prototype, {
+ onOpen(old) {
+ return function () {
+ old.call(this);
+ const viewSwitchEl = createDiv({cls: "float-search-view-switch"});
+ const targetEl = this.filterSectionToggleEl;
+ const viewSwitchButton = new ExtraButtonComponent(viewSwitchEl);
+ viewSwitchButton.setIcon('layout-template').setTooltip("Switch to File View");
+ viewSwitchButton.onClick(() => {
+ const currentType = checkCurrentViewType(this.leaf);
+ const layoutMenu = initViewMenu(new Menu(), currentType, this.leaf);
+ const viewSwitchButtonPos = viewSwitchEl.getBoundingClientRect();
+ layoutMenu.showAtPosition({x: viewSwitchButtonPos.x, y: viewSwitchButtonPos.y + 30});
+ });
+ targetEl.parentElement.insertBefore(viewSwitchEl, targetEl);
+ };
+ },
+ startSearch(old) {
+ return function () {
+ old.call(this);
+ const viewState = this.getState();
+ updateCurrentState({
+ ...viewState,
+ query: this.searchComponent.getValue(),
+ });
+ };
+ }
+ })
+ );
+ searchView.leaf?.rebuildView();
+ console.log("Metadata-Style: all property view get patched");
+ return true;
+ };
+ this.app.workspace.onLayoutReady(() => {
+ if (!patchSearch()) {
+ const evt = this.app.workspace.on("layout-change", () => {
+ patchSearch() && this.app.workspace.offref(evt);
+ });
+ this.registerEvent(evt);
+ }
+ });
+ }
- registerObsidianURIHandler() {
- this.registerObsidianProtocolHandler("fs", (path) => this.initModal({
- ...this.state,
- query: path.query,
- current: false
- }, true, true));
- }
+ registerObsidianURIHandler() {
+ this.registerObsidianProtocolHandler("fs", (path) => this.initModal({
+ ...this.state,
+ query: path.query,
+ current: false
+ }, true, true));
+ }
- private createCommand(options: {
- id: string;
- name: string;
- queryBuilder: (file: any) => string;
- }): void {
- this.addCommand({
- id: options.id,
- name: options.name,
- checkCallback: (checking: boolean) => {
- const activeLeaf = this.app.workspace.activeLeaf;
- if (!activeLeaf) return;
+ private createCommand(options: {
+ id: string;
+ name: string;
+ queryBuilder: (file: any) => string;
+ }): void {
+ this.addCommand({
+ id: options.id,
+ name: options.name,
+ checkCallback: (checking: boolean) => {
+ const activeLeaf = this.app.workspace.activeLeaf;
+ if (!activeLeaf) return;
- const viewType = activeLeaf.view.getViewType();
- if (viewType === "markdown" || viewType === "canvas") {
- if (!checking) {
- const currentFile = activeLeaf.view.file;
- const query = options.queryBuilder(currentFile);
- this.initModal({...this.state, query, current: true}, true, false);
- }
- return true;
- }
- }
- });
- }
+ const viewType = activeLeaf.view.getViewType();
+ if (viewType === "markdown" || viewType === "canvas") {
+ if (!checking) {
+ const currentFile = activeLeaf.view.file;
+ const query = options.queryBuilder(currentFile);
+ this.initModal({...this.state, query, current: true}, true, false);
+ }
+ return true;
+ }
+ }
+ });
+ }
- registerObsidianCommands() {
- this.addCommand({
- id: 'search-obsidian-globally',
- name: 'Search Obsidian Globally',
- callback: () => this.initModal({...this.state, query: "", current: false}, false, true)
- });
+ registerObsidianCommands() {
+ this.addCommand({
+ id: 'search-obsidian-globally',
+ name: 'Search Obsidian Globally',
+ callback: () => this.initModal({...this.state, query: "", current: false}, false, true)
+ });
- this.addCommand({
- id: 'search-obsidian-globally-state',
- name: 'Search Obsidian Globally (With Last State)',
- callback: () => this.initModal({...this.state, query: "", current: false}, true, false)
- });
+ this.addCommand({
+ id: 'search-obsidian-globally-state',
+ name: 'Search Obsidian Globally (With Last State)',
+ callback: () => this.initModal({...this.state, query: this.state.query, current: false}, true, false)
+ });
- this.createCommand({
- id: 'search-in-backlink',
- name: 'Search In Backlink Of Current File',
- queryBuilder: (file) => {
- return " /\\[\\[" + (file.extension === "canvas" ? file.name : file.basename) + "(\\|[^\\]]*)?\\]\\]/";
- }
- });
+ this.createCommand({
+ id: 'search-in-backlink',
+ name: 'Search In Backlink Of Current File',
+ queryBuilder: (file) => {
+ return " /\\[\\[" + (file.extension === "canvas" ? file.name : file.basename) + "(\\|[^\\]]*)?\\]\\]/";
+ }
+ });
- this.createCommand({
- id: 'search-in-current-file',
- name: 'Search In Current File',
- queryBuilder: (file) => {
- return " path:" + `"${file.path}"`;
- }
- });
+ this.createCommand({
+ id: 'search-in-current-file',
+ name: 'Search In Current File',
+ queryBuilder: (file) => {
+ return " path:" + `"${file.path}"`;
+ }
+ });
- for (const type of ['split', 'tab', 'window'] as PaneType[]) {
- this.addCommand({
- id: `open-search-view-${type}`,
- name: `Open Search View (${type})`,
- callback: async () => initSearchViewWithLeaf(this.app, type)
- });
- }
- }
+ for (const type of ['split', 'tab', 'window'] as PaneType[]) {
+ this.addCommand({
+ id: `open-search-view-${type}`,
+ name: `Open Search View (${type})`,
+ callback: async () => {
+ const existingLeaf = this.app.workspace.getLeavesOfType("search");
+ switch (type) {
+ case "window":
+ const isExistingWindowLeaf = existingLeaf.find((leaf) => leaf.parentSplit.parent.type === "window");
+ if (isExistingWindowLeaf) {
+ this.app.workspace.revealLeaf(isExistingWindowLeaf);
+ return;
+ }
+ await initSearchViewWithLeaf(this.app, type);
+ break;
+ case "tab":
+ case "split":
+ const isExistingLeaf = existingLeaf.find((leaf) => !leaf.parentSplit.parent.side);
+ if (isExistingLeaf) {
+ this.app.workspace.revealLeaf(isExistingLeaf);
+ return;
+ }
+ await initSearchViewWithLeaf(this.app, type);
+ break;
+ }
+ }
+ });
+ }
+ }
- registerEditorMenuHandler() {
- this.registerEvent(
- this.app.workspace.on('editor-menu', (menu: Menu, editor: Editor) => {
- if (!editor) {
- return;
- }
- if (editor.getSelection().length === 0) {
- return;
- }
- const selection = editor.getSelection().trim();
- let searchWord = selection;
+ registerEditorMenuHandler() {
+ this.registerEvent(
+ this.app.workspace.on('editor-menu', (menu: Menu, editor: Editor) => {
+ if (!editor) {
+ return;
+ }
+ if (editor.getSelection().length === 0) {
+ return;
+ }
+ const selection = editor.getSelection().trim();
+ let searchWord = selection;
- if (selection.length > 8) {
- searchWord = selection.substring(0, 3) + "..." + selection.substring(selection.length - 3, selection.length);
- } else {
- searchWord = selection;
- }
+ if (selection.length > 8) {
+ searchWord = selection.substring(0, 3) + "..." + selection.substring(selection.length - 3, selection.length);
+ } else {
+ searchWord = selection;
+ }
- menu.addItem((item) => {
- // Add sub menu
- item.setTitle('Search "' + searchWord + '"' + " in Float Search").setIcon("search")
- .onClick(() => this.initModal({...this.state, query: selection, current: false}, true, false))
- })
- }))
- }
+ menu.addItem((item) => {
+ // Add sub menu
+ item.setTitle('Search "' + searchWord + '"' + " in Float Search").setIcon("search")
+ .onClick(() => this.initModal({...this.state, query: selection, current: false}, true, false));
+ });
+ }));
+ }
- registerContextMenuHandler() {
- this.registerEvent(
- this.app.workspace.on("file-menu", (menu: Menu, file: TAbstractFile, source: string, leaf?: WorkspaceLeaf) => {
- const popover = leaf ? EmbeddedView.forLeaf(leaf) : undefined;
- if (file instanceof TFile && !popover && !leaf) {
- menu.addItem(item => {
- item
- .setIcon("popup-open")
- .setTitle("Open in Float Preview")
- .onClick(async () => {
- if (this.modal) {
- await this.modal.initFileView(file, undefined);
+ registerContextMenuHandler() {
+ this.registerEvent(
+ this.app.workspace.on("file-menu", (menu: Menu, file: TAbstractFile, source: string, leaf?: WorkspaceLeaf) => {
+ const popover = leaf ? EmbeddedView.forLeaf(leaf) : undefined;
+ if (file instanceof TFile && !popover && !leaf) {
+ menu.addItem(item => {
+ item
+ .setIcon("popup-open")
+ .setTitle("Open in Float Preview")
+ .onClick(async () => {
+ if (this.modal) {
+ await this.modal.initFileView(file, undefined);
- return;
- }
+ return;
+ }
- this.initModal({...this.state, current: false}, true, true);
- setTimeout(async () => {
- await this.modal.initFileView(file, undefined);
- }, 20);
- })
- .setSection?.("open");
- });
- }
- }),
- );
- }
+ this.initModal({...this.state, current: false}, true, true);
+ setTimeout(async () => {
+ await this.modal.initFileView(file, undefined);
+ }, 20);
+ })
+ .setSection?.("open");
+ });
+ }
+ }),
+ );
+ }
- async loadSettings() {
- this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
- }
+ async loadSettings() {
+ this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
+ }
- async saveSettings() {
- await this.saveData(this.settings);
- }
+ async saveSettings() {
+ await this.saveData(this.settings);
+ }
}
class FloatSearchModal extends Modal {
- private readonly plugin: FloatSearchPlugin;
- private searchEmbeddedView: EmbeddedView;
- private fileEmbeddedView: EmbeddedView;
+ private readonly plugin: FloatSearchPlugin;
+ private searchEmbeddedView: EmbeddedView;
+ private fileEmbeddedView: EmbeddedView;
- searchLeaf: WorkspaceLeaf;
- fileLeaf: WorkspaceLeaf | undefined;
+ searchLeaf: WorkspaceLeaf;
+ fileLeaf: WorkspaceLeaf | undefined;
- private cb: (state: any) => void;
- private state: any;
+ private cb: (state: any) => void;
+ private state: any;
- private fileState: any;
+ private fileState: any;
- private searchCtnEl: HTMLElement;
- private instructionsEl: HTMLElement;
- private fileEl: HTMLElement;
- private viewType: string;
+ private searchCtnEl: HTMLElement;
+ private instructionsEl: HTMLElement;
+ private fileEl: HTMLElement;
+ private viewType: string;
- constructor(cb: (state: any) => void, plugin: FloatSearchPlugin, state: any, viewType: string = "search") {
- super(plugin.app);
- this.plugin = plugin;
- this.cb = cb;
- this.state = state;
- this.viewType = viewType;
- }
+ constructor(cb: (state: any) => void, plugin: FloatSearchPlugin, state: any, viewType: string = "search") {
+ super(plugin.app);
+ this.plugin = plugin;
+ this.cb = cb;
+ this.state = state;
+ this.viewType = viewType;
+ }
- async onOpen() {
- const {contentEl, containerEl, modalEl} = this;
+ async onOpen() {
+ const {contentEl, containerEl, modalEl} = this;
- this.searchCtnEl = contentEl.createDiv({cls: "float-search-modal-search-ctn"});
- this.instructionsEl = modalEl.createDiv({cls: "float-search-modal-instructions"});
+ this.searchCtnEl = contentEl.createDiv({cls: "float-search-modal-search-ctn"});
+ this.instructionsEl = modalEl.createDiv({cls: "float-search-modal-instructions"});
- this.initInstructions(this.instructionsEl);
- this.initCss(contentEl, modalEl, containerEl);
- await this.initSearchView(this.searchCtnEl);
- this.initInput();
- this.initContent();
- }
+ this.initInstructions(this.instructionsEl);
+ this.initCss(contentEl, modalEl, containerEl);
+ await this.initSearchView(this.searchCtnEl);
+ this.initInput();
+ this.initContent();
+ }
- onClose() {
- const {contentEl} = this;
+ onClose() {
+ const {contentEl} = this;
- this.cb(this.searchLeaf.view.getState());
+ this.cb(this.searchLeaf.view.getState());
- this.searchLeaf.detach();
- this.fileLeaf?.detach();
- this.searchEmbeddedView.unload();
- this.fileEmbeddedView?.unload();
- contentEl.empty();
- }
+ this.searchLeaf.detach();
+ this.fileLeaf?.detach();
+ this.searchEmbeddedView.unload();
+ this.fileEmbeddedView?.unload();
+ contentEl.empty();
+ }
- initInstructions(instructionsEl: HTMLElement) {
- const navigateInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-navigate"});
- const collapseInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-collapse"});
- const enterInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-enter"});
- const altEnterInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-alt-enter"});
+ initInstructions(instructionsEl: HTMLElement) {
+ const navigateInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-navigate"});
+ const collapseInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-collapse"});
+ const enterInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-enter"});
+ const altEnterInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-alt-enter"});
- const tabInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-tab"});
- const switchInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-switch"});
+ const tabInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-tab"});
+ const switchInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-switch"});
- const navigateIconEl = navigateInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
- const navigateTextEl = navigateInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
- navigateIconEl.setText("↑↓");
- navigateTextEl.setText("Navigate");
+ const navigateIconEl = navigateInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
+ const navigateTextEl = navigateInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
+ navigateIconEl.setText("↑↓");
+ navigateTextEl.setText("Navigate");
- const collapseIconEl = collapseInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
- const collapseTextEl = collapseInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
- collapseIconEl.setText("Shift+↑↓");
- collapseTextEl.setText("Collapse/Expand");
+ const collapseIconEl = collapseInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
+ const collapseTextEl = collapseInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
+ collapseIconEl.setText("Shift+↑↓");
+ collapseTextEl.setText("Collapse/Expand");
- const enterIconEl = enterInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
- const enterTextEl = enterInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
- enterIconEl.setText("↵");
- enterTextEl.setText("Open in background");
+ const enterIconEl = enterInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
+ const enterTextEl = enterInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
+ enterIconEl.setText("↵");
+ enterTextEl.setText("Open in background");
- const altEnterIconEl = altEnterInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
- const altEnterTextEl = altEnterInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
- altEnterIconEl.setText("Alt+↵");
- altEnterTextEl.setText("Open File and Close");
+ const altEnterIconEl = altEnterInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
+ const altEnterTextEl = altEnterInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
+ altEnterIconEl.setText("Alt+↵");
+ altEnterTextEl.setText("Open File and Close");
- const tabIconEl = tabInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
- const tabTextEl = tabInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
- tabIconEl.setText("Tab/Shift+Tab");
- tabTextEl.setText("Preview/Close Preview");
+ const tabIconEl = tabInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
+ const tabTextEl = tabInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
+ tabIconEl.setText("Tab/Shift+Tab");
+ tabTextEl.setText("Preview/Close Preview");
- const switchIconEl = switchInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
- const switchTextEl = switchInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
- switchIconEl.setText("Ctrl+G");
- switchTextEl.setText("Switch Between Search and File View");
+ const switchIconEl = switchInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
+ const switchTextEl = switchInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
+ switchIconEl.setText("Ctrl+G");
+ switchTextEl.setText("Switch Between Search and File View");
- const clickInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-click"});
- const clickIconEl = clickInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
- const clickTextEl = clickInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
- clickIconEl.setText("Alt+Click");
- clickTextEl.setText("Close Modal While In File View");
- }
+ const clickInstructionsEl = instructionsEl.createDiv({cls: "float-search-modal-instructions-click"});
+ const clickIconEl = clickInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
+ const clickTextEl = clickInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
+ clickIconEl.setText("Alt+Click");
+ clickTextEl.setText("Close Modal While In File View");
+ }
- initCss(contentEl: HTMLElement, modalEl: HTMLElement, containerEl: HTMLElement) {
- contentEl.classList.add("float-search-modal-content");
- modalEl.classList.add("float-search-modal");
- containerEl.classList.add("float-search-modal-container");
- }
+ initCss(contentEl: HTMLElement, modalEl: HTMLElement, containerEl: HTMLElement) {
+ contentEl.classList.add("float-search-modal-content");
+ modalEl.classList.add("float-search-modal");
+ containerEl.classList.add("float-search-modal-container");
+ }
- async initSearchView(contentEl: HTMLElement) {
- const [createdLeaf, embeddedView] = spawnLeafView(this.plugin, contentEl);
- this.searchLeaf = createdLeaf;
- this.searchEmbeddedView = embeddedView;
+ async initSearchView(contentEl: HTMLElement) {
+ const [createdLeaf, embeddedView] = spawnLeafView(this.plugin, contentEl);
+ this.searchLeaf = createdLeaf;
+ this.searchEmbeddedView = embeddedView;
- this.searchLeaf.setPinned(true);
- await this.searchLeaf.setViewState({
- type: "search",
- });
+ this.searchLeaf.setPinned(true);
+ await this.searchLeaf.setViewState({
+ type: "search",
+ });
- setTimeout(async () => {
- await this.searchLeaf.view.setState(this.state, {
- history: false,
- });
- this.state?.current ? (this.searchLeaf.view as SearchView).searchComponent.inputEl.setSelectionRange(0, 0) : (this.searchLeaf.view as SearchView).searchComponent.inputEl.setSelectionRange(0, this.state?.query?.length);
- }, 0);
+ setTimeout(async () => {
+ await this.searchLeaf.view.setState(this.state, {
+ history: false,
+ });
+ this.state?.current ? (this.searchLeaf.view as SearchView).searchComponent.inputEl.setSelectionRange(0, 0) : (this.searchLeaf.view as SearchView).searchComponent.inputEl.setSelectionRange(0, this.state?.query?.length);
+ }, 0);
- return;
- }
+ return;
+ }
- initInput() {
- const inputEl = this.contentEl.getElementsByTagName("input")[0];
- inputEl.focus();
- inputEl.onkeydown = (e) => {
- const currentView = this.searchLeaf.view as SearchView;
- switch (e.key) {
- case "ArrowDown":
- if (e.shiftKey) {
- currentView.onKeyShowMoreAfter(e);
- if (currentView.dom.focusedItem) {
- if (currentView.dom.focusedItem.collapsible) {
- currentView.dom.focusedItem.setCollapse(false);
- }
- }
- break;
- } else {
- currentView.onKeyArrowDownInFocus(e);
- break;
- }
- case "ArrowUp":
- if (e.shiftKey) {
- currentView.onKeyShowMoreBefore(e);
- if (currentView.dom.focusedItem) {
- if (currentView.dom.focusedItem.collapseEl) {
- currentView.dom.focusedItem.setCollapse(true);
- }
- }
- break;
- } else {
- currentView.onKeyArrowUpInFocus(e);
- break;
- }
- case "ArrowLeft":
- currentView.onKeyArrowLeftInFocus(e);
- break;
- case "ArrowRight":
- currentView.onKeyArrowRightInFocus(e);
- break;
- case "Enter":
- currentView.onKeyEnterInFocus(e);
- if (e.altKey && currentView.dom.focusedItem) {
- this.close();
- }
- break;
- case "Tab":
- e.preventDefault();
- if (e.shiftKey) {
- if (this.fileLeaf) {
- this.fileLeaf?.detach();
- this.fileLeaf = undefined;
- this.fileEmbeddedView?.unload();
- this.modalEl.toggleClass("float-search-width", false);
- this.fileEl.detach();
+ initInput() {
+ const inputEl = this.contentEl.getElementsByTagName("input")[0];
+ inputEl.focus();
+ inputEl.onkeydown = (e) => {
+ const currentView = this.searchLeaf.view as SearchView;
+ switch (e.key) {
+ case "ArrowDown":
+ if (e.shiftKey) {
+ currentView.onKeyShowMoreAfter(e);
+ if (currentView.dom.focusedItem) {
+ if (currentView.dom.focusedItem.collapsible) {
+ currentView.dom.focusedItem.setCollapse(false);
+ }
+ }
+ break;
+ } else {
+ currentView.onKeyArrowDownInFocus(e);
+ break;
+ }
+ case "ArrowUp":
+ if (e.shiftKey) {
+ currentView.onKeyShowMoreBefore(e);
+ if (currentView.dom.focusedItem) {
+ if (currentView.dom.focusedItem.collapseEl) {
+ currentView.dom.focusedItem.setCollapse(true);
+ }
+ }
+ break;
+ } else {
+ currentView.onKeyArrowUpInFocus(e);
+ break;
+ }
+ case "ArrowLeft":
+ currentView.onKeyArrowLeftInFocus(e);
+ break;
+ case "ArrowRight":
+ currentView.onKeyArrowRightInFocus(e);
+ break;
+ case "Enter":
+ currentView.onKeyEnterInFocus(e);
+ if (e.altKey && currentView.dom.focusedItem) {
+ this.close();
+ }
+ break;
+ case "Tab":
+ e.preventDefault();
+ if (e.shiftKey) {
+ if (this.fileLeaf) {
+ this.fileLeaf?.detach();
+ this.fileLeaf = undefined;
+ this.fileEmbeddedView?.unload();
+ this.modalEl.toggleClass("float-search-width", false);
+ this.fileEl.detach();
- break;
- }
- }
+ break;
+ }
+ }
- if (currentView.dom.focusedItem) {
- const item = currentView.dom.focusedItem;
- const file = item.parent.file instanceof TFile ? item.parent.file : item.file;
+ if (currentView.dom.focusedItem) {
+ const item = currentView.dom.focusedItem;
+ const file = item.parent.file instanceof TFile ? item.parent.file : item.file;
- item.parent.file instanceof TFile ? this.initFileView(file, {
- match: {
- content: item.content,
- matches: item.matches
- }
- }) : this.initFileView(file, undefined);
- }
- break;
- case "e":
- if (e.ctrlKey) {
- e.preventDefault();
- if (this.fileLeaf) {
- const estate = this.fileLeaf.getViewState();
- estate.state.mode = "preview" === estate.state.mode ? "source" : "preview";
- this.fileLeaf.setViewState(estate, {
- focus: !0
- });
- setTimeout(() => {
- (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
- }, 0);
- }
- }
- break;
- case "g":
- if (this.fileLeaf && e.ctrlKey) {
- e.preventDefault();
- this.plugin.app.workspace.setActiveLeaf(this.fileLeaf, {
- focus: true,
- });
- }
- break;
- case "C":
- if (e.ctrlKey && e.shiftKey) {
- e.preventDefault();
- const text = currentView.dom.focusedItem.el.innerText;
- navigator.clipboard.writeText(text);
- }
- break;
+ item.parent.file instanceof TFile ? this.initFileView(file, {
+ match: {
+ content: item.content,
+ matches: item.matches
+ }
+ }) : this.initFileView(file, undefined);
+ }
+ break;
+ case "e":
+ if (e.ctrlKey) {
+ e.preventDefault();
+ if (this.fileLeaf) {
+ const estate = this.fileLeaf.getViewState();
+ estate.state.mode = "preview" === estate.state.mode ? "source" : "preview";
+ this.fileLeaf.setViewState(estate, {
+ focus: !0
+ });
+ setTimeout(() => {
+ (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
+ }, 0);
+ }
+ }
+ break;
+ case "g":
+ if (this.fileLeaf && e.ctrlKey) {
+ e.preventDefault();
+ this.plugin.app.workspace.setActiveLeaf(this.fileLeaf, {
+ focus: true,
+ });
+ }
+ break;
+ case "C":
+ if (e.ctrlKey && e.shiftKey) {
+ e.preventDefault();
+ const text = currentView.dom.focusedItem.el.innerText;
+ navigator.clipboard.writeText(text);
+ }
+ break;
- }
- }
- }
+ }
+ };
+ }
- initContent() {
- const {contentEl} = this;
- contentEl.onclick = (e) => {
- const resultElement = contentEl.getElementsByClassName('search-results-children')[0];
- if (resultElement.children.length < 2) {
- return;
- }
+ initContent() {
+ const {contentEl} = this;
+ contentEl.onclick = (e) => {
+ const resultElement = contentEl.getElementsByClassName('search-results-children')[0];
+ if (resultElement.children.length < 2) {
+ return;
+ }
- let targetElement = e.target as HTMLElement | null;
+ let targetElement = e.target as HTMLElement | null;
- if (e.altKey || !this.fileLeaf) {
- while (targetElement) {
- if (targetElement.classList.contains('tree-item-icon')) {
- break;
- }
- if (targetElement.classList.contains('tree-item')) {
- this.close();
- break;
- }
- targetElement = targetElement.parentElement;
- }
- return;
- }
+ if (e.altKey || !this.fileLeaf) {
+ while (targetElement) {
+ if (targetElement.classList.contains('tree-item-icon')) {
+ break;
+ }
+ if (targetElement.classList.contains('tree-item')) {
+ this.close();
+ break;
+ }
+ targetElement = targetElement.parentElement;
+ }
+ return;
+ }
- if (this.fileLeaf) {
- const currentView = this.searchLeaf.view as SearchView;
+ if (this.fileLeaf) {
+ const currentView = this.searchLeaf.view as SearchView;
- if ((this.searchCtnEl as Node).contains(targetElement as Node)) {
- while (targetElement) {
- if (targetElement.classList.contains('tree-item')) {
- break;
- }
- targetElement = targetElement.parentElement;
- }
- if (!targetElement) return;
+ if ((this.searchCtnEl as Node).contains(targetElement as Node)) {
+ while (targetElement) {
+ if (targetElement.classList.contains('tree-item')) {
+ break;
+ }
+ targetElement = targetElement.parentElement;
+ }
+ if (!targetElement) return;
- const fileInnerEl = targetElement?.getElementsByClassName("tree-item-inner")[0] as HTMLElement;
- const innerText = fileInnerEl.innerText;
- const file = this.plugin.app.metadataCache.getFirstLinkpathDest(innerText, "");
+ const fileInnerEl = targetElement?.getElementsByClassName("tree-item-inner")[0] as HTMLElement;
+ const innerText = fileInnerEl.innerText;
+ const file = this.plugin.app.metadataCache.getFirstLinkpathDest(innerText, "");
- if (file) {
- const item = currentView.dom.resultDomLookup.get(file);
- currentView.dom.setFocusedItem(item);
- this.initFileView(file, undefined);
- (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
- }
- }
+ if (file) {
+ const item = currentView.dom.resultDomLookup.get(file);
+ currentView.dom.setFocusedItem(item);
+ this.initFileView(file, undefined);
+ (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
+ }
+ }
- return;
- }
- }
- }
+ return;
+ }
+ };
+ }
- async initFileView(file: TFile, state: any) {
- if (this.fileLeaf) {
- await this.fileLeaf.openFile(file, {
- active: false,
- eState: state
- });
+ async initFileView(file: TFile, state: any) {
+ if (this.fileLeaf) {
+ await this.fileLeaf.openFile(file, {
+ active: false,
+ eState: state
+ });
- if (this.fileState?.match?.matches[0] === state?.match?.matches[0] && state && this.fileState) {
- setTimeout(() => {
- if (this.fileLeaf) {
- this.plugin.app.workspace.setActiveLeaf(this.fileLeaf, {
- focus: true,
- });
- }
- }, 0);
- } else {
- this.fileState = state;
- setTimeout(() => {
- (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
- }, 0);
- }
+ if (this.fileState?.match?.matches[0] === state?.match?.matches[0] && state && this.fileState) {
+ setTimeout(() => {
+ if (this.fileLeaf) {
+ this.plugin.app.workspace.setActiveLeaf(this.fileLeaf, {
+ focus: true,
+ });
+ }
+ }, 0);
+ } else {
+ this.fileState = state;
+ setTimeout(() => {
+ (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
+ }, 0);
+ }
- return;
- }
+ return;
+ }
- const {contentEl} = this;
- this.fileEl = contentEl.createDiv({cls: "float-search-modal-file-ctn"});
- this.modalEl.toggleClass("float-search-width", true);
- this.fileEl.onkeydown = (e) => {
- if (e.ctrlKey && e.key === "g") {
- e.preventDefault();
- e.stopPropagation();
+ const {contentEl} = this;
+ this.fileEl = contentEl.createDiv({cls: "float-search-modal-file-ctn"});
+ this.modalEl.toggleClass("float-search-width", true);
+ this.fileEl.onkeydown = (e) => {
+ if (e.ctrlKey && e.key === "g") {
+ e.preventDefault();
+ e.stopPropagation();
- (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
- }
+ (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
+ }
- if (e.key === "Tab" && e.ctrlKey) {
- e.preventDefault();
- e.stopPropagation();
+ if (e.key === "Tab" && e.ctrlKey) {
+ e.preventDefault();
+ e.stopPropagation();
- (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
- }
- }
+ (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
+ }
+ };
- if (!this.fileEl) return;
+ if (!this.fileEl) return;
- const [createdLeaf, embeddedView] = spawnLeafView(this.plugin, this.fileEl);
- this.fileLeaf = createdLeaf;
- this.fileEmbeddedView = embeddedView;
+ const [createdLeaf, embeddedView] = spawnLeafView(this.plugin, this.fileEl);
+ this.fileLeaf = createdLeaf;
+ this.fileEmbeddedView = embeddedView;
- this.fileLeaf.setPinned(true);
- await this.fileLeaf.openFile(file, {
- active: false,
- eState: state
- });
- this.fileState = state;
+ this.fileLeaf.setPinned(true);
+ await this.fileLeaf.openFile(file, {
+ active: false,
+ eState: state
+ });
+ this.fileState = state;
- (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
+ (this.searchLeaf.view as SearchView).searchComponent.inputEl.focus();
- }
+ }
}
diff --git a/versions.json b/versions.json
index 259362b..09ed13a 100644
--- a/versions.json
+++ b/versions.json
@@ -22,5 +22,6 @@
"3.4.2": "0.15.0",
"3.4.3": "0.15.0",
"3.4.4": "0.15.0",
- "3.4.5": "0.15.0"
+ "3.4.5": "0.15.0",
+ "3.4.6": "0.15.0"
}
\ No newline at end of file
From 920801bc2541baa65ae6e59bcf1b918508b4edf3 Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Wed, 17 Jan 2024 13:51:14 +0800
Subject: [PATCH 02/12] fix: cannot show more context
---
.github/workflows/release.yml | 2 --
src/floatSearchIndex.ts | 4 ++++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 523d81d..3a3a67c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,8 +7,6 @@ on:
env:
PLUGIN_NAME: obsidian-float-search
-permissions: write-all
-
jobs:
build:
runs-on: ubuntu-latest
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index 46b09c1..fa8fbde 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -811,10 +811,14 @@ class FloatSearchModal extends Modal {
if (targetElement.classList.contains('tree-item-icon')) {
break;
}
+ if (targetElement.classList.contains('search-result-hover-button')) {
+ break;
+ }
if (targetElement.classList.contains('tree-item')) {
this.close();
break;
}
+
targetElement = targetElement.parentElement;
}
return;
From 2ba8f05d669d8a6a91bfbccd949017e4eb5d2e57 Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Wed, 17 Jan 2024 14:00:10 +0800
Subject: [PATCH 03/12] fix: query issue
---
src/floatSearchIndex.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index fa8fbde..f24e636 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -415,7 +415,7 @@ export default class FloatSearchPlugin extends Plugin {
...this.state,
query: path.query,
current: false
- }, true, true));
+ }, true, false));
}
private createCommand(options: {
From dc5d25f2b7c9231e1812b7abd27a421f370a3cf1 Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Wed, 17 Jan 2024 14:14:07 +0800
Subject: [PATCH 04/12] fix: cannot close via hotkey
---
src/floatSearchIndex.ts | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index f24e636..e25bf1f 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -81,6 +81,8 @@ const initSearchViewWithLeaf = async (app: App, type: PaneType | 'sidebar', stat
state: state
});
+ console.log(leaf.view);
+
setTimeout(() => {
const inputEl = leaf.containerEl.getElementsByTagName("input")[0];
inputEl.focus();
@@ -371,6 +373,10 @@ export default class FloatSearchPlugin extends Plugin {
onOpen(old) {
return function () {
old.call(this);
+ (this.scope as Scope).register(['Mod'], 'w', () => {
+ this.leaf?.detach();
+ });
+
const viewSwitchEl = createDiv({cls: "float-search-view-switch"});
const targetEl = this.filterSectionToggleEl;
const viewSwitchButton = new ExtraButtonComponent(viewSwitchEl);
@@ -482,6 +488,7 @@ export default class FloatSearchPlugin extends Plugin {
const existingLeaf = this.app.workspace.getLeavesOfType("search");
switch (type) {
case "window":
+ // @ts-ignore
const isExistingWindowLeaf = existingLeaf.find((leaf) => leaf.parentSplit.parent.type === "window");
if (isExistingWindowLeaf) {
this.app.workspace.revealLeaf(isExistingWindowLeaf);
@@ -491,7 +498,9 @@ export default class FloatSearchPlugin extends Plugin {
break;
case "tab":
case "split":
+ // @ts-ignore
const isExistingLeaf = existingLeaf.find((leaf) => !leaf.parentSplit.parent.side);
+ console.log(isExistingLeaf);
if (isExistingLeaf) {
this.app.workspace.revealLeaf(isExistingLeaf);
return;
From 4467c23332a82fc97a9ed201b9825a980a18b180 Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Sun, 21 Jan 2024 15:50:53 +0800
Subject: [PATCH 05/12] fix: cannot focus on search bar after init
---
manifest.json | 2 +-
package.json | 2 +-
src/floatSearchIndex.ts | 7 +++++--
versions.json | 3 ++-
4 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/manifest.json b/manifest.json
index 885816e..44130be 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "float-search",
"name": "Floating Search",
- "version": "3.4.6",
+ "version": "3.4.7",
"minAppVersion": "0.15.0",
"description": "You can use search view in modal/leaf/popout window now.",
"author": "Boninall",
diff --git a/package.json b/package.json
index b4454a4..e3b6697 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "float-search",
- "version": "3.4.6",
+ "version": "3.4.7",
"description": "You can use search view in modal/leaf/popout window now.",
"main": "main.js",
"scripts": {
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index e25bf1f..5938653 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -81,8 +81,6 @@ const initSearchViewWithLeaf = async (app: App, type: PaneType | 'sidebar', stat
state: state
});
- console.log(leaf.view);
-
setTimeout(() => {
const inputEl = leaf.containerEl.getElementsByTagName("input")[0];
inputEl.focus();
@@ -503,6 +501,11 @@ export default class FloatSearchPlugin extends Plugin {
console.log(isExistingLeaf);
if (isExistingLeaf) {
this.app.workspace.revealLeaf(isExistingLeaf);
+ isExistingLeaf.setViewState({
+ type: "search",
+ active: true,
+ state: this.state
+ });
return;
}
await initSearchViewWithLeaf(this.app, type);
diff --git a/versions.json b/versions.json
index 09ed13a..82f58ce 100644
--- a/versions.json
+++ b/versions.json
@@ -23,5 +23,6 @@
"3.4.3": "0.15.0",
"3.4.4": "0.15.0",
"3.4.5": "0.15.0",
- "3.4.6": "0.15.0"
+ "3.4.6": "0.15.0",
+ "3.4.7": "0.15.0"
}
\ No newline at end of file
From 1e8f1c672d130d5fcd67aa0736b67d11d9b2b016 Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Tue, 23 Jan 2024 16:46:03 +0800
Subject: [PATCH 06/12] fix: cannot save settings
---
src/floatSearchIndex.ts | 80 +--
src/leafView.ts | 1045 ++++++++++++++++++++-------------------
2 files changed, 565 insertions(+), 560 deletions(-)
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index 5938653..5137a77 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -1,14 +1,22 @@
import {
addIcon,
App,
- Editor, ExtraButtonComponent,
- Menu, MenuItem,
- Modal, OpenViewState, PaneType,
- Plugin, Scope, SearchView, setIcon,
+ debounce,
+ Editor,
+ ExtraButtonComponent,
+ Menu,
+ MenuItem,
+ Modal,
+ OpenViewState,
+ PaneType,
+ Plugin,
+ Scope,
+ SearchView,
TAbstractFile,
- TFile, ViewStateResult,
+ TFile,
Workspace,
- WorkspaceContainer, WorkspaceItem,
+ WorkspaceContainer,
+ WorkspaceItem,
WorkspaceLeaf
} from 'obsidian';
import { EmbeddedView, isEmebeddedLeaf, spawnLeafView } from "./leafView";
@@ -92,35 +100,27 @@ export default class FloatSearchPlugin extends Plugin {
private state: any;
private modal: FloatSearchModal;
- private applyStateDebounceTimer = 0;
- private applySettingsDebounceTimer = 0;
+ public applySettingsUpdate = debounce(async () => {
+ await this.saveSettings();
+ }, 1000);
-
- public applySettingsUpdate() {
- clearTimeout(this.applySettingsDebounceTimer);
- this.applySettingsDebounceTimer = window.setTimeout(async () => {
- await this.saveSettings();
- }, 1000);
- }
-
- private applyStateUpdate() {
- this.applyStateDebounceTimer = window.setTimeout(() => {
- clearTimeout(this.applyStateDebounceTimer);
- this.state = {
- ...this.state,
- query: "",
- };
- }, 30000);
- }
+ private applyStateUpdate = debounce(() => {
+ this.state = {
+ ...this.state,
+ query: "",
+ };
+ }, 30000);
async onload() {
await this.loadSettings();
this.initState();
this.registerIcons();
- this.patchWorkspace();
- this.patchWorkspaceLeaf();
- this.patchSearchView();
+ this.app.workspace.onLayoutReady(() => {
+ this.patchWorkspace();
+ this.patchWorkspaceLeaf();
+ this.patchSearchView();
+ });
this.registerObsidianURIHandler();
this.registerObsidianCommands();
@@ -158,6 +158,7 @@ export default class FloatSearchPlugin extends Plugin {
patchWorkspace() {
let layoutChanging = false;
+ const self = this;
const uninstaller = around(Workspace.prototype, {
getLeaf: (next) =>
function (...args) {
@@ -168,7 +169,7 @@ export default class FloatSearchPlugin extends Plugin {
if (activeLeaf.view.getViewType() === "markdown") {
return activeLeaf;
}
- const newLeaf = app.workspace.getUnpinnedLeaf();
+ const newLeaf = self.app.workspace.getUnpinnedLeaf();
if (newLeaf) {
this.setActiveLeaf(newLeaf);
}
@@ -202,7 +203,7 @@ export default class FloatSearchPlugin extends Plugin {
if (layoutChanging) return false; // Don't let HEs close during workspace change
// 0.14.x doesn't have WorkspaceContainer; this can just be an instanceof check once 15.x is mandatory:
- if (parent === app.workspace.rootSplit || (WorkspaceContainer && parent instanceof WorkspaceContainer)) {
+ if (parent === self.app.workspace.rootSplit || (WorkspaceContainer && parent instanceof WorkspaceContainer)) {
for (const popover of EmbeddedView.popoversForWindow((parent as WorkspaceContainer).win)) {
// Use old API here for compat w/0.14.x
if (old.call(this, cb, popover.rootSplit)) return true;
@@ -306,6 +307,7 @@ export default class FloatSearchPlugin extends Plugin {
patchSearchView() {
const updateCurrentState = (state: searchState) => {
this.state = state;
+ console.log(state);
this.settings.searchViewState = state as searchState;
this.applySettingsUpdate();
};
@@ -361,6 +363,7 @@ export default class FloatSearchPlugin extends Plugin {
const patchSearch = () => {
const searchView = this.app.workspace.getLeavesOfType("search")[0]?.view as any;
+ const self = this;
if (!searchView) return false;
@@ -388,14 +391,15 @@ export default class FloatSearchPlugin extends Plugin {
targetEl.parentElement.insertBefore(viewSwitchEl, targetEl);
};
},
- startSearch(old) {
- return function () {
- old.call(this);
- const viewState = this.getState();
- updateCurrentState({
- ...viewState,
- query: this.searchComponent.getValue(),
- });
+ setState(old) {
+ return async function (state: any, result: any) {
+ old.call(this, state, result);
+ if (self.app.workspace.layoutReady) {
+ updateCurrentState({
+ ...state,
+ query: this.searchComponent.getValue(),
+ });
+ }
};
}
})
diff --git a/src/leafView.ts b/src/leafView.ts
index 3c22146..ea5301a 100644
--- a/src/leafView.ts
+++ b/src/leafView.ts
@@ -3,551 +3,552 @@
// Please rememeber if you want to use this file, you should patch the types-obsidian.d.ts file
// And also monkey around the Obsidian original method.
import {
- Component,
+ Component,
EphemeralState,
- HoverPopover,
- MarkdownEditView,
- parseLinktext,
- PopoverState,
- requireApiVersion,
- resolveSubpath,
- TFile,
- View,
- Workspace,
- WorkspaceLeaf,
- WorkspaceSplit,
- WorkspaceTabs,
+ HoverPopover,
+ MarkdownEditView,
+ parseLinktext,
+ PopoverState,
+ requireApiVersion,
+ resolveSubpath,
+ TFile,
+ View,
+ Workspace,
+ WorkspaceLeaf,
+ WorkspaceSplit,
+ WorkspaceTabs,
} from "obsidian";
import type { OpenViewState } from "src/types/types-obsidian";
import type FloatSearchPlugin from "src/floatSearchIndex";
export interface EmbeddedViewParent {
- hoverPopover: EmbeddedView | null;
- containerEl?: HTMLElement;
- view?: View;
- dom?: HTMLElement;
+ hoverPopover: EmbeddedView | null;
+ containerEl?: HTMLElement;
+ view?: View;
+ dom?: HTMLElement;
}
+
const popovers = new WeakMap();
-type ConstructableWorkspaceSplit = new (ws: Workspace, dir: "horizontal"|"vertical") => WorkspaceSplit;
+type ConstructableWorkspaceSplit = new (ws: Workspace, dir: "horizontal" | "vertical") => WorkspaceSplit;
export function isEmebeddedLeaf(leaf: WorkspaceLeaf) {
- // Work around missing enhance.js API by checking match condition instead of looking up parent
- return leaf.containerEl.matches(".fs-block.fs-leaf-view .workspace-leaf");
+ // Work around missing enhance.js API by checking match condition instead of looking up parent
+ return leaf.containerEl.matches(".fs-block.fs-leaf-view .workspace-leaf");
}
function genId(size: number): string {
- const chars = [];
- for (let n = 0; n < size; n++) chars.push(((16 * Math.random()) | 0).toString(16));
- return chars.join("");
+ const chars = [];
+ for (let n = 0; n < size; n++) chars.push(((16 * Math.random()) | 0).toString(16));
+ return chars.join("");
}
function nosuper(base: new (...args: unknown[]) => T): new () => T {
- const derived = function () {
- return Object.setPrototypeOf(new Component, new.target.prototype);
- };
- derived.prototype = base.prototype;
- return Object.setPrototypeOf(derived, base);
+ const derived = function () {
+ return Object.setPrototypeOf(new Component, new.target.prototype);
+ };
+ derived.prototype = base.prototype;
+ return Object.setPrototypeOf(derived, base);
}
export const spawnLeafView = (plugin: FloatSearchPlugin, initiatingEl?: HTMLElement, leaf?: WorkspaceLeaf, onShowCallback?: () => unknown): [WorkspaceLeaf, EmbeddedView] => {
- // When Obsidian doesn't set any leaf active, use leaf instead.
- let parent = app.workspace.activeLeaf as unknown as EmbeddedViewParent;
- if (!parent) parent = leaf as unknown as EmbeddedViewParent;
+ // When Obsidian doesn't set any leaf active, use leaf instead.
+ let parent = app.workspace.activeLeaf as unknown as EmbeddedViewParent;
+ if (!parent) parent = leaf as unknown as EmbeddedViewParent;
- if (!initiatingEl) initiatingEl = parent?.containerEl;
+ if (!initiatingEl) initiatingEl = parent?.containerEl;
- const hoverPopover = new EmbeddedView(parent, initiatingEl!, plugin, undefined, onShowCallback);
- return [hoverPopover.attachLeaf(), hoverPopover];
+ const hoverPopover = new EmbeddedView(parent, initiatingEl!, plugin, undefined, onShowCallback);
+ return [hoverPopover.attachLeaf(), hoverPopover];
-}
+};
export class EmbeddedView extends nosuper(HoverPopover) {
- onTarget: boolean;
- setActive: (event: MouseEvent) => void;
-
- lockedOut: boolean;
- abortController? = this.addChild(new Component());
- detaching = false;
- opening = false;
-
- rootSplit: WorkspaceSplit = new (WorkspaceSplit as ConstructableWorkspaceSplit)(window.app.workspace, "vertical");
-
- isPinned = true;
-
- titleEl: HTMLElement;
- containerEl: HTMLElement;
-
- // It is currently not useful.
- // leafInHoverEl: WorkspaceLeaf;
-
- oldPopover = this.parent?.hoverPopover;
- document: Document = this.targetEl?.ownerDocument ?? window.activeDocument ?? window.document;
-
- id = genId(8);
- bounce?: NodeJS.Timeout;
- boundOnZoomOut: () => void;
-
- originalPath: string; // these are kept to avoid adopting targets w/a different link
- originalLinkText: string;
- static activePopover?: EmbeddedView;
-
- static activeWindows() {
- const windows: Window[] = [window];
- const { floatingSplit } = app.workspace;
- if (floatingSplit) {
- for (const split of floatingSplit.children) {
- if (split.win) windows.push(split.win);
- }
- }
- return windows;
- }
-
- static containerForDocument(doc: Document) {
- if (doc !== document && app.workspace.floatingSplit)
- for (const container of app.workspace.floatingSplit.children) {
- if (container.doc === doc) return container;
- }
- return app.workspace.rootSplit;
- }
-
- static activePopovers() {
- return this.activeWindows().flatMap(this.popoversForWindow);
- }
-
- static popoversForWindow(win?: Window) {
- return (Array.prototype.slice.call(win?.document?.body.querySelectorAll(".fs-leaf-view") ?? []) as HTMLElement[])
- .map(el => popovers.get(el)!)
- .filter(he => he);
- }
-
- static forLeaf(leaf: WorkspaceLeaf | undefined) {
- // leaf can be null such as when right clicking on an internal link
- const el = leaf && document.body.matchParent.call(leaf.containerEl, ".fs-leaf-view"); // work around matchParent race condition
- return el ? popovers.get(el) : undefined;
- }
-
- static iteratePopoverLeaves(ws: Workspace, cb: (leaf: WorkspaceLeaf) => boolean | void) {
- for (const popover of this.activePopovers()) {
- if (popover.rootSplit && ws.iterateLeaves(cb, popover.rootSplit)) return true;
- }
- return false;
- }
-
- hoverEl: HTMLElement = this.document.defaultView!.createDiv({
- cls: "fs-block fs-leaf-view",
- attr: { id: "fs-" + this.id },
- });
-
- constructor(
- parent: EmbeddedViewParent,
- public targetEl: HTMLElement,
- public plugin: FloatSearchPlugin,
- waitTime?: number,
- public onShowCallback?: () => unknown,
- ) {
- //
- super();
-
- if (waitTime === undefined) {
- waitTime = 300;
- }
- this.onTarget = true;
-
- this.parent = parent;
- this.waitTime = waitTime;
- this.state = PopoverState.Showing;
- const { hoverEl } = this;
-
- this.abortController!.load();
- this.show();
- this.onShow();
-
- this.setActive = this._setActive.bind(this);
- // if (hoverEl) {
- // hoverEl.addEventListener("mousedown", this.setActive);
- // }
- // custom logic begin
- popovers.set(this.hoverEl, this);
- this.hoverEl.addClass("fs-block");
- this.containerEl = this.hoverEl.createDiv("fs-content");
- this.buildWindowControls();
- this.setInitialDimensions();
- }
-
- _setActive(evt: MouseEvent) {
- evt.preventDefault();
- evt.stopPropagation();
- this.plugin.app.workspace.setActiveLeaf(this.leaves()[0], {focus: true})
- }
-
- getDefaultMode() {
- // return this.parent?.view?.getMode ? this.parent.view.getMode() : "source";
- return "source";
- }
-
- updateLeaves() {
- if (this.onTarget && this.targetEl && !this.document.contains(this.targetEl)) {
- this.onTarget = false;
- this.transition();
- }
- let leafCount = 0;
- this.plugin.app.workspace.iterateLeaves(leaf => {
- leafCount++;
- }, this.rootSplit);
-
- if (leafCount === 0) {
- this.hide(); // close if we have no leaves
- }
- this.hoverEl.setAttribute("data-leaf-count", leafCount.toString());
- }
-
- leaves() {
- const leaves: WorkspaceLeaf[] = [];
- this.plugin.app.workspace.iterateLeaves(leaf => {
- leaves.push(leaf);
- }, this.rootSplit);
- return leaves;
- }
-
- setInitialDimensions() {
-
- this.hoverEl.style.height = 'auto';
- this.hoverEl.style.width = "100%";
- }
-
- transition() {
- if (this.shouldShow()) {
- if (this.state === PopoverState.Hiding) {
- this.state = PopoverState.Shown;
- clearTimeout(this.timer);
- }
- } else {
- if (this.state === PopoverState.Showing) {
- this.hide();
- } else {
- if (this.state === PopoverState.Shown) {
- this.state = PopoverState.Hiding;
- this.timer = window.setTimeout(() => {
- if (this.shouldShow()) {
- this.transition();
- } else {
- this.hide();
- }
- }, this.waitTime);
- }
- }
- }
- }
-
-
- buildWindowControls() {
- this.titleEl = this.document.defaultView!.createDiv("popover-titlebar");
- this.titleEl.createDiv("popover-title");
-
- this.containerEl.prepend(this.titleEl);
-
- }
-
- attachLeaf(): WorkspaceLeaf {
- this.rootSplit.getRoot = () => this.plugin.app.workspace[this.document === document ? "rootSplit" : "floatingSplit"]!;
- this.rootSplit.getContainer = () => EmbeddedView.containerForDocument(this.document);
-
- this.titleEl.insertAdjacentElement("afterend", this.rootSplit.containerEl);
- const leaf = this.plugin.app.workspace.createLeafInParent(this.rootSplit, 0);
-
- this.updateLeaves();
- return leaf;
- }
-
- onload(): void {
- super.onload();
- this.registerEvent(this.plugin.app.workspace.on("layout-change", this.updateLeaves, this));
- this.registerEvent(app.workspace.on("layout-change", () => {
- // Ensure that top-level items in a popover are not tabbed
- // @ts-ignore
- this.rootSplit.children.forEach((item: any, index: any) => {
- if (item instanceof WorkspaceTabs) {
- this.rootSplit.replaceChild(index, item.children[0]);
- }
- })
- }));
- }
-
- onShow() {
- // Once we've been open for closeDelay, use the closeDelay as a hiding timeout
- const closeDelay = 600;
- setTimeout(() => (this.waitTime = closeDelay), closeDelay);
-
- this.oldPopover?.hide();
- this.oldPopover = null;
-
- this.hoverEl.toggleClass("is-new", true);
-
- this.document.body.addEventListener(
- "click",
- () => {
- this.hoverEl.toggleClass("is-new", false);
- },
- { once: true, capture: true },
- );
-
- if (this.parent) {
- this.parent.hoverPopover = this;
- }
-
- // Remove original view header;
- const viewHeaderEl = this.hoverEl.querySelector(".view-header");
- viewHeaderEl?.remove();
-
- const sizer = this.hoverEl.querySelector(".workspace-leaf");
- if(sizer) this.hoverEl.appendChild(sizer);
-
- // Remove original inline tilte;
- const inlineTitle = this.hoverEl.querySelector(".inline-title");
- if(inlineTitle) inlineTitle.remove();
-
- this.onShowCallback?.();
- this.onShowCallback = undefined; // only call it once
- }
-
- detect(el: HTMLElement) {
- // TODO: may not be needed? the mouseover/out handers handle most detection use cases
- const { targetEl } = this;
-
- if (targetEl) {
- this.onTarget = el === targetEl || targetEl.contains(el);
- }
- }
-
- shouldShow() {
- return this.shouldShowSelf() || this.shouldShowChild();
- }
-
- shouldShowChild(): boolean {
- return EmbeddedView.activePopovers().some(popover => {
- if (popover !== this && popover.targetEl && this.hoverEl.contains(popover.targetEl)) {
- return popover.shouldShow();
- }
- return false;
- });
- }
-
- shouldShowSelf() {
- // Don't let obsidian show() us if we've already started closing
- // return !this.detaching && (this.onTarget || this.onHover);
- return (
- !this.detaching &&
- !!(
- this.onTarget ||
- (this.state == PopoverState.Shown) ||
- this.document.querySelector(`body>.modal-container, body > #he${this.id} ~ .menu, body > #he${this.id} ~ .suggestion-container`)
- )
- );
- }
-
- show() {
- // native obsidian logic start
- // if (!this.targetEl || this.document.body.contains(this.targetEl)) {
- this.state = PopoverState.Shown;
- this.timer = 0;
-
- this.targetEl.appendChild(this.hoverEl);
- this.onShow();
- app.workspace.onLayoutChange();
-
- // initializingHoverPopovers.remove(this);
- // activeHoverPopovers.push(this);
- // initializePopoverChecker();
- this.load();
- // }
- // native obsidian logic end
-
- // if this is an image view, set the dimensions to the natural dimensions of the image
- // an interactjs reflow will be triggered to constrain the image to the viewport if it's
- // too large
- if (this.hoverEl.dataset.imgHeight && this.hoverEl.dataset.imgWidth) {
- this.hoverEl.style.height = parseFloat(this.hoverEl.dataset.imgHeight) + this.titleEl.offsetHeight + "px";
- this.hoverEl.style.width = parseFloat(this.hoverEl.dataset.imgWidth) + "px";
- }
- }
-
- onHide() {
- this.oldPopover = null;
- if (this.parent?.hoverPopover === this) {
- this.parent.hoverPopover = null;
- }
- }
-
- hide() {
- this.onTarget = false;
- this.detaching = true;
- // Once we reach this point, we're committed to closing
-
- // in case we didn't ever call show()
-
-
- // A timer might be pending to call show() for the first time, make sure
- // it doesn't bring us back up after we close
- if (this.timer) {
- clearTimeout(this.timer);
- this.timer = 0;
- }
-
- // Hide our HTML element immediately, even if our leaves might not be
- // detachable yet. This makes things more responsive and improves the
- // odds of not showing an empty popup that's just going to disappear
- // momentarily.
- this.hoverEl.hide();
-
- // If a file load is in progress, we need to wait until it's finished before
- // detaching leaves. Because we set .detaching, The in-progress openFile()
- // will call us again when it finishes.
- if (this.opening) return;
-
- // Leave this code here to observe the state of the leaves
- const leaves = this.leaves();
- if (leaves.length) {
- // Detach all leaves before we unload the popover and remove it from the DOM.
- // Each leaf.detach() will trigger layout-changed and the updateLeaves()
- // method will then call hide() again when the last one is gone.
- // leaves[0].detach();
- // leaves[0].detach();
- this.targetEl.empty();
- } else {
- this.parent = null;
- this.abortController?.unload();
- this.abortController = undefined;
- return this.nativeHide();
- }
- }
-
- nativeHide() {
- const { hoverEl, targetEl } = this;
- this.state = PopoverState.Hidden;
- hoverEl.detach();
-
- if (targetEl) {
- const parent = targetEl.matchParent(".fs-leaf-view");
- if (parent) popovers.get(parent)?.transition();
- }
-
- this.onHide();
- this.unload();
- }
-
- resolveLink(linkText: string, sourcePath: string): TFile | null {
- const link = parseLinktext(linkText);
- const tFile = link ? this.plugin.app.metadataCache.getFirstLinkpathDest(link.path, sourcePath) : null;
- return tFile;
- }
-
- async openLink(linkText: string, sourcePath: string, eState?: EphemeralState, createInLeaf?: WorkspaceLeaf) {
- let file = this.resolveLink(linkText, sourcePath);
- const link = parseLinktext(linkText);
- if (!file && createInLeaf) {
- const folder = this.plugin.app.fileManager.getNewFileParent(sourcePath);
- file = await this.plugin.app.fileManager.createNewMarkdownFile(folder, link.path);
- }
-
- if (!file) {
- // this.displayCreateFileAction(linkText, sourcePath, eState);
- return;
- }
- const { viewRegistry } = this.plugin.app;
- const viewType = viewRegistry.typeByExtension[file.extension];
- if (!viewType || !viewRegistry.viewByType[viewType]) {
- // this.displayOpenFileAction(file);
- return;
- }
-
- eState = Object.assign(this.buildEphemeralState(file, link), eState);
- const parentMode = this.getDefaultMode();
- const state = this.buildState(parentMode, eState);
- const leaf = await this.openFile(file, state, createInLeaf);
- const leafViewType = leaf?.view?.getViewType();
- // console.log(leaf);
- if (leafViewType === "image") {
- // TODO: temporary workaround to prevent image popover from disappearing immediately when using live preview
- if (
- this.parent?.hasOwnProperty("editorEl") &&
- (this.parent as unknown as MarkdownEditView).editorEl!.hasClass("is-live-preview")
- ) {
- this.waitTime = 3000;
- }
- const img = leaf!.view.contentEl.querySelector("img")!;
- this.hoverEl.dataset.imgHeight = String(img.naturalHeight);
- this.hoverEl.dataset.imgWidth = String(img.naturalWidth);
- this.hoverEl.dataset.imgRatio = String(img.naturalWidth / img.naturalHeight);
- } else if (leafViewType === "pdf") {
- this.hoverEl.style.height = "800px";
- this.hoverEl.style.width = "600px";
- }
- if (state.state?.mode === "source") {
- this.whenShown(() => {
- // Not sure why this is needed, but without it we get issue #186
- if (requireApiVersion("1.0"))(leaf?.view as any)?.editMode?.reinit?.();
- leaf?.view?.setEphemeralState(state.eState);
- });
- }
- }
-
- whenShown(callback: () => any) {
- // invoke callback once the popover is visible
- if (this.detaching) return;
- const existingCallback = this.onShowCallback;
- this.onShowCallback = () => {
- if (this.detaching) return;
- callback();
- if (typeof existingCallback === "function") existingCallback();
- };
- if (this.state === PopoverState.Shown) {
- this.onShowCallback();
- this.onShowCallback = undefined;
- }
- }
-
- async openFile(file: TFile, openState?: OpenViewState, useLeaf?: WorkspaceLeaf) {
- if (this.detaching) return;
- const leaf = useLeaf ?? this.attachLeaf();
- this.opening = true;
-
- try {
- await leaf.openFile(file, openState);
- } catch (e) {
- console.error(e);
- } finally {
- this.opening = false;
- if (this.detaching) this.hide();
- }
- this.plugin.app.workspace.setActiveLeaf(leaf);
-
- return leaf;
- }
-
- buildState(parentMode: string, eState?: EphemeralState) {
- return {
- active: false, // Don't let Obsidian force focus if we have autofocus off
- state: { mode: "source" }, // Don't set any state for the view, because this leaf is stayed on another view.
- eState: eState,
- };
- }
-
- buildEphemeralState(
- file: TFile,
- link?: {
- path: string;
- subpath: string;
- },
- ) {
- const cache = this.plugin.app.metadataCache.getFileCache(file);
- const subpath = cache ? resolveSubpath(cache, link?.subpath || "") : undefined;
- const eState: EphemeralState = { subpath: link?.subpath };
- if (subpath) {
- eState.line = subpath.start.line;
- eState.startLoc = subpath.start;
- eState.endLoc = subpath.end || undefined;
- }
- return eState;
- }
+ onTarget: boolean;
+ setActive: (event: MouseEvent) => void;
+
+ lockedOut: boolean;
+ abortController? = this.addChild(new Component());
+ detaching = false;
+ opening = false;
+
+ rootSplit: WorkspaceSplit = new (WorkspaceSplit as ConstructableWorkspaceSplit)(window.app.workspace, "vertical");
+
+ isPinned = true;
+
+ titleEl: HTMLElement;
+ containerEl: HTMLElement;
+
+ // It is currently not useful.
+ // leafInHoverEl: WorkspaceLeaf;
+
+ oldPopover = this.parent?.hoverPopover;
+ document: Document = this.targetEl?.ownerDocument ?? window.activeDocument ?? window.document;
+
+ id = genId(8);
+ bounce?: NodeJS.Timeout;
+ boundOnZoomOut: () => void;
+
+ originalPath: string; // these are kept to avoid adopting targets w/a different link
+ originalLinkText: string;
+ static activePopover?: EmbeddedView;
+
+ static activeWindows() {
+ const windows: Window[] = [window];
+ const {floatingSplit} = app.workspace;
+ if (floatingSplit) {
+ for (const split of floatingSplit.children) {
+ if (split.win) windows.push(split.win);
+ }
+ }
+ return windows;
+ }
+
+ static containerForDocument(doc: Document) {
+ if (doc !== document && app.workspace.floatingSplit)
+ for (const container of app.workspace.floatingSplit.children) {
+ if (container.doc === doc) return container;
+ }
+ return app.workspace.rootSplit;
+ }
+
+ static activePopovers() {
+ return this.activeWindows().flatMap(this.popoversForWindow);
+ }
+
+ static popoversForWindow(win?: Window) {
+ return (Array.prototype.slice.call(win?.document?.body.querySelectorAll(".fs-leaf-view") ?? []) as HTMLElement[])
+ .map(el => popovers.get(el)!)
+ .filter(he => he);
+ }
+
+ static forLeaf(leaf: WorkspaceLeaf | undefined) {
+ // leaf can be null such as when right clicking on an internal link
+ const el = leaf && document.body.matchParent.call(leaf.containerEl, ".fs-leaf-view"); // work around matchParent race condition
+ return el ? popovers.get(el) : undefined;
+ }
+
+ static iteratePopoverLeaves(ws: Workspace, cb: (leaf: WorkspaceLeaf) => boolean | void) {
+ for (const popover of this.activePopovers()) {
+ if (popover.rootSplit && ws.iterateLeaves(cb, popover.rootSplit)) return true;
+ }
+ return false;
+ }
+
+ hoverEl: HTMLElement = this.document.defaultView!.createDiv({
+ cls: "fs-block fs-leaf-view",
+ attr: {id: "fs-" + this.id},
+ });
+
+ constructor(
+ parent: EmbeddedViewParent,
+ public targetEl: HTMLElement,
+ public plugin: FloatSearchPlugin,
+ waitTime?: number,
+ public onShowCallback?: () => unknown,
+ ) {
+ //
+ super();
+
+ if (waitTime === undefined) {
+ waitTime = 300;
+ }
+ this.onTarget = true;
+
+ this.parent = parent;
+ this.waitTime = waitTime;
+ this.state = PopoverState.Showing;
+ const {hoverEl} = this;
+
+ this.abortController!.load();
+ this.show();
+ this.onShow();
+
+ this.setActive = this._setActive.bind(this);
+ // if (hoverEl) {
+ // hoverEl.addEventListener("mousedown", this.setActive);
+ // }
+ // custom logic begin
+ popovers.set(this.hoverEl, this);
+ this.hoverEl.addClass("fs-block");
+ this.containerEl = this.hoverEl.createDiv("fs-content");
+ this.buildWindowControls();
+ this.setInitialDimensions();
+ }
+
+ _setActive(evt: MouseEvent) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ this.plugin.app.workspace.setActiveLeaf(this.leaves()[0], {focus: true});
+ }
+
+ getDefaultMode() {
+ // return this.parent?.view?.getMode ? this.parent.view.getMode() : "source";
+ return "source";
+ }
+
+ updateLeaves() {
+ if (this.onTarget && this.targetEl && !this.document.contains(this.targetEl)) {
+ this.onTarget = false;
+ this.transition();
+ }
+ let leafCount = 0;
+ this.plugin.app.workspace.iterateLeaves(leaf => {
+ leafCount++;
+ }, this.rootSplit);
+
+ if (leafCount === 0) {
+ this.hide(); // close if we have no leaves
+ }
+ this.hoverEl.setAttribute("data-leaf-count", leafCount.toString());
+ }
+
+ leaves() {
+ const leaves: WorkspaceLeaf[] = [];
+ this.plugin.app.workspace.iterateLeaves(leaf => {
+ leaves.push(leaf);
+ }, this.rootSplit);
+ return leaves;
+ }
+
+ setInitialDimensions() {
+
+ this.hoverEl.style.height = 'auto';
+ this.hoverEl.style.width = "100%";
+ }
+
+ transition() {
+ if (this.shouldShow()) {
+ if (this.state === PopoverState.Hiding) {
+ this.state = PopoverState.Shown;
+ clearTimeout(this.timer);
+ }
+ } else {
+ if (this.state === PopoverState.Showing) {
+ this.hide();
+ } else {
+ if (this.state === PopoverState.Shown) {
+ this.state = PopoverState.Hiding;
+ this.timer = window.setTimeout(() => {
+ if (this.shouldShow()) {
+ this.transition();
+ } else {
+ this.hide();
+ }
+ }, this.waitTime);
+ }
+ }
+ }
+ }
+
+
+ buildWindowControls() {
+ this.titleEl = this.document.defaultView!.createDiv("popover-titlebar");
+ this.titleEl.createDiv("popover-title");
+
+ this.containerEl.prepend(this.titleEl);
+
+ }
+
+ attachLeaf(): WorkspaceLeaf {
+ this.rootSplit.getRoot = () => this.plugin.app.workspace[this.document === document ? "rootSplit" : "floatingSplit"]!;
+ this.rootSplit.getContainer = () => EmbeddedView.containerForDocument(this.document);
+
+ this.titleEl.insertAdjacentElement("afterend", this.rootSplit.containerEl);
+ const leaf = this.plugin.app.workspace.createLeafInParent(this.rootSplit, 0);
+
+ this.updateLeaves();
+ return leaf;
+ }
+
+ onload(): void {
+ super.onload();
+ this.registerEvent(this.plugin.app.workspace.on("layout-change", this.updateLeaves, this));
+ this.registerEvent(app.workspace.on("layout-change", () => {
+ // Ensure that top-level items in a popover are not tabbed
+ // @ts-ignore
+ this.rootSplit.children.forEach((item: any, index: any) => {
+ if (item instanceof WorkspaceTabs) {
+ this.rootSplit.replaceChild(index, item.children[0]);
+ }
+ });
+ }));
+ }
+
+ onShow() {
+ // Once we've been open for closeDelay, use the closeDelay as a hiding timeout
+ const closeDelay = 600;
+ setTimeout(() => (this.waitTime = closeDelay), closeDelay);
+
+ this.oldPopover?.hide();
+ this.oldPopover = null;
+
+ this.hoverEl.toggleClass("is-new", true);
+
+ this.document.body.addEventListener(
+ "click",
+ () => {
+ this.hoverEl.toggleClass("is-new", false);
+ },
+ {once: true, capture: true},
+ );
+
+ if (this.parent) {
+ this.parent.hoverPopover = this;
+ }
+
+ // Remove original view header;
+ const viewHeaderEl = this.hoverEl.querySelector(".view-header");
+ viewHeaderEl?.remove();
+
+ const sizer = this.hoverEl.querySelector(".workspace-leaf");
+ if (sizer) this.hoverEl.appendChild(sizer);
+
+ // Remove original inline tilte;
+ const inlineTitle = this.hoverEl.querySelector(".inline-title");
+ if (inlineTitle) inlineTitle.remove();
+
+ this.onShowCallback?.();
+ this.onShowCallback = undefined; // only call it once
+ }
+
+ detect(el: HTMLElement) {
+ // TODO: may not be needed? the mouseover/out handers handle most detection use cases
+ const {targetEl} = this;
+
+ if (targetEl) {
+ this.onTarget = el === targetEl || targetEl.contains(el);
+ }
+ }
+
+ shouldShow() {
+ return this.shouldShowSelf() || this.shouldShowChild();
+ }
+
+ shouldShowChild(): boolean {
+ return EmbeddedView.activePopovers().some(popover => {
+ if (popover !== this && popover.targetEl && this.hoverEl.contains(popover.targetEl)) {
+ return popover.shouldShow();
+ }
+ return false;
+ });
+ }
+
+ shouldShowSelf() {
+ // Don't let obsidian show() us if we've already started closing
+ // return !this.detaching && (this.onTarget || this.onHover);
+ return (
+ !this.detaching &&
+ !!(
+ this.onTarget ||
+ (this.state == PopoverState.Shown) ||
+ this.document.querySelector(`body>.modal-container, body > #he${this.id} ~ .menu, body > #he${this.id} ~ .suggestion-container`)
+ )
+ );
+ }
+
+ show() {
+ // native obsidian logic start
+ // if (!this.targetEl || this.document.body.contains(this.targetEl)) {
+ this.state = PopoverState.Shown;
+ this.timer = 0;
+
+ this.targetEl.appendChild(this.hoverEl);
+ this.onShow();
+ app.workspace.onLayoutChange();
+
+ // initializingHoverPopovers.remove(this);
+ // activeHoverPopovers.push(this);
+ // initializePopoverChecker();
+ this.load();
+ // }
+ // native obsidian logic end
+
+ // if this is an image view, set the dimensions to the natural dimensions of the image
+ // an interactjs reflow will be triggered to constrain the image to the viewport if it's
+ // too large
+ if (this.hoverEl.dataset.imgHeight && this.hoverEl.dataset.imgWidth) {
+ this.hoverEl.style.height = parseFloat(this.hoverEl.dataset.imgHeight) + this.titleEl.offsetHeight + "px";
+ this.hoverEl.style.width = parseFloat(this.hoverEl.dataset.imgWidth) + "px";
+ }
+ }
+
+ onHide() {
+ this.oldPopover = null;
+ if (this.parent?.hoverPopover === this) {
+ this.parent.hoverPopover = null;
+ }
+ }
+
+ hide() {
+ this.onTarget = false;
+ this.detaching = true;
+ // Once we reach this point, we're committed to closing
+
+ // in case we didn't ever call show()
+
+
+ // A timer might be pending to call show() for the first time, make sure
+ // it doesn't bring us back up after we close
+ if (this.timer) {
+ clearTimeout(this.timer);
+ this.timer = 0;
+ }
+
+ // Hide our HTML element immediately, even if our leaves might not be
+ // detachable yet. This makes things more responsive and improves the
+ // odds of not showing an empty popup that's just going to disappear
+ // momentarily.
+ this.hoverEl.hide();
+
+ // If a file load is in progress, we need to wait until it's finished before
+ // detaching leaves. Because we set .detaching, The in-progress openFile()
+ // will call us again when it finishes.
+ if (this.opening) return;
+
+ // Leave this code here to observe the state of the leaves
+ const leaves = this.leaves();
+ if (leaves.length) {
+ // Detach all leaves before we unload the popover and remove it from the DOM.
+ // Each leaf.detach() will trigger layout-changed and the updateLeaves()
+ // method will then call hide() again when the last one is gone.
+ // leaves[0].detach();
+ // leaves[0].detach();
+ this.targetEl.empty();
+ } else {
+ this.parent = null;
+ this.abortController?.unload();
+ this.abortController = undefined;
+ return this.nativeHide();
+ }
+ }
+
+ nativeHide() {
+ const {hoverEl, targetEl} = this;
+ this.state = PopoverState.Hidden;
+ hoverEl.detach();
+
+ if (targetEl) {
+ const parent = targetEl.matchParent(".fs-leaf-view");
+ if (parent) popovers.get(parent)?.transition();
+ }
+
+ this.onHide();
+ this.unload();
+ }
+
+ resolveLink(linkText: string, sourcePath: string): TFile | null {
+ const link = parseLinktext(linkText);
+ const tFile = link ? this.plugin.app.metadataCache.getFirstLinkpathDest(link.path, sourcePath) : null;
+ return tFile;
+ }
+
+ async openLink(linkText: string, sourcePath: string, eState?: EphemeralState, createInLeaf?: WorkspaceLeaf) {
+ let file = this.resolveLink(linkText, sourcePath);
+ const link = parseLinktext(linkText);
+ if (!file && createInLeaf) {
+ const folder = this.plugin.app.fileManager.getNewFileParent(sourcePath);
+ file = await this.plugin.app.fileManager.createNewMarkdownFile(folder, link.path);
+ }
+
+ if (!file) {
+ // this.displayCreateFileAction(linkText, sourcePath, eState);
+ return;
+ }
+ const {viewRegistry} = this.plugin.app;
+ const viewType = viewRegistry.typeByExtension[file.extension];
+ if (!viewType || !viewRegistry.viewByType[viewType]) {
+ // this.displayOpenFileAction(file);
+ return;
+ }
+
+ eState = Object.assign(this.buildEphemeralState(file, link), eState);
+ const parentMode = this.getDefaultMode();
+ const state = this.buildState(parentMode, eState);
+ const leaf = await this.openFile(file, state, createInLeaf);
+ const leafViewType = leaf?.view?.getViewType();
+ // console.log(leaf);
+ if (leafViewType === "image") {
+ // TODO: temporary workaround to prevent image popover from disappearing immediately when using live preview
+ if (
+ this.parent?.hasOwnProperty("editorEl") &&
+ (this.parent as unknown as MarkdownEditView).editorEl!.hasClass("is-live-preview")
+ ) {
+ this.waitTime = 3000;
+ }
+ const img = leaf!.view.contentEl.querySelector("img")!;
+ this.hoverEl.dataset.imgHeight = String(img.naturalHeight);
+ this.hoverEl.dataset.imgWidth = String(img.naturalWidth);
+ this.hoverEl.dataset.imgRatio = String(img.naturalWidth / img.naturalHeight);
+ } else if (leafViewType === "pdf") {
+ this.hoverEl.style.height = "800px";
+ this.hoverEl.style.width = "600px";
+ }
+ if (state.state?.mode === "source") {
+ this.whenShown(() => {
+ // Not sure why this is needed, but without it we get issue #186
+ if (requireApiVersion("1.0")) (leaf?.view as any)?.editMode?.reinit?.();
+ leaf?.view?.setEphemeralState(state.eState);
+ });
+ }
+ }
+
+ whenShown(callback: () => any) {
+ // invoke callback once the popover is visible
+ if (this.detaching) return;
+ const existingCallback = this.onShowCallback;
+ this.onShowCallback = () => {
+ if (this.detaching) return;
+ callback();
+ if (typeof existingCallback === "function") existingCallback();
+ };
+ if (this.state === PopoverState.Shown) {
+ this.onShowCallback();
+ this.onShowCallback = undefined;
+ }
+ }
+
+ async openFile(file: TFile, openState?: OpenViewState, useLeaf?: WorkspaceLeaf) {
+ if (this.detaching) return;
+ const leaf = useLeaf ?? this.attachLeaf();
+ this.opening = true;
+
+ try {
+ await leaf.openFile(file, openState);
+ } catch (e) {
+ console.error(e);
+ } finally {
+ this.opening = false;
+ if (this.detaching) this.hide();
+ }
+ this.plugin.app.workspace.setActiveLeaf(leaf);
+
+ return leaf;
+ }
+
+ buildState(parentMode: string, eState?: EphemeralState) {
+ return {
+ active: false, // Don't let Obsidian force focus if we have autofocus off
+ state: {mode: "source"}, // Don't set any state for the view, because this leaf is stayed on another view.
+ eState: eState,
+ };
+ }
+
+ buildEphemeralState(
+ file: TFile,
+ link?: {
+ path: string;
+ subpath: string;
+ },
+ ) {
+ const cache = this.plugin.app.metadataCache.getFileCache(file);
+ const subpath = cache ? resolveSubpath(cache, link?.subpath || "") : undefined;
+ const eState: EphemeralState = {subpath: link?.subpath};
+ if (subpath) {
+ eState.line = subpath.start.line;
+ eState.startLoc = subpath.start;
+ eState.endLoc = subpath.end || undefined;
+ }
+ return eState;
+ }
}
From 32b64ea6421e898438221af80b0d51c31cb6986b Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Tue, 23 Jan 2024 16:47:56 +0800
Subject: [PATCH 07/12] chore: bump version
---
manifest.json | 2 +-
package.json | 2 +-
src/floatSearchIndex.ts | 3 ++-
versions.json | 3 ++-
4 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/manifest.json b/manifest.json
index 44130be..69bcf86 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "float-search",
"name": "Floating Search",
- "version": "3.4.7",
+ "version": "3.4.8",
"minAppVersion": "0.15.0",
"description": "You can use search view in modal/leaf/popout window now.",
"author": "Boninall",
diff --git a/package.json b/package.json
index e3b6697..3f7942f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "float-search",
- "version": "3.4.7",
+ "version": "3.4.8",
"description": "You can use search view in modal/leaf/popout window now.",
"main": "main.js",
"scripts": {
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index 5137a77..c58db40 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -162,8 +162,9 @@ export default class FloatSearchPlugin extends Plugin {
const uninstaller = around(Workspace.prototype, {
getLeaf: (next) =>
function (...args) {
- const activeLeaf = this.activeLeaf;
+ const activeLeaf = (this as Workspace).activeLeaf;
if (activeLeaf) {
+ // @ts-ignore
const fsCtnEl = (activeLeaf.parent.containerEl as HTMLElement).parentElement;
if (fsCtnEl?.hasClass("fs-content")) {
if (activeLeaf.view.getViewType() === "markdown") {
diff --git a/versions.json b/versions.json
index 82f58ce..360822b 100644
--- a/versions.json
+++ b/versions.json
@@ -24,5 +24,6 @@
"3.4.4": "0.15.0",
"3.4.5": "0.15.0",
"3.4.6": "0.15.0",
- "3.4.7": "0.15.0"
+ "3.4.7": "0.15.0",
+ "3.4.8": "0.15.0"
}
\ No newline at end of file
From 5330b125f0def03d5e75c7e37ebbe150e9ca15ac Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Wed, 24 Jan 2024 17:18:54 +0800
Subject: [PATCH 08/12] fix: cannot open leaf when last leaf is pinned
---
src/floatSearchIndex.ts | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index c58db40..03c138e 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -170,7 +170,8 @@ export default class FloatSearchPlugin extends Plugin {
if (activeLeaf.view.getViewType() === "markdown") {
return activeLeaf;
}
- const newLeaf = self.app.workspace.getUnpinnedLeaf();
+
+ const newLeaf = self.app.workspace.getMostRecentLeaf();
if (newLeaf) {
this.setActiveLeaf(newLeaf);
}
@@ -207,12 +208,21 @@ export default class FloatSearchPlugin extends Plugin {
if (parent === self.app.workspace.rootSplit || (WorkspaceContainer && parent instanceof WorkspaceContainer)) {
for (const popover of EmbeddedView.popoversForWindow((parent as WorkspaceContainer).win)) {
// Use old API here for compat w/0.14.x
- if (old.call(this, cb, popover.rootSplit)) return true;
+ if (old.call(this, cb, popover.rootSplit)) return false;
}
}
return false;
};
},
+ setActiveLeaf(old) {
+ return function (leaf: any, params?: any) {
+ if (isEmebeddedLeaf(leaf)) {
+ old.call(this, leaf, params);
+ leaf.activeTime = 1700000000000;
+ }
+ return old.call(this, leaf, params);
+ };
+ },
onDragLeaf(old) {
return function (event: MouseEvent, leaf: WorkspaceLeaf) {
return old.call(this, event, leaf);
@@ -308,7 +318,6 @@ export default class FloatSearchPlugin extends Plugin {
patchSearchView() {
const updateCurrentState = (state: searchState) => {
this.state = state;
- console.log(state);
this.settings.searchViewState = state as searchState;
this.applySettingsUpdate();
};
@@ -406,7 +415,6 @@ export default class FloatSearchPlugin extends Plugin {
})
);
searchView.leaf?.rebuildView();
- console.log("Metadata-Style: all property view get patched");
return true;
};
this.app.workspace.onLayoutReady(() => {
@@ -503,7 +511,6 @@ export default class FloatSearchPlugin extends Plugin {
case "split":
// @ts-ignore
const isExistingLeaf = existingLeaf.find((leaf) => !leaf.parentSplit.parent.side);
- console.log(isExistingLeaf);
if (isExistingLeaf) {
this.app.workspace.revealLeaf(isExistingLeaf);
isExistingLeaf.setViewState({
From 667a956f87e5e6333542ce1a64227638d58f614c Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Wed, 24 Jan 2024 17:19:29 +0800
Subject: [PATCH 09/12] chore: bump version
---
manifest.json | 2 +-
package.json | 2 +-
versions.json | 3 ++-
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/manifest.json b/manifest.json
index 69bcf86..a75561f 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "float-search",
"name": "Floating Search",
- "version": "3.4.8",
+ "version": "3.4.9",
"minAppVersion": "0.15.0",
"description": "You can use search view in modal/leaf/popout window now.",
"author": "Boninall",
diff --git a/package.json b/package.json
index 3f7942f..5f89682 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "float-search",
- "version": "3.4.8",
+ "version": "3.4.9",
"description": "You can use search view in modal/leaf/popout window now.",
"main": "main.js",
"scripts": {
diff --git a/versions.json b/versions.json
index 360822b..2c4a00e 100644
--- a/versions.json
+++ b/versions.json
@@ -25,5 +25,6 @@
"3.4.5": "0.15.0",
"3.4.6": "0.15.0",
"3.4.7": "0.15.0",
- "3.4.8": "0.15.0"
+ "3.4.8": "0.15.0",
+ "3.4.9": "0.15.0"
}
\ No newline at end of file
From 36e22c887d90b1c9b8966edd1f0660ddf96ba573 Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Wed, 24 Jan 2024 17:43:38 +0800
Subject: [PATCH 10/12] chore: bump version
---
manifest.json | 2 +-
package.json | 2 +-
src/floatSearchIndex.ts | 25 +++++++++++++++++++++++--
versions.json | 3 ++-
4 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/manifest.json b/manifest.json
index a75561f..1872520 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "float-search",
"name": "Floating Search",
- "version": "3.4.9",
+ "version": "3.4.10",
"minAppVersion": "0.15.0",
"description": "You can use search view in modal/leaf/popout window now.",
"author": "Boninall",
diff --git a/package.json b/package.json
index 5f89682..c77738d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "float-search",
- "version": "3.4.9",
+ "version": "3.4.10",
"description": "You can use search view in modal/leaf/popout window now.",
"main": "main.js",
"scripts": {
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index 03c138e..a8ced2c 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -3,7 +3,7 @@ import {
App,
debounce,
Editor,
- ExtraButtonComponent,
+ ExtraButtonComponent, Keymap,
Menu,
MenuItem,
Modal,
@@ -610,6 +610,8 @@ class FloatSearchModal extends Modal {
private fileEl: HTMLElement;
private viewType: string;
+ private focusdItem: any;
+
constructor(cb: (state: any) => void, plugin: FloatSearchPlugin, state: any, viewType: string = "search") {
super(plugin.app);
this.plugin = plugin;
@@ -672,6 +674,11 @@ class FloatSearchModal extends Modal {
altEnterIconEl.setText("Alt+↵");
altEnterTextEl.setText("Open File and Close");
+ const ctrlEnterIconEl = altEnterInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
+ const ctrlEnterTextEl = altEnterInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
+ altEnterIconEl.setText("Ctrl+↵");
+ altEnterTextEl.setText("Create File When Not Exist");
+
const tabIconEl = tabInstructionsEl.createSpan({cls: "float-search-modal-instructions-key"});
const tabTextEl = tabInstructionsEl.createSpan({cls: "float-search-modal-instructions-text"});
tabIconEl.setText("Tab/Shift+Tab");
@@ -728,10 +735,12 @@ class FloatSearchModal extends Modal {
if (currentView.dom.focusedItem.collapsible) {
currentView.dom.focusedItem.setCollapse(false);
}
+ this.focusdItem = currentView.dom.focusedItem;
}
break;
} else {
currentView.onKeyArrowDownInFocus(e);
+ this.focusdItem = currentView.dom.focusedItem;
break;
}
case "ArrowUp":
@@ -741,10 +750,15 @@ class FloatSearchModal extends Modal {
if (currentView.dom.focusedItem.collapseEl) {
currentView.dom.focusedItem.setCollapse(true);
}
+ this.focusdItem = currentView.dom.focusedItem;
}
break;
} else {
currentView.onKeyArrowUpInFocus(e);
+ this.focusdItem = currentView.dom.focusedItem;
+ if (!currentView.dom.focusedItem.content) {
+ this.focusdItem = undefined;
+ }
break;
}
case "ArrowLeft":
@@ -754,6 +768,14 @@ class FloatSearchModal extends Modal {
currentView.onKeyArrowRightInFocus(e);
break;
case "Enter":
+ if (Keymap.isModifier(e, 'Mod') && !this.focusdItem) {
+ e.preventDefault();
+ const fileName = inputEl.value;
+ const real = fileName.replace(/[/\\?%*:|"<>]/g, '-');
+ this.plugin.app.workspace.openLinkText(real, "", true);
+ this.close();
+ break;
+ }
currentView.onKeyEnterInFocus(e);
if (e.altKey && currentView.dom.focusedItem) {
this.close();
@@ -815,7 +837,6 @@ class FloatSearchModal extends Modal {
navigator.clipboard.writeText(text);
}
break;
-
}
};
}
diff --git a/versions.json b/versions.json
index 2c4a00e..d3075e2 100644
--- a/versions.json
+++ b/versions.json
@@ -26,5 +26,6 @@
"3.4.6": "0.15.0",
"3.4.7": "0.15.0",
"3.4.8": "0.15.0",
- "3.4.9": "0.15.0"
+ "3.4.9": "0.15.0",
+ "3.4.10": "0.15.0"
}
\ No newline at end of file
From 31fdb1d40901d778e7fe734cc2b59c7c2a3063cd Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Wed, 24 Jan 2024 17:48:37 +0800
Subject: [PATCH 11/12] style: update modal style
---
styles.css | 82 +++++++++++++++++++++++++++---------------------------
1 file changed, 41 insertions(+), 41 deletions(-)
diff --git a/styles.css b/styles.css
index 053e040..762d84b 100644
--- a/styles.css
+++ b/styles.css
@@ -7,107 +7,107 @@ If your plugin does not need CSS, delete this file.
*/
.float-search-modal {
- width: 600px;
+ width: 700px;
- padding-bottom: 0;
+ padding-bottom: 0;
}
.float-search-modal.float-search-width {
- width: 1200px;
+ width: 1200px;
}
.float-search-modal-search-ctn, .float-search-modal-file-ctn {
- height: 100%;
- width: 100%;
+ height: 100%;
+ width: 100%;
}
.float-search-modal-file-ctn .view-header {
- display: none;
+ display: none;
}
.float-search-modal-content {
- height: 800px;
- display: flex;
- align-items: center;
- flex-direction: row;
+ height: 800px;
+ display: flex;
+ align-items: center;
+ flex-direction: row;
}
.float-search-modal-file-ctn .view-content {
- height: 100%;
+ height: 100%;
}
.float-search-modal .modal-close-button {
- z-index: 40;
+ z-index: 40;
}
.fs-content .workspace-split.mod-vertical {
- height: 100%;
+ height: 100%;
}
.fs-content {
- height: 100%;
+ height: 100%;
}
.fs-content .cm-scroller {
- margin-top: 20px;
+ margin-top: 20px;
}
.fs-block {
- height: 100% !important;
+ height: 100% !important;
}
.fs-block .workspace-leaf-resize-handle {
- display: none;
+ display: none;
}
.modal-container.float-search-modal-container.mod-dim {
- z-index: 30;
+ z-index: 30;
}
.float-search-modal-instructions {
- border-top: 1px solid var(--background-secondary);
- user-select: none;
- font-size: var(--font-ui-smaller);
- color: var(--text-muted);
- padding: var(--size-4-2);
- text-align: center;
- display: flex;
- flex-wrap: wrap;
- justify-content: center;
- gap: var(--size-4-3);
+ border-top: 1px solid var(--background-secondary);
+ user-select: none;
+ font-size: var(--font-ui-smaller);
+ color: var(--text-muted);
+ padding: var(--size-4-2);
+ text-align: center;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: var(--size-4-3);
}
.float-search-modal-instructions .float-search-modal-instructions-key {
- font-weight: var(--font-extrabold);
- margin-right: var(--size-2-2);
+ font-weight: var(--font-extrabold);
+ margin-right: var(--size-2-2);
}
.float-search-modal-content:has(.float-search-modal-file-ctn) .float-search-modal-search-ctn {
- border-right: 1px solid var(--background-secondary);
+ border-right: 1px solid var(--background-secondary);
}
.float-search-modal-file-ctn:has(.mod-active) {
- border-bottom: 3px solid var(--color-accent);
+ border-bottom: 3px solid var(--color-accent);
}
.float-search-modal-file-ctn:has(.mod-active) .cm-scroller {
- background-color: var(--background-primary);
+ background-color: var(--background-primary);
}
.float-search-view-switch {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
}
.float-search-view-menu .menu-item-icon {
- color: var(--text-muted);
+ color: var(--text-muted);
}
.float-search-view-menu svg {
- fill: none;
- stroke: currentcolor;
- stroke-width: 2;
- stroke-linecap: round;
- stroke-linejoin: round;
+ fill: none;
+ stroke: currentcolor;
+ stroke-width: 2;
+ stroke-linecap: round;
+ stroke-linejoin: round;
}
From 285693757c1023d96948f67c0ca5efe185cae1e8 Mon Sep 17 00:00:00 2001
From: quorafind <951011105@qq.com>
Date: Wed, 24 Jan 2024 17:50:30 +0800
Subject: [PATCH 12/12] feat: support create via ctrl+shift+enter
---
src/floatSearchIndex.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/floatSearchIndex.ts b/src/floatSearchIndex.ts
index a8ced2c..ece99d3 100644
--- a/src/floatSearchIndex.ts
+++ b/src/floatSearchIndex.ts
@@ -768,9 +768,9 @@ class FloatSearchModal extends Modal {
currentView.onKeyArrowRightInFocus(e);
break;
case "Enter":
- if (Keymap.isModifier(e, 'Mod') && !this.focusdItem) {
+ if (Keymap.isModifier(e, 'Mod') && Keymap.isModifier(e, 'Shift') && !this.focusdItem) {
e.preventDefault();
- const fileName = inputEl.value;
+ const fileName = inputEl.value.trim();
const real = fileName.replace(/[/\\?%*:|"<>]/g, '-');
this.plugin.app.workspace.openLinkText(real, "", true);
this.close();