mirror of
https://github.com/4source/open-editors-obsidian-plugin.git
synced 2026-07-22 11:40:26 +00:00
commit
1548a3bad5
4 changed files with 229 additions and 137 deletions
219
.eslintrc
219
.eslintrc
|
|
@ -1,108 +1,115 @@
|
|||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"@stylistic/eslint-plugin"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"@stylistic/indent": [
|
||||
"error",
|
||||
"tab"
|
||||
],
|
||||
"@stylistic/indent-binary-ops": [
|
||||
"error",
|
||||
"tab"
|
||||
],
|
||||
"@stylistic/quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"@stylistic/semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@stylistic/brace-style": [
|
||||
"error",
|
||||
"stroustrup",
|
||||
{
|
||||
"allowSingleLine": true
|
||||
}
|
||||
],
|
||||
"@stylistic/comma-dangle": [
|
||||
"error",
|
||||
"always-multiline"
|
||||
],
|
||||
"@stylistic/dot-location": [
|
||||
"error",
|
||||
"property"
|
||||
],
|
||||
"@stylistic/function-call-argument-newline": [
|
||||
"error",
|
||||
"consistent"
|
||||
],
|
||||
"@stylistic/function-call-spacing": "error",
|
||||
"@stylistic/implicit-arrow-linebreak": "error",
|
||||
"@stylistic/key-spacing": "error",
|
||||
"@stylistic/lines-around-comment": "error",
|
||||
"@stylistic/new-parens": "error",
|
||||
"@stylistic/no-confusing-arrow": "error",
|
||||
"@stylistic/no-extra-semi": "error",
|
||||
"@stylistic/no-floating-decimal": "error",
|
||||
"@stylistic/no-mixed-operators": "error",
|
||||
"@stylistic/no-mixed-spaces-and-tabs": "error",
|
||||
"@stylistic/no-multi-spaces": "error",
|
||||
"@stylistic/no-multiple-empty-lines": [
|
||||
"error",
|
||||
{
|
||||
"max": 1,
|
||||
"maxEOF": 0,
|
||||
"maxBOF": 0
|
||||
}
|
||||
],
|
||||
"@stylistic/no-trailing-spaces": [
|
||||
"error",
|
||||
{
|
||||
"skipBlankLines": true
|
||||
}
|
||||
],
|
||||
"@stylistic/nonblock-statement-body-position": "error",
|
||||
"@stylistic/operator-linebreak": [
|
||||
"error",
|
||||
"after"
|
||||
],
|
||||
"@stylistic/padded-blocks": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"@stylistic/padding-line-between-statements": "error",
|
||||
"@stylistic/rest-spread-spacing": "error",
|
||||
"@stylistic/semi-spacing": "error",
|
||||
"@stylistic/semi-style": "error",
|
||||
"@stylistic/space-before-blocks": "error",
|
||||
"@stylistic/space-before-function-paren": "error",
|
||||
"@stylistic/arrow-spacing": "error",
|
||||
"@stylistic/space-in-parens": "error",
|
||||
"@stylistic/space-infix-ops": "error",
|
||||
"@stylistic/space-unary-ops": "error",
|
||||
"@stylistic/spaced-comment": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@stylistic/switch-colon-spacing": "error",
|
||||
"@stylistic/template-curly-spacing": "error",
|
||||
"@stylistic/type-generic-spacing": "error",
|
||||
"@stylistic/type-named-tuple-spacing": "error"
|
||||
}
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"@stylistic/eslint-plugin"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"@stylistic/indent": [
|
||||
"error",
|
||||
"tab"
|
||||
],
|
||||
"@stylistic/indent-binary-ops": [
|
||||
"error",
|
||||
"tab"
|
||||
],
|
||||
"@stylistic/quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"@stylistic/semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@stylistic/brace-style": [
|
||||
"error",
|
||||
"stroustrup",
|
||||
{
|
||||
"allowSingleLine": true
|
||||
}
|
||||
],
|
||||
"@stylistic/comma-dangle": [
|
||||
"error",
|
||||
"always-multiline"
|
||||
],
|
||||
"@stylistic/dot-location": [
|
||||
"error",
|
||||
"property"
|
||||
],
|
||||
"@stylistic/function-call-argument-newline": [
|
||||
"error",
|
||||
"consistent"
|
||||
],
|
||||
"@stylistic/function-call-spacing": "error",
|
||||
"@stylistic/implicit-arrow-linebreak": "error",
|
||||
"@stylistic/key-spacing": "error",
|
||||
"@stylistic/lines-around-comment": "error",
|
||||
"@stylistic/new-parens": "error",
|
||||
"@stylistic/no-confusing-arrow": "error",
|
||||
"@stylistic/no-extra-semi": "error",
|
||||
"@stylistic/no-floating-decimal": "error",
|
||||
"@stylistic/no-mixed-operators": "error",
|
||||
"@stylistic/no-mixed-spaces-and-tabs": "error",
|
||||
"@stylistic/no-multi-spaces": "error",
|
||||
"@stylistic/no-multiple-empty-lines": [
|
||||
"error",
|
||||
{
|
||||
"max": 1,
|
||||
"maxEOF": 0,
|
||||
"maxBOF": 0
|
||||
}
|
||||
],
|
||||
"@stylistic/no-trailing-spaces": [
|
||||
"error",
|
||||
{
|
||||
"skipBlankLines": true
|
||||
}
|
||||
],
|
||||
"@stylistic/nonblock-statement-body-position": "error",
|
||||
"@stylistic/operator-linebreak": [
|
||||
"error",
|
||||
"after"
|
||||
],
|
||||
"@stylistic/padded-blocks": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"@stylistic/padding-line-between-statements": "error",
|
||||
"@stylistic/rest-spread-spacing": "error",
|
||||
"@stylistic/semi-spacing": "error",
|
||||
"@stylistic/semi-style": "error",
|
||||
"@stylistic/space-before-blocks": "error",
|
||||
"@stylistic/space-before-function-paren": "error",
|
||||
"@stylistic/arrow-spacing": "error",
|
||||
"@stylistic/space-in-parens": "error",
|
||||
"@stylistic/space-infix-ops": "error",
|
||||
"@stylistic/space-unary-ops": "error",
|
||||
"@stylistic/spaced-comment": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@stylistic/switch-colon-spacing": "error",
|
||||
"@stylistic/template-curly-spacing": "error",
|
||||
"@stylistic/type-generic-spacing": "error",
|
||||
"@stylistic/type-named-tuple-spacing": "error",
|
||||
"no-fallthrough": [
|
||||
"error",
|
||||
{
|
||||
"allowEmptyCase": true
|
||||
}
|
||||
],
|
||||
"no-console": ["error", {"allow": ["warn", "error", "debug"]}]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { View, WorkspaceLeaf } from 'obsidian';
|
||||
import { View, WorkspaceLeaf, WorkspaceSplit } from 'obsidian';
|
||||
import { ICON_CLOSE, ICON_CLOSE_GROUP, ICON_CLOSE_WINDOW, ICON_OPEN_EDITORS, VIEW_DISPLAY_OPEN_EDITORS, VIEW_TYPE_OPEN_EDITORS } from './constants';
|
||||
import { TreeItem } from './TreeItem';
|
||||
|
||||
|
|
@ -53,23 +53,31 @@ export class OpenEditorsView extends View {
|
|||
const main = layout['main'];
|
||||
if (main) {
|
||||
// Create a top-level tree item for the main window
|
||||
const tree = new TreeItem(this.treeEl, 'Main window', main.id, undefined, [{
|
||||
const tree = new TreeItem(this.treeEl, 'Main window', main.id, main.type, undefined, [{
|
||||
iconId: ICON_CLOSE_WINDOW,
|
||||
ariaLabel: 'Close all',
|
||||
onClickCallback: () => {
|
||||
// Recursively detach all leaves in the tree
|
||||
tree.rekursiveCall((tree) => {
|
||||
this.app.workspace.getLeafById(tree.id)?.detach();
|
||||
});
|
||||
tree.rekursiveCallForType([{
|
||||
callback: (tree) => {
|
||||
this.app.workspace.getLeafById(tree.id)?.detach();
|
||||
},
|
||||
type: 'leaf',
|
||||
}]);
|
||||
},
|
||||
ariaLabel: 'Close all',
|
||||
}]);
|
||||
// Add the tree item to the list of windows
|
||||
this.windows.push(tree);
|
||||
|
||||
// Recursively build the tree structure for all child elements
|
||||
main.children.forEach((element: { id: string, type: string, children: object[], state: { title: string, type: string } }) => {
|
||||
this.TreeWalker(element, tree);
|
||||
});
|
||||
if (main.children.length > 1) {
|
||||
this.TreeWalker(main, tree);
|
||||
}
|
||||
else {
|
||||
main.children.forEach((element: { id: string, type: string, children: object[], state: { title: string, type: string } }) => {
|
||||
this.TreeWalker(element, tree);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Handle floating windows (popout windows)
|
||||
|
|
@ -93,15 +101,18 @@ export class OpenEditorsView extends View {
|
|||
// Handle popout window layouts
|
||||
case 'window':
|
||||
// Create a tree item for each floating window
|
||||
parent = new TreeItem(this.treeEl, `Window ${count}`, layout.id, undefined, [{
|
||||
parent = new TreeItem(this.treeEl, `Window ${count}`, layout.id, layout.type, undefined, [{
|
||||
iconId: ICON_CLOSE_WINDOW,
|
||||
ariaLabel: 'Close all',
|
||||
onClickCallback: () => {
|
||||
// Recursively detach all leaves in the tree
|
||||
parent?.rekursiveCall((parent) => {
|
||||
this.app.workspace.getLeafById(parent.id)?.detach();
|
||||
});
|
||||
parent?.rekursiveCallForType([{
|
||||
callback: (parent) => {
|
||||
this.app.workspace.getLeafById(parent.id)?.detach();
|
||||
},
|
||||
type: 'leaf',
|
||||
}]);
|
||||
},
|
||||
ariaLabel: 'Close all',
|
||||
}]);
|
||||
// Add the tree item to the list of windows
|
||||
this.windows.push(parent);
|
||||
|
|
@ -110,14 +121,17 @@ export class OpenEditorsView extends View {
|
|||
let groupCount = 1;
|
||||
// Recursively build the tree structure for all child elements
|
||||
layout.children.forEach((element: { id: string, type: string, children: object[], state: { title: string, type: string } }) => {
|
||||
const group = parent?.addChild((containerEl) => new TreeItem(containerEl, `Group ${groupCount}`, element.id, undefined, [{
|
||||
const group = parent?.addChild((containerEl) => new TreeItem(containerEl, `Group ${groupCount}`, element.id, element.type, undefined, [{
|
||||
iconId: ICON_CLOSE_GROUP,
|
||||
onClickCallback: () => {
|
||||
parent?.rekursiveCall((tree) => {
|
||||
this.app.workspace.getLeafById(tree.id)?.detach();
|
||||
});
|
||||
},
|
||||
ariaLabel: 'Close all',
|
||||
onClickCallback: () => {
|
||||
parent?.rekursiveCallForType([{
|
||||
callback: (tree) => {
|
||||
this.app.workspace.getLeafById(tree.id)?.detach();
|
||||
},
|
||||
type: 'leaf',
|
||||
}]);
|
||||
},
|
||||
}]));
|
||||
this.TreeWalker(element, group);
|
||||
groupCount++;
|
||||
|
|
@ -136,17 +150,66 @@ export class OpenEditorsView extends View {
|
|||
case 'split':
|
||||
layout.children.forEach((element: { id: string, type: string, children: object[], state: { title: string, type: string } }) => {
|
||||
// Create a child tree item for each split group
|
||||
const tree = parent?.addChild((container) => new TreeItem(container, `Group ${count}`, element.id, undefined, [{
|
||||
iconId: ICON_CLOSE_GROUP,
|
||||
const group = parent?.addChild((container) => new TreeItem(container, `Group ${count}`, element.id, element.type, undefined, [{
|
||||
iconId: ICON_CLOSE,
|
||||
ariaLabel: 'Close group',
|
||||
onClickCallback: () => {
|
||||
tree?.rekursiveCall((tree) => {
|
||||
this.app.workspace.getLeafById(tree.id)?.detach();
|
||||
});
|
||||
group?.rekursiveCallForType([{
|
||||
callback: (tree) => {
|
||||
this.app.workspace.getLeafById(tree.id)?.detach();
|
||||
},
|
||||
type: 'leaf',
|
||||
}]);
|
||||
},
|
||||
}, {
|
||||
iconId: ICON_CLOSE_GROUP,
|
||||
ariaLabel: 'Close all editors',
|
||||
onClickCallback: () => {
|
||||
if (group) {
|
||||
// Find leaf parent
|
||||
let leafParent: WorkspaceSplit | undefined;
|
||||
group.rekursiveCallForType([{
|
||||
callback: tree => {
|
||||
const leaf = this.app.workspace.getLeafById(tree.id);
|
||||
if (!leaf) {
|
||||
console.debug('Leaf not found with the id ', tree.id);
|
||||
return;
|
||||
}
|
||||
const parent = leaf?.parent;
|
||||
if (!parent) {
|
||||
console.debug('Parent of leaf not found!');
|
||||
return;
|
||||
}
|
||||
leafParent = parent;
|
||||
},
|
||||
type: 'leaf',
|
||||
}]);
|
||||
if (!leafParent) {
|
||||
console.warn('Parent of leaf not found!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a new tab to parent
|
||||
const newId = this.app.workspace.createLeafInParent(leafParent, 0).id;
|
||||
if (!newId) {
|
||||
console.warn('No id of new leaf!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursively detach all leaves in the tree except for new tab
|
||||
group?.rekursiveCallForType([{
|
||||
callback: (tree) => {
|
||||
if (tree.id != newId) {
|
||||
this.app.workspace.getLeafById(tree.id)?.detach();
|
||||
}
|
||||
},
|
||||
type: 'leaf',
|
||||
}]);
|
||||
}
|
||||
},
|
||||
ariaLabel: 'Close all',
|
||||
}]));
|
||||
// Recursively process tabs as children
|
||||
this.TreeWalker(element, tree);
|
||||
this.TreeWalker(element, group);
|
||||
count++;
|
||||
});
|
||||
break;
|
||||
|
|
@ -158,7 +221,7 @@ export class OpenEditorsView extends View {
|
|||
break;
|
||||
// Handle individual leaves
|
||||
case 'leaf':
|
||||
parent?.addChild((container) => new TreeItem(container, layout.state.title, layout.id, {
|
||||
parent?.addChild((container) => new TreeItem(container, layout.state.title, layout.id, layout.type, {
|
||||
onClickCallback: () => {
|
||||
// Set the clicked leaf as the active leaf in the workspace
|
||||
const leaf = this.app.workspace.getLeafById(layout.id);
|
||||
|
|
@ -171,10 +234,10 @@ export class OpenEditorsView extends View {
|
|||
},
|
||||
}, [{
|
||||
iconId: ICON_CLOSE,
|
||||
ariaLabel: 'Close',
|
||||
onClickCallback: () => {
|
||||
this.app.workspace.getLeafById(layout.id)?.detach();
|
||||
},
|
||||
ariaLabel: 'Close',
|
||||
}]));
|
||||
break;
|
||||
default:
|
||||
|
|
@ -195,4 +258,4 @@ export class OpenEditorsView extends View {
|
|||
// Clear the tree container in the DOM
|
||||
this.treeEl.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ import { IconName, setIcon } from 'obsidian';
|
|||
export class TreeItem {
|
||||
// The ID of the tree item
|
||||
id: string;
|
||||
// The type of the workspace item behind this tree item
|
||||
type: string;
|
||||
// Tree items inside this tree item
|
||||
children: TreeItem[];
|
||||
// Main container element for the tree item
|
||||
|
|
@ -26,6 +28,7 @@ export class TreeItem {
|
|||
conteinerEl: HTMLDivElement,
|
||||
title: string,
|
||||
id: string,
|
||||
type: string,
|
||||
handler?: {
|
||||
onClickCallback?: (ev: MouseEvent) => void,
|
||||
onDragCallback?: (ev: DragEvent, delegateTarget: HTMLElement) => void
|
||||
|
|
@ -34,6 +37,7 @@ export class TreeItem {
|
|||
) {
|
||||
this.children = [];
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
|
||||
// Create the main tree item element
|
||||
this.treeItemEl = conteinerEl.createEl('div', { cls: 'tree-item' });
|
||||
|
|
@ -109,6 +113,23 @@ export class TreeItem {
|
|||
callback(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively apply a callback funstion to the current tree item and its children if the type matches the given type.
|
||||
* @param callback - The function to call for each tree item.
|
||||
* @param type - The type TreeItem needs to have
|
||||
*/
|
||||
rekursiveCallForType (actions: { callback: (tree: TreeItem) => void, type: string }[]) {
|
||||
this.children.forEach((child) => {
|
||||
child.rekursiveCallForType(actions);
|
||||
});
|
||||
|
||||
actions.forEach(action => {
|
||||
if (this.type === action.type) {
|
||||
action.callback(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child TreeItem to this tree item.
|
||||
* @param treeCreateCallback - A function that creates and returns a new TreeItem.
|
||||
|
|
|
|||
|
|
@ -9,4 +9,5 @@ export const ICON_MOVE_TO_NEW_WINDOW = 'picture-in-picture';
|
|||
export const ICON_OPEN_IN_NEW_WINDOW = 'picture-in-picture-2';
|
||||
export const ICON_SPLIT_RIGHT = 'separator-vertical';
|
||||
export const ICON_SPLIT_DOWN = 'separator-horizontal';
|
||||
export const ICON_REVEAL_IN_NAVIGATION = 'folder-open';
|
||||
export const ICON_REVEAL_IN_NAVIGATION = 'folder-open';
|
||||
export const ICON_COMBINE_GROUP = 'shrink';
|
||||
Loading…
Reference in a new issue