mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-08-10 10:22:29 +08:00
Merge branch 'develop' into port_dialogs
This commit is contained in:
commit
883a67bcfe
@ -66,8 +66,6 @@ chmod 755 $PKG_DIR/trilium.sh
|
||||
cp bin/tpl/anonymize-database.sql $PKG_DIR/
|
||||
|
||||
cp -r translations $PKG_DIR/
|
||||
cp -r dump-db $PKG_DIR/
|
||||
rm -rf $PKG_DIR/dump-db/node_modules
|
||||
|
||||
VERSION=`jq -r ".version" package.json`
|
||||
|
||||
|
43
eslint.config.js
Normal file
43
eslint.config.js
Normal file
@ -0,0 +1,43 @@
|
||||
import eslint from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default tseslint.config(
|
||||
eslint.configs.recommended,
|
||||
tseslint.configs.recommended,
|
||||
// consider using rules below, once we have a full TS codebase and can be more strict
|
||||
// tseslint.configs.strictTypeChecked,
|
||||
// tseslint.configs.stylisticTypeChecked,
|
||||
// tseslint.configs.recommendedTypeChecked,
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
// add rule overrides here
|
||||
"no-undef": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_",
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
"build/*",
|
||||
"dist/*",
|
||||
"docs/*",
|
||||
"libraries/*",
|
||||
"src/public/app-dist/*",
|
||||
"src/public/app/doc_notes/*"
|
||||
]
|
||||
}
|
||||
);
|
893
package-lock.json
generated
893
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -57,6 +57,8 @@
|
||||
"dev:watch-dist": "tsx ./bin/watch-dist.ts",
|
||||
"dev:prettier-check": "prettier . --check",
|
||||
"dev:prettier-fix": "prettier . --write",
|
||||
"dev:linter-check": "eslint .",
|
||||
"dev:linter-fix": "eslint . --fix",
|
||||
"chore:update-build-info": "tsx bin/update-build-info.ts",
|
||||
"chore:ci-update-nightly-version": "tsx ./bin/update-nightly-version.ts",
|
||||
"chore:generate-document": "cross-env nodemon ./bin/generate_document.ts 1000",
|
||||
@ -103,7 +105,6 @@
|
||||
"express-session": "1.18.1",
|
||||
"force-graph": "1.49.2",
|
||||
"fs-extra": "11.3.0",
|
||||
"happy-dom": "17.1.8",
|
||||
"helmet": "8.0.0",
|
||||
"html": "1.0.0",
|
||||
"html2plaintext": "2.1.4",
|
||||
@ -123,7 +124,6 @@
|
||||
"jsdom": "26.0.0",
|
||||
"jsplumb": "2.15.6",
|
||||
"katex": "0.16.21",
|
||||
"knockout": "3.5.1",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-gpx": "2.1.2",
|
||||
"mark.js": "8.11.1",
|
||||
@ -150,7 +150,6 @@
|
||||
"striptags": "3.2.0",
|
||||
"swagger-ui-express": "5.0.1",
|
||||
"tmp": "0.2.3",
|
||||
"ts-loader": "9.5.2",
|
||||
"turndown": "7.2.0",
|
||||
"unescape": "1.0.1",
|
||||
"vanilla-js-wheel-zoom": "9.0.4",
|
||||
@ -168,6 +167,7 @@
|
||||
"@electron-forge/maker-zip": "7.7.0",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "7.7.0",
|
||||
"@electron/rebuild": "3.7.1",
|
||||
"@eslint/js": "9.21.0",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@types/archiver": "6.0.3",
|
||||
@ -216,9 +216,12 @@
|
||||
"cross-env": "7.0.3",
|
||||
"css-loader": "7.1.2",
|
||||
"electron": "34.3.0",
|
||||
"eslint": "9.21.0",
|
||||
"esm": "3.2.25",
|
||||
"happy-dom": "17.2.2",
|
||||
"i18next-http-backend": "3.0.2",
|
||||
"jsdoc": "4.0.4",
|
||||
"knockout": "3.5.1",
|
||||
"lorem-ipsum": "2.0.8",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"nodemon": "3.1.9",
|
||||
@ -231,10 +234,12 @@
|
||||
"split.js": "1.6.5",
|
||||
"supertest": "7.0.0",
|
||||
"swagger-jsdoc": "6.2.8",
|
||||
"ts-loader": "9.5.2",
|
||||
"tslib": "2.8.1",
|
||||
"tsx": "4.19.3",
|
||||
"typedoc": "0.27.9",
|
||||
"typescript": "5.8.2",
|
||||
"typescript-eslint": "8.26.0",
|
||||
"vitest": "3.0.7",
|
||||
"webpack": "5.98.0",
|
||||
"webpack-cli": "6.0.1",
|
||||
|
@ -22,6 +22,7 @@ import type LoadResults from "../services/load_results.js";
|
||||
import type { Attribute } from "../services/attribute_parser.js";
|
||||
import type NoteTreeWidget from "../widgets/note_tree.js";
|
||||
import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js";
|
||||
import type { ContextMenuEvent } from "../menus/context_menu.js";
|
||||
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
|
||||
import type EditableTextTypeWidget from "../widgets/type_widgets/editable_text.js";
|
||||
|
||||
@ -56,8 +57,8 @@ export interface ContextMenuCommandData extends CommandData {
|
||||
}
|
||||
|
||||
export interface NoteCommandData extends CommandData {
|
||||
notePath?: string | null;
|
||||
hoistedNoteId?: string | null;
|
||||
notePath?: string;
|
||||
hoistedNoteId?: string;
|
||||
viewScope?: ViewScope;
|
||||
}
|
||||
|
||||
@ -172,9 +173,9 @@ export type CommandMappings = {
|
||||
callback: (value: NoteDetailWidget | PromiseLike<NoteDetailWidget>) => void;
|
||||
};
|
||||
executeWithTextEditor: CommandData &
|
||||
ExecuteCommandData<TextEditor> & {
|
||||
callback?: GetTextEditorCallback;
|
||||
};
|
||||
ExecuteCommandData<TextEditor> & {
|
||||
callback?: GetTextEditorCallback;
|
||||
};
|
||||
executeWithCodeEditor: CommandData & ExecuteCommandData<null>;
|
||||
/**
|
||||
* Called upon when attempting to retrieve the content element of a {@link NoteContext}.
|
||||
@ -326,7 +327,7 @@ type EventMappings = {
|
||||
ntxId: string | null;
|
||||
};
|
||||
contextsReopenedEvent: {
|
||||
mainNtxId: string | null;
|
||||
mainNtxId: string;
|
||||
tabPosition: number;
|
||||
};
|
||||
noteDetailRefreshed: {
|
||||
@ -340,7 +341,7 @@ type EventMappings = {
|
||||
newNoteContextCreated: {
|
||||
noteContext: NoteContext;
|
||||
};
|
||||
noteContextRemoved: {
|
||||
noteContextRemovedEvent: {
|
||||
ntxIds: string[];
|
||||
};
|
||||
exportSvg: {
|
||||
@ -366,6 +367,7 @@ type EventMappings = {
|
||||
textTypeWidget: EditableTextTypeWidget;
|
||||
text: string;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
export type EventListener<T extends EventNames> = {
|
||||
|
@ -66,13 +66,12 @@ export default class Entrypoints extends Component {
|
||||
}
|
||||
|
||||
async toggleNoteHoistingCommand({ noteId = appContext.tabManager.getActiveContextNoteId() }) {
|
||||
const activeNoteContext = appContext.tabManager.getActiveContext();
|
||||
|
||||
if (!activeNoteContext || !noteId) {
|
||||
if (!noteId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const noteToHoist = await froca.getNote(noteId);
|
||||
const activeNoteContext = appContext.tabManager.getActiveContext();
|
||||
|
||||
if (noteToHoist?.noteId === activeNoteContext.hoistedNoteId) {
|
||||
await activeNoteContext.unhoist();
|
||||
@ -84,11 +83,6 @@ export default class Entrypoints extends Component {
|
||||
async hoistNoteCommand({ noteId }: { noteId: string }) {
|
||||
const noteContext = appContext.tabManager.getActiveContext();
|
||||
|
||||
if (!noteContext) {
|
||||
logError("hoistNoteCommand: noteContext is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (noteContext.hoistedNoteId !== noteId) {
|
||||
await noteContext.setHoistedNoteId(noteId);
|
||||
}
|
||||
@ -180,11 +174,7 @@ export default class Entrypoints extends Component {
|
||||
}
|
||||
|
||||
async runActiveNoteCommand() {
|
||||
const noteContext = appContext.tabManager.getActiveContext();
|
||||
if (!noteContext) {
|
||||
return;
|
||||
}
|
||||
const { ntxId, note } = noteContext;
|
||||
const { ntxId, note } = appContext.tabManager.getActiveContext();
|
||||
|
||||
// ctrl+enter is also used elsewhere, so make sure we're running only when appropriate
|
||||
if (!note || note.type !== "code") {
|
||||
|
@ -4,40 +4,23 @@ import server from "../services/server.js";
|
||||
import options from "../services/options.js";
|
||||
import froca from "../services/froca.js";
|
||||
import treeService from "../services/tree.js";
|
||||
import utils from "../services/utils.js";
|
||||
import NoteContext from "./note_context.js";
|
||||
import appContext from "./app_context.js";
|
||||
import Mutex from "../utils/mutex.js";
|
||||
import linkService from "../services/link.js";
|
||||
import type { EventData } from "./app_context.js";
|
||||
import type FNote from "../entities/fnote.js";
|
||||
|
||||
interface TabState {
|
||||
contexts: NoteContext[];
|
||||
position: number;
|
||||
}
|
||||
|
||||
interface NoteContextState {
|
||||
ntxId: string;
|
||||
mainNtxId: string | null;
|
||||
notePath: string | null;
|
||||
hoistedNoteId: string;
|
||||
active: boolean;
|
||||
viewScope: Record<string, any>;
|
||||
}
|
||||
|
||||
export default class TabManager extends Component {
|
||||
public children: NoteContext[];
|
||||
public mutex: Mutex;
|
||||
public activeNtxId: string | null;
|
||||
public recentlyClosedTabs: TabState[];
|
||||
public tabsUpdate: SpacedUpdate;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
/** @property {NoteContext[]} */
|
||||
this.children = [];
|
||||
this.mutex = new Mutex();
|
||||
|
||||
this.activeNtxId = null;
|
||||
|
||||
// elements are arrays of {contexts, position}, storing note contexts for each tab (one main context + subcontexts [splits]), and the original position of the tab
|
||||
this.recentlyClosedTabs = [];
|
||||
|
||||
this.tabsUpdate = new SpacedUpdate(async () => {
|
||||
@ -45,9 +28,7 @@ export default class TabManager extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const openNoteContexts = this.noteContexts
|
||||
.map((nc) => nc.getPojoState())
|
||||
.filter((t) => !!t);
|
||||
const openNoteContexts = this.noteContexts.map((nc) => nc.getPojoState()).filter((t) => !!t);
|
||||
|
||||
await server.put("options", {
|
||||
openNoteContexts: JSON.stringify(openNoteContexts)
|
||||
@ -57,11 +38,13 @@ export default class TabManager extends Component {
|
||||
appContext.addBeforeUnloadListener(this);
|
||||
}
|
||||
|
||||
get noteContexts(): NoteContext[] {
|
||||
/** @returns {NoteContext[]} */
|
||||
get noteContexts() {
|
||||
return this.children;
|
||||
}
|
||||
|
||||
get mainNoteContexts(): NoteContext[] {
|
||||
/** @type {NoteContext[]} */
|
||||
get mainNoteContexts() {
|
||||
return this.noteContexts.filter((nc) => !nc.mainNtxId);
|
||||
}
|
||||
|
||||
@ -70,12 +53,11 @@ export default class TabManager extends Component {
|
||||
const noteContextsToOpen = (appContext.isMainWindow && options.getJson("openNoteContexts")) || [];
|
||||
|
||||
// preload all notes at once
|
||||
await froca.getNotes([...noteContextsToOpen.flatMap((tab: NoteContextState) =>
|
||||
[treeService.getNoteIdFromUrl(tab.notePath), tab.hoistedNoteId])], true);
|
||||
await froca.getNotes([...noteContextsToOpen.flatMap((tab) => [treeService.getNoteIdFromUrl(tab.notePath), tab.hoistedNoteId])], true);
|
||||
|
||||
const filteredNoteContexts = noteContextsToOpen.filter((openTab: NoteContextState) => {
|
||||
const filteredNoteContexts = noteContextsToOpen.filter((openTab) => {
|
||||
const noteId = treeService.getNoteIdFromUrl(openTab.notePath);
|
||||
if (noteId && !(noteId in froca.notes)) {
|
||||
if (!(noteId in froca.notes)) {
|
||||
// note doesn't exist so don't try to open tab for it
|
||||
return false;
|
||||
}
|
||||
@ -98,10 +80,9 @@ export default class TabManager extends Component {
|
||||
ntxId: parsedFromUrl.ntxId,
|
||||
active: true,
|
||||
hoistedNoteId: parsedFromUrl.hoistedNoteId || "root",
|
||||
viewScope: parsedFromUrl.viewScope || {},
|
||||
mainNtxId: null
|
||||
viewScope: parsedFromUrl.viewScope || {}
|
||||
});
|
||||
} else if (!filteredNoteContexts.find((tab: NoteContextState) => tab.active)) {
|
||||
} else if (!filteredNoteContexts.find((tab) => tab.active)) {
|
||||
filteredNoteContexts[0].active = true;
|
||||
}
|
||||
|
||||
@ -120,30 +101,21 @@ export default class TabManager extends Component {
|
||||
// if there's a notePath in the URL, make sure it's open and active
|
||||
// (useful, for e.g., opening clipped notes from clipper or opening link in an extra window)
|
||||
if (parsedFromUrl.notePath) {
|
||||
await appContext.tabManager.switchToNoteContext(
|
||||
parsedFromUrl.ntxId,
|
||||
parsedFromUrl.notePath,
|
||||
parsedFromUrl.viewScope,
|
||||
parsedFromUrl.hoistedNoteId
|
||||
);
|
||||
await appContext.tabManager.switchToNoteContext(parsedFromUrl.ntxId, parsedFromUrl.notePath, parsedFromUrl.viewScope, parsedFromUrl.hoistedNoteId);
|
||||
} else if (parsedFromUrl.searchString) {
|
||||
await appContext.triggerCommand("searchNotes", {
|
||||
searchString: parsedFromUrl.searchString
|
||||
});
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
logError(`Loading note contexts '${options.get("openNoteContexts")}' failed: ${e.message} ${e.stack}`);
|
||||
} else {
|
||||
logError(`Loading note contexts '${options.get("openNoteContexts")}' failed: ${String(e)}`);
|
||||
}
|
||||
} catch (e) {
|
||||
logError(`Loading note contexts '${options.get("openNoteContexts")}' failed: ${e.message} ${e.stack}`);
|
||||
|
||||
// try to recover
|
||||
await this.openEmptyTab();
|
||||
}
|
||||
}
|
||||
|
||||
noteSwitchedEvent({ noteContext }: EventData<"noteSwitched">) {
|
||||
noteSwitchedEvent({ noteContext }) {
|
||||
if (noteContext.isActive()) {
|
||||
this.setCurrentNavigationStateToHash();
|
||||
}
|
||||
@ -163,10 +135,10 @@ export default class TabManager extends Component {
|
||||
const activeNoteContext = this.getActiveContext();
|
||||
this.updateDocumentTitle(activeNoteContext);
|
||||
|
||||
this.triggerEvent("activeNoteChangedEvent", {}); // trigger this even in on popstate event
|
||||
this.triggerEvent("activeNoteChanged"); // trigger this even in on popstate event
|
||||
}
|
||||
|
||||
calculateHash(): string {
|
||||
calculateHash() {
|
||||
const activeNoteContext = this.getActiveContext();
|
||||
if (!activeNoteContext) {
|
||||
return "";
|
||||
@ -180,15 +152,21 @@ export default class TabManager extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
getNoteContexts(): NoteContext[] {
|
||||
/** @returns {NoteContext[]} */
|
||||
getNoteContexts() {
|
||||
return this.noteContexts;
|
||||
}
|
||||
|
||||
getMainNoteContexts(): NoteContext[] {
|
||||
/**
|
||||
* Main context is essentially a tab (children are splits), so this returns tabs.
|
||||
* @returns {NoteContext[]}
|
||||
*/
|
||||
getMainNoteContexts() {
|
||||
return this.noteContexts.filter((nc) => nc.isMainContext());
|
||||
}
|
||||
|
||||
getNoteContextById(ntxId: string | null): NoteContext {
|
||||
/** @returns {NoteContext} */
|
||||
getNoteContextById(ntxId) {
|
||||
const noteContext = this.noteContexts.find((nc) => nc.ntxId === ntxId);
|
||||
|
||||
if (!noteContext) {
|
||||
@ -198,47 +176,58 @@ export default class TabManager extends Component {
|
||||
return noteContext;
|
||||
}
|
||||
|
||||
getActiveContext(): NoteContext | null {
|
||||
/**
|
||||
* Get active context which represents the visible split with focus. Active context can, but doesn't have to be "main".
|
||||
*
|
||||
* @returns {NoteContext}
|
||||
*/
|
||||
getActiveContext() {
|
||||
return this.activeNtxId ? this.getNoteContextById(this.activeNtxId) : null;
|
||||
}
|
||||
|
||||
getActiveMainContext(): NoteContext | null {
|
||||
/**
|
||||
* Get active main context which corresponds to the active tab.
|
||||
*
|
||||
* @returns {NoteContext}
|
||||
*/
|
||||
getActiveMainContext() {
|
||||
return this.activeNtxId ? this.getNoteContextById(this.activeNtxId).getMainContext() : null;
|
||||
}
|
||||
|
||||
getActiveContextNotePath(): string | null {
|
||||
/** @returns {string|null} */
|
||||
getActiveContextNotePath() {
|
||||
const activeContext = this.getActiveContext();
|
||||
return activeContext?.notePath ?? null;
|
||||
return activeContext ? activeContext.notePath : null;
|
||||
}
|
||||
|
||||
getActiveContextNote(): FNote | null {
|
||||
/** @returns {FNote} */
|
||||
getActiveContextNote() {
|
||||
const activeContext = this.getActiveContext();
|
||||
return activeContext ? activeContext.note : null;
|
||||
}
|
||||
|
||||
getActiveContextNoteId(): string | null {
|
||||
/** @returns {string|null} */
|
||||
getActiveContextNoteId() {
|
||||
const activeNote = this.getActiveContextNote();
|
||||
|
||||
return activeNote ? activeNote.noteId : null;
|
||||
}
|
||||
|
||||
getActiveContextNoteType(): string | null {
|
||||
/** @returns {string|null} */
|
||||
getActiveContextNoteType() {
|
||||
const activeNote = this.getActiveContextNote();
|
||||
|
||||
return activeNote ? activeNote.type : null;
|
||||
}
|
||||
|
||||
getActiveContextNoteMime(): string | null {
|
||||
/** @returns {string|null} */
|
||||
getActiveContextNoteMime() {
|
||||
const activeNote = this.getActiveContextNote();
|
||||
|
||||
return activeNote ? activeNote.mime : null;
|
||||
}
|
||||
|
||||
async switchToNoteContext(
|
||||
ntxId: string | null,
|
||||
notePath: string,
|
||||
viewScope: Record<string, any> = {},
|
||||
hoistedNoteId: string | null = null
|
||||
) {
|
||||
const noteContext = this.noteContexts.find((nc) => nc.ntxId === ntxId) ||
|
||||
await this.openEmptyTab();
|
||||
async switchToNoteContext(ntxId, notePath, viewScope = {}, hoistedNoteId = null) {
|
||||
const noteContext = this.noteContexts.find((nc) => nc.ntxId === ntxId) || (await this.openEmptyTab());
|
||||
|
||||
await this.activateNoteContext(noteContext.ntxId);
|
||||
|
||||
@ -253,21 +242,20 @@ export default class TabManager extends Component {
|
||||
|
||||
async openAndActivateEmptyTab() {
|
||||
const noteContext = await this.openEmptyTab();
|
||||
|
||||
await this.activateNoteContext(noteContext.ntxId);
|
||||
noteContext.setEmpty();
|
||||
|
||||
await noteContext.setEmpty();
|
||||
}
|
||||
|
||||
async openEmptyTab(
|
||||
ntxId: string | null = null,
|
||||
hoistedNoteId: string = "root",
|
||||
mainNtxId: string | null = null
|
||||
): Promise<NoteContext> {
|
||||
async openEmptyTab(ntxId = null, hoistedNoteId = "root", mainNtxId) {
|
||||
const noteContext = new NoteContext(ntxId, hoistedNoteId, mainNtxId);
|
||||
|
||||
const existingNoteContext = this.children.find((nc) => nc.ntxId === noteContext.ntxId);
|
||||
|
||||
if (existingNoteContext) {
|
||||
await existingNoteContext.setHoistedNoteId(hoistedNoteId);
|
||||
|
||||
return existingNoteContext;
|
||||
}
|
||||
|
||||
@ -278,40 +266,29 @@ export default class TabManager extends Component {
|
||||
return noteContext;
|
||||
}
|
||||
|
||||
async openInNewTab(targetNoteId: string, hoistedNoteId: string | null = null) {
|
||||
const noteContext = await this.openEmptyTab(
|
||||
null,
|
||||
hoistedNoteId || this.getActiveContext()?.hoistedNoteId
|
||||
);
|
||||
async openInNewTab(targetNoteId, hoistedNoteId = null) {
|
||||
const noteContext = await this.openEmptyTab(null, hoistedNoteId || this.getActiveContext().hoistedNoteId);
|
||||
|
||||
await noteContext.setNote(targetNoteId);
|
||||
}
|
||||
|
||||
async openInSameTab(targetNoteId: string, hoistedNoteId: string | null = null) {
|
||||
async openInSameTab(targetNoteId, hoistedNoteId = null) {
|
||||
const activeContext = this.getActiveContext();
|
||||
if (!activeContext) return;
|
||||
|
||||
await activeContext.setHoistedNoteId(hoistedNoteId || activeContext.hoistedNoteId);
|
||||
await activeContext.setNote(targetNoteId);
|
||||
}
|
||||
|
||||
async openTabWithNoteWithHoisting(
|
||||
notePath: string,
|
||||
opts: {
|
||||
activate?: boolean | null;
|
||||
ntxId?: string | null;
|
||||
mainNtxId?: string | null;
|
||||
hoistedNoteId?: string | null;
|
||||
viewScope?: Record<string, any> | null;
|
||||
} = {}
|
||||
): Promise<NoteContext> {
|
||||
/**
|
||||
* If the requested notePath is within current note hoisting scope then keep the note hoisting also for the new tab.
|
||||
*/
|
||||
async openTabWithNoteWithHoisting(notePath, opts = {}) {
|
||||
const noteContext = this.getActiveContext();
|
||||
let hoistedNoteId = "root";
|
||||
|
||||
if (noteContext) {
|
||||
const resolvedNotePath = await treeService.resolveNotePath(notePath, noteContext.hoistedNoteId);
|
||||
|
||||
if (resolvedNotePath?.includes(noteContext.hoistedNoteId) || resolvedNotePath?.includes("_hidden")) {
|
||||
if (resolvedNotePath.includes(noteContext.hoistedNoteId) || resolvedNotePath.includes("_hidden")) {
|
||||
hoistedNoteId = noteContext.hoistedNoteId;
|
||||
}
|
||||
}
|
||||
@ -321,16 +298,7 @@ export default class TabManager extends Component {
|
||||
return this.openContextWithNote(notePath, opts);
|
||||
}
|
||||
|
||||
async openContextWithNote(
|
||||
notePath: string | null,
|
||||
opts: {
|
||||
activate?: boolean | null;
|
||||
ntxId?: string | null;
|
||||
mainNtxId?: string | null;
|
||||
hoistedNoteId?: string | null;
|
||||
viewScope?: Record<string, any> | null;
|
||||
} = {}
|
||||
): Promise<NoteContext> {
|
||||
async openContextWithNote(notePath, opts = {}) {
|
||||
const activate = !!opts.activate;
|
||||
const ntxId = opts.ntxId || null;
|
||||
const mainNtxId = opts.mainNtxId || null;
|
||||
@ -347,10 +315,10 @@ export default class TabManager extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
if (activate && noteContext.notePath) {
|
||||
if (activate) {
|
||||
this.activateNoteContext(noteContext.ntxId, false);
|
||||
|
||||
await this.triggerEvent("noteSwitchedAndActivatedEvent", {
|
||||
await this.triggerEvent("noteSwitchedAndActivated", {
|
||||
noteContext,
|
||||
notePath: noteContext.notePath // resolved note path
|
||||
});
|
||||
@ -359,24 +327,21 @@ export default class TabManager extends Component {
|
||||
return noteContext;
|
||||
}
|
||||
|
||||
async activateOrOpenNote(noteId: string) {
|
||||
async activateOrOpenNote(noteId) {
|
||||
for (const noteContext of this.getNoteContexts()) {
|
||||
if (noteContext.note && noteContext.note.noteId === noteId) {
|
||||
this.activateNoteContext(noteContext.ntxId);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if no tab with this note has been found we'll create new tab
|
||||
|
||||
await this.openContextWithNote(noteId, { activate: true });
|
||||
}
|
||||
|
||||
async activateNoteContext(ntxId: string | null, triggerEvent: boolean = true) {
|
||||
if (!ntxId) {
|
||||
logError("activateNoteContext: ntxId is null");
|
||||
return;
|
||||
}
|
||||
|
||||
async activateNoteContext(ntxId, triggerEvent = true) {
|
||||
if (ntxId === this.activeNtxId) {
|
||||
return;
|
||||
}
|
||||
@ -394,7 +359,11 @@ export default class TabManager extends Component {
|
||||
this.setCurrentNavigationStateToHash();
|
||||
}
|
||||
|
||||
async removeNoteContext(ntxId: string | null) {
|
||||
/**
|
||||
* @param ntxId
|
||||
* @returns {Promise<boolean>} true if note context has been removed, false otherwise
|
||||
*/
|
||||
async removeNoteContext(ntxId) {
|
||||
// removing note context is an async process which can take some time, if users presses CTRL-W quickly, two
|
||||
// close events could interleave which would then lead to attempting to activate already removed context.
|
||||
return await this.mutex.runExclusively(async () => {
|
||||
@ -404,7 +373,7 @@ export default class TabManager extends Component {
|
||||
noteContextToRemove = this.getNoteContextById(ntxId);
|
||||
} catch {
|
||||
// note context not found
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (noteContextToRemove.isMainContext()) {
|
||||
@ -414,7 +383,7 @@ export default class TabManager extends Component {
|
||||
if (noteContextToRemove.isEmpty()) {
|
||||
// this is already the empty note context, no point in closing it and replacing with another
|
||||
// empty tab
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.openEmptyTab();
|
||||
@ -430,7 +399,7 @@ export default class TabManager extends Component {
|
||||
const noteContextsToRemove = noteContextToRemove.getSubContexts();
|
||||
const ntxIdsToRemove = noteContextsToRemove.map((nc) => nc.ntxId);
|
||||
|
||||
await this.triggerEvent("beforeNoteContextRemove", { ntxIds: ntxIdsToRemove.filter((id) => id !== null) });
|
||||
await this.triggerEvent("beforeNoteContextRemove", { ntxIds: ntxIdsToRemove });
|
||||
|
||||
if (!noteContextToRemove.isMainContext()) {
|
||||
const siblings = noteContextToRemove.getMainContext().getSubContexts();
|
||||
@ -452,10 +421,12 @@ export default class TabManager extends Component {
|
||||
}
|
||||
|
||||
this.removeNoteContexts(noteContextsToRemove);
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
removeNoteContexts(noteContextsToRemove: NoteContext[]) {
|
||||
removeNoteContexts(noteContextsToRemove) {
|
||||
const ntxIdsToRemove = noteContextsToRemove.map((nc) => nc.ntxId);
|
||||
|
||||
const position = this.noteContexts.findIndex((nc) => ntxIdsToRemove.includes(nc.ntxId));
|
||||
@ -464,12 +435,12 @@ export default class TabManager extends Component {
|
||||
|
||||
this.addToRecentlyClosedTabs(noteContextsToRemove, position);
|
||||
|
||||
this.triggerEvent("noteContextRemoved", { ntxIds: ntxIdsToRemove.filter((id) => id !== null) });
|
||||
this.triggerEvent("noteContextRemoved", { ntxIds: ntxIdsToRemove });
|
||||
|
||||
this.tabsUpdate.scheduleUpdate();
|
||||
}
|
||||
|
||||
addToRecentlyClosedTabs(noteContexts: NoteContext[], position: number) {
|
||||
addToRecentlyClosedTabs(noteContexts, position) {
|
||||
if (noteContexts.length === 1 && noteContexts[0].isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@ -477,42 +448,26 @@ export default class TabManager extends Component {
|
||||
this.recentlyClosedTabs.push({ contexts: noteContexts, position: position });
|
||||
}
|
||||
|
||||
tabReorderEvent({ ntxIdsInOrder }: { ntxIdsInOrder: string[] }) {
|
||||
const order: Record<string, number> = {};
|
||||
tabReorderEvent({ ntxIdsInOrder }) {
|
||||
const order = {};
|
||||
|
||||
let i = 0;
|
||||
|
||||
for (const ntxId of ntxIdsInOrder) {
|
||||
for (const noteContext of this.getNoteContextById(ntxId).getSubContexts()) {
|
||||
if (noteContext.ntxId) {
|
||||
order[noteContext.ntxId] = i++;
|
||||
}
|
||||
order[noteContext.ntxId] = i++;
|
||||
}
|
||||
}
|
||||
|
||||
this.children.sort((a, b) => {
|
||||
if (!a.ntxId || !b.ntxId) return 0;
|
||||
return (order[a.ntxId] ?? 0) < (order[b.ntxId] ?? 0) ? -1 : 1;
|
||||
});
|
||||
this.children.sort((a, b) => (order[a.ntxId] < order[b.ntxId] ? -1 : 1));
|
||||
|
||||
this.tabsUpdate.scheduleUpdate();
|
||||
}
|
||||
|
||||
noteContextReorderEvent({
|
||||
ntxIdsInOrder,
|
||||
oldMainNtxId,
|
||||
newMainNtxId
|
||||
}: {
|
||||
ntxIdsInOrder: string[];
|
||||
oldMainNtxId?: string;
|
||||
newMainNtxId?: string;
|
||||
}) {
|
||||
noteContextReorderEvent({ ntxIdsInOrder, oldMainNtxId, newMainNtxId }) {
|
||||
const order = Object.fromEntries(ntxIdsInOrder.map((v, i) => [v, i]));
|
||||
|
||||
this.children.sort((a, b) => {
|
||||
if (!a.ntxId || !b.ntxId) return 0;
|
||||
return (order[a.ntxId] ?? 0) < (order[b.ntxId] ?? 0) ? -1 : 1;
|
||||
});
|
||||
this.children.sort((a, b) => (order[a.ntxId] < order[b.ntxId] ? -1 : 1));
|
||||
|
||||
if (oldMainNtxId && newMainNtxId) {
|
||||
this.children.forEach((c) => {
|
||||
@ -530,8 +485,7 @@ export default class TabManager extends Component {
|
||||
}
|
||||
|
||||
async activateNextTabCommand() {
|
||||
const activeMainNtxId = this.getActiveMainContext()?.ntxId;
|
||||
if (!activeMainNtxId) return;
|
||||
const activeMainNtxId = this.getActiveMainContext().ntxId;
|
||||
|
||||
const oldIdx = this.mainNoteContexts.findIndex((nc) => nc.ntxId === activeMainNtxId);
|
||||
const newActiveNtxId = this.mainNoteContexts[oldIdx === this.mainNoteContexts.length - 1 ? 0 : oldIdx + 1].ntxId;
|
||||
@ -540,8 +494,7 @@ export default class TabManager extends Component {
|
||||
}
|
||||
|
||||
async activatePreviousTabCommand() {
|
||||
const activeMainNtxId = this.getActiveMainContext()?.ntxId;
|
||||
if (!activeMainNtxId) return;
|
||||
const activeMainNtxId = this.getActiveMainContext().ntxId;
|
||||
|
||||
const oldIdx = this.mainNoteContexts.findIndex((nc) => nc.ntxId === activeMainNtxId);
|
||||
const newActiveNtxId = this.mainNoteContexts[oldIdx === 0 ? this.mainNoteContexts.length - 1 : oldIdx - 1].ntxId;
|
||||
@ -550,13 +503,12 @@ export default class TabManager extends Component {
|
||||
}
|
||||
|
||||
async closeActiveTabCommand() {
|
||||
if (this.activeNtxId) {
|
||||
await this.removeNoteContext(this.activeNtxId);
|
||||
}
|
||||
await this.removeNoteContext(this.activeNtxId);
|
||||
}
|
||||
|
||||
beforeUnloadEvent(): boolean {
|
||||
beforeUnloadEvent() {
|
||||
this.tabsUpdate.updateNowIfNecessary();
|
||||
|
||||
return true; // don't block closing the tab, this metadata is not that important
|
||||
}
|
||||
|
||||
@ -566,39 +518,35 @@ export default class TabManager extends Component {
|
||||
|
||||
async closeAllTabsCommand() {
|
||||
for (const ntxIdToRemove of this.mainNoteContexts.map((nc) => nc.ntxId)) {
|
||||
if (ntxIdToRemove) {
|
||||
await this.removeNoteContext(ntxIdToRemove);
|
||||
}
|
||||
await this.removeNoteContext(ntxIdToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
async closeOtherTabsCommand({ ntxId }: { ntxId: string }) {
|
||||
async closeOtherTabsCommand({ ntxId }) {
|
||||
for (const ntxIdToRemove of this.mainNoteContexts.map((nc) => nc.ntxId)) {
|
||||
if (ntxIdToRemove && ntxIdToRemove !== ntxId) {
|
||||
if (ntxIdToRemove !== ntxId) {
|
||||
await this.removeNoteContext(ntxIdToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async closeRightTabsCommand({ ntxId }: { ntxId: string }) {
|
||||
async closeRightTabsCommand({ ntxId }) {
|
||||
const ntxIds = this.mainNoteContexts.map((nc) => nc.ntxId);
|
||||
const index = ntxIds.indexOf(ntxId);
|
||||
|
||||
if (index !== -1) {
|
||||
const idsToRemove = ntxIds.slice(index + 1);
|
||||
for (const ntxIdToRemove of idsToRemove) {
|
||||
if (ntxIdToRemove) {
|
||||
await this.removeNoteContext(ntxIdToRemove);
|
||||
}
|
||||
await this.removeNoteContext(ntxIdToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async closeTabCommand({ ntxId }: { ntxId: string }) {
|
||||
async closeTabCommand({ ntxId }) {
|
||||
await this.removeNoteContext(ntxId);
|
||||
}
|
||||
|
||||
async moveTabToNewWindowCommand({ ntxId }: { ntxId: string }) {
|
||||
async moveTabToNewWindowCommand({ ntxId }) {
|
||||
const { notePath, hoistedNoteId } = this.getNoteContextById(ntxId);
|
||||
|
||||
const removed = await this.removeNoteContext(ntxId);
|
||||
@ -608,16 +556,17 @@ export default class TabManager extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
async copyTabToNewWindowCommand({ ntxId }: { ntxId: string }) {
|
||||
async copyTabToNewWindowCommand({ ntxId }) {
|
||||
const { notePath, hoistedNoteId } = this.getNoteContextById(ntxId);
|
||||
this.triggerCommand("openInWindow", { notePath, hoistedNoteId });
|
||||
}
|
||||
|
||||
async reopenLastTabCommand() {
|
||||
const closeLastEmptyTab: NoteContext | undefined = await this.mutex.runExclusively(async () => {
|
||||
let closeLastEmptyTab
|
||||
let closeLastEmptyTab = null;
|
||||
|
||||
await this.mutex.runExclusively(async () => {
|
||||
if (this.recentlyClosedTabs.length === 0) {
|
||||
return closeLastEmptyTab;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.noteContexts.length === 1 && this.noteContexts[0].isEmpty()) {
|
||||
@ -626,8 +575,6 @@ export default class TabManager extends Component {
|
||||
}
|
||||
|
||||
const lastClosedTab = this.recentlyClosedTabs.pop();
|
||||
if (!lastClosedTab) return closeLastEmptyTab;
|
||||
|
||||
const noteContexts = lastClosedTab.contexts;
|
||||
|
||||
for (const noteContext of noteContexts) {
|
||||
@ -642,26 +589,25 @@ export default class TabManager extends Component {
|
||||
...this.noteContexts.slice(-noteContexts.length),
|
||||
...this.noteContexts.slice(lastClosedTab.position, -noteContexts.length)
|
||||
];
|
||||
this.noteContextReorderEvent({ ntxIdsInOrder: ntxsInOrder.map((nc) => nc.ntxId).filter((id) => id !== null) });
|
||||
await this.noteContextReorderEvent({ ntxIdsInOrder: ntxsInOrder.map((nc) => nc.ntxId) });
|
||||
|
||||
let mainNtx = noteContexts.find((nc) => nc.isMainContext());
|
||||
if (mainNtx) {
|
||||
// reopened a tab, need to reorder new tab widget in tab row
|
||||
await this.triggerEvent("contextsReopenedEvent", {
|
||||
await this.triggerEvent("contextsReopened", {
|
||||
mainNtxId: mainNtx.ntxId,
|
||||
tabPosition: ntxsInOrder.filter((nc) => nc.isMainContext()).findIndex((nc) => nc.ntxId === mainNtx.ntxId)
|
||||
});
|
||||
} else {
|
||||
// reopened a single split, need to reorder the pane widget in split note container
|
||||
await this.triggerEvent("contextsReopenedEvent", {
|
||||
mainNtxId: ntxsInOrder[lastClosedTab.position].ntxId,
|
||||
await this.triggerEvent("contextsReopened", {
|
||||
ntxId: ntxsInOrder[lastClosedTab.position].ntxId,
|
||||
// this is safe since lastClosedTab.position can never be 0 in this case
|
||||
tabPosition: lastClosedTab.position - 1
|
||||
afterNtxId: ntxsInOrder[lastClosedTab.position - 1].ntxId
|
||||
});
|
||||
}
|
||||
|
||||
const noteContextToActivate = noteContexts.length === 1 ? noteContexts[0] : noteContexts.find((nc) => nc.isMainContext());
|
||||
if (!noteContextToActivate) return closeLastEmptyTab;
|
||||
|
||||
await this.activateNoteContext(noteContextToActivate.ntxId);
|
||||
|
||||
@ -669,7 +615,6 @@ export default class TabManager extends Component {
|
||||
noteContext: noteContextToActivate,
|
||||
notePath: noteContextToActivate.notePath
|
||||
});
|
||||
return closeLastEmptyTab;
|
||||
});
|
||||
|
||||
if (closeLastEmptyTab) {
|
||||
@ -681,9 +626,7 @@ export default class TabManager extends Component {
|
||||
this.tabsUpdate.scheduleUpdate();
|
||||
}
|
||||
|
||||
async updateDocumentTitle(activeNoteContext: NoteContext | null) {
|
||||
if (!activeNoteContext) return;
|
||||
|
||||
async updateDocumentTitle(activeNoteContext) {
|
||||
const titleFragments = [
|
||||
// it helps to navigate in history if note title is included in the title
|
||||
await activeNoteContext.getNavigationTitle(),
|
||||
@ -693,7 +636,7 @@ export default class TabManager extends Component {
|
||||
document.title = titleFragments.join(" - ");
|
||||
}
|
||||
|
||||
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||
async entitiesReloadedEvent({ loadResults }) {
|
||||
const activeContext = this.getActiveContext();
|
||||
|
||||
if (activeContext && loadResults.isNoteReloaded(activeContext.noteId)) {
|
||||
@ -703,6 +646,7 @@ export default class TabManager extends Component {
|
||||
|
||||
async frocaReloadedEvent() {
|
||||
const activeContext = this.getActiveContext();
|
||||
|
||||
if (activeContext) {
|
||||
await this.updateDocumentTitle(activeContext);
|
||||
}
|
@ -22,19 +22,13 @@ function getItems(): MenuItem<CommandNames>[] {
|
||||
|
||||
function handleLinkContextMenuItem(command: string | undefined, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
|
||||
if (!hoistedNoteId) {
|
||||
hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId ?? null;
|
||||
hoistedNoteId = appContext.tabManager.getActiveContext().hoistedNoteId;
|
||||
}
|
||||
|
||||
if (command === "openNoteInNewTab") {
|
||||
appContext.tabManager.openContextWithNote(notePath, { hoistedNoteId, viewScope });
|
||||
} else if (command === "openNoteInNewSplit") {
|
||||
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
||||
|
||||
if (!subContexts) {
|
||||
logError("subContexts is null");
|
||||
return;
|
||||
}
|
||||
|
||||
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
|
||||
const { ntxId } = subContexts[subContexts.length - 1];
|
||||
|
||||
appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope });
|
||||
|
@ -288,15 +288,11 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent
|
||||
|
||||
const noteContext = ntxId ? appContext.tabManager.getNoteContextById(ntxId) : appContext.tabManager.getActiveContext();
|
||||
|
||||
if (noteContext) {
|
||||
noteContext.setNote(notePath, { viewScope }).then(() => {
|
||||
if (noteContext !== appContext.tabManager.getActiveContext()) {
|
||||
appContext.tabManager.activateNoteContext(noteContext.ntxId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
appContext.tabManager.openContextWithNote(notePath, { viewScope, activate: true });
|
||||
}
|
||||
noteContext.setNote(notePath, { viewScope }).then(() => {
|
||||
if (noteContext !== appContext.tabManager.getActiveContext()) {
|
||||
appContext.tabManager.activateNoteContext(noteContext.ntxId);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (hrefLink) {
|
||||
const withinEditLink = $link?.hasClass("ck-link-actions__preview");
|
||||
|
@ -138,7 +138,7 @@ function getParentProtectedStatus(node: Fancytree.FancytreeNode) {
|
||||
return hoistedNoteService.isHoistedNode(node) ? false : node.getParent().data.isProtected;
|
||||
}
|
||||
|
||||
function getNoteIdFromUrl(urlOrNotePath: string | null | undefined) {
|
||||
function getNoteIdFromUrl(urlOrNotePath: string | undefined) {
|
||||
if (!urlOrNotePath) {
|
||||
return null;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ export default class Mutex {
|
||||
return newPromise;
|
||||
}
|
||||
|
||||
async runExclusively(cb: () => Promise<any>) {
|
||||
async runExclusively(cb: () => Promise<void>) {
|
||||
const unlock = await this.lock();
|
||||
|
||||
try {
|
||||
|
@ -63,7 +63,7 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
||||
hoistedNoteId?: string;
|
||||
viewScope?: any;
|
||||
}) {
|
||||
const mainNtxId = appContext.tabManager.getActiveMainContext()?.ntxId;
|
||||
const mainNtxId = appContext.tabManager.getActiveMainContext().ntxId;
|
||||
|
||||
if (!mainNtxId) {
|
||||
logError("empty mainNtxId!");
|
||||
@ -76,7 +76,7 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
||||
ntxId = mainNtxId;
|
||||
}
|
||||
|
||||
hoistedNoteId = hoistedNoteId || appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||
hoistedNoteId = hoistedNoteId || appContext.tabManager.getActiveContext().hoistedNoteId;
|
||||
|
||||
const noteContext = await appContext.tabManager.openEmptyTab(null, hoistedNoteId, mainNtxId);
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import Draggabilly, { type MoveVector } from "draggabilly";
|
||||
import Draggabilly, { type DraggabillyCallback, type MoveVector } from "draggabilly";
|
||||
import { t } from "../services/i18n.js";
|
||||
import BasicWidget from "./basic_widget.js";
|
||||
import contextMenu from "../menus/context_menu.js";
|
||||
import utils from "../services/utils.js";
|
||||
import keyboardActionService from "../services/keyboard_actions.js";
|
||||
import appContext, { type CommandListenerData, type EventData } from "../components/app_context.js";
|
||||
import appContext, { type CommandData, type CommandListenerData, type EventData } from "../components/app_context.js";
|
||||
import froca from "../services/froca.js";
|
||||
import attributeService from "../services/attributes.js";
|
||||
import type NoteContext from "../components/note_context.js";
|
||||
@ -419,13 +419,13 @@ export default class TabRowWidget extends BasicWidget {
|
||||
closeActiveTabCommand({ $el }: CommandListenerData<"closeActiveTab">) {
|
||||
const ntxId = $el.closest(".note-tab").attr("data-ntx-id");
|
||||
|
||||
appContext.tabManager.removeNoteContext(ntxId ?? null);
|
||||
appContext.tabManager.removeNoteContext(ntxId);
|
||||
}
|
||||
|
||||
setTabCloseEvent($tab: JQuery<HTMLElement>) {
|
||||
$tab.on("mousedown", (e) => {
|
||||
if (e.which === 2) {
|
||||
appContext.tabManager.removeNoteContext($tab.attr("data-ntx-id") ?? null);
|
||||
appContext.tabManager.removeNoteContext($tab.attr("data-ntx-id"));
|
||||
|
||||
return true; // event has been handled
|
||||
}
|
||||
@ -494,7 +494,7 @@ export default class TabRowWidget extends BasicWidget {
|
||||
return $tab.attr("data-ntx-id");
|
||||
}
|
||||
|
||||
noteContextRemovedEvent({ ntxIds }: EventData<"noteContextRemoved">) {
|
||||
noteContextRemovedEvent({ ntxIds }: EventData<"noteContextRemovedEvent">) {
|
||||
for (const ntxId of ntxIds) {
|
||||
this.removeTab(ntxId);
|
||||
}
|
||||
@ -516,7 +516,7 @@ export default class TabRowWidget extends BasicWidget {
|
||||
this.draggabillyDragging.element.style.transform = "";
|
||||
this.draggabillyDragging.dragEnd();
|
||||
this.draggabillyDragging.isDragging = false;
|
||||
this.draggabillyDragging.positionDrag = () => { }; // Prevent Draggabilly from updating tabEl.style.transform in later frames
|
||||
this.draggabillyDragging.positionDrag = () => {}; // Prevent Draggabilly from updating tabEl.style.transform in later frames
|
||||
this.draggabillyDragging.destroy();
|
||||
this.draggabillyDragging = null;
|
||||
}
|
||||
@ -650,7 +650,7 @@ export default class TabRowWidget extends BasicWidget {
|
||||
}
|
||||
|
||||
contextsReopenedEvent({ mainNtxId, tabPosition }: EventData<"contextsReopenedEvent">) {
|
||||
if (!mainNtxId || !tabPosition) {
|
||||
if (mainNtxId === undefined || tabPosition === undefined) {
|
||||
// no tab reopened
|
||||
return;
|
||||
}
|
||||
@ -748,7 +748,7 @@ export default class TabRowWidget extends BasicWidget {
|
||||
hoistedNoteChangedEvent({ ntxId }: EventData<"hoistedNoteChanged">) {
|
||||
const $tab = this.getTabById(ntxId);
|
||||
|
||||
if ($tab && ntxId) {
|
||||
if ($tab) {
|
||||
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
||||
|
||||
this.updateTab($tab, noteContext);
|
||||
|
@ -541,10 +541,6 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
||||
excalidrawAPI: (api: ExcalidrawImperativeAPI) => {
|
||||
this.excalidrawApi = api;
|
||||
},
|
||||
onPaste: (data: unknown, event: unknown) => {
|
||||
console.log("Verbose: excalidraw internal paste. No trilium action implemented.", data, event);
|
||||
return false;
|
||||
},
|
||||
onLibraryChange: () => {
|
||||
this.libraryChanged = true;
|
||||
|
||||
|
@ -102,13 +102,20 @@ export const DEFAULT_ALLOWED_TAGS = [
|
||||
];
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<div class="html-import-tags-settings options-section">
|
||||
<style>
|
||||
.html-import-tags-settings .allowed-html-tags {
|
||||
height: 150px;
|
||||
margin-bottom: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
<h4>${t("import.html_import_tags.title")}</h4>
|
||||
|
||||
<p>${t("import.html_import_tags.description")}</p>
|
||||
|
||||
<textarea class="allowed-html-tags form-control" style="height: 150px; font-family: monospace;"
|
||||
placeholder="${t("import.html_import_tags.placeholder")}"></textarea>
|
||||
<textarea class="allowed-html-tags form-control" spellcheck="false"
|
||||
placeholder="${t("import.html_import_tags.placeholder")}"></textarea>
|
||||
|
||||
<div>
|
||||
<button class="btn btn-sm btn-secondary reset-to-default">
|
||||
|
@ -165,6 +165,11 @@ span[style] {
|
||||
overflow: unset !important;
|
||||
}
|
||||
|
||||
/* TODO: This will break once we translate the language */
|
||||
.ck-content pre[data-language="Auto-detected"]:after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*
|
||||
* Code note specific fixes.
|
||||
*/
|
||||
@ -300,6 +305,8 @@ blockquote {
|
||||
pre > code {
|
||||
widows: 6;
|
||||
orphans: 6;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
|
||||
h1,
|
||||
|
@ -140,6 +140,7 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
|
||||
|
||||
.find-replace-widget {
|
||||
container-type: inline-size;
|
||||
border-top: 3px solid var(--root-background) !important;
|
||||
}
|
||||
|
||||
.find-replace-widget > div {
|
||||
|
@ -80,6 +80,35 @@ div.editability-dropdown a.dropdown-item {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/* Narrow width layout */
|
||||
.note-info-widget {
|
||||
container: note-info / inline-size;
|
||||
}
|
||||
|
||||
@container note-info (max-width: 800px) {
|
||||
table, tbody, tr, td, th {
|
||||
display: block;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
tbody {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
tr {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
th {
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
td {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attribute detail dialog
|
||||
*/
|
||||
|
@ -201,6 +201,22 @@ body.layout-horizontal > .horizontal {
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
@keyframes sync-icon-animation {
|
||||
from {
|
||||
transform: rotate(360deg);
|
||||
} to {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sync in progres / unknown sync status icon */
|
||||
#launcher-pane .sync-status-icon.sync-status-in-progress::before,
|
||||
#launcher-pane .sync-status-icon.sync-status-unknown::before {
|
||||
display: block;
|
||||
content: "\ec37";
|
||||
animation: sync-icon-animation 2s linear infinite;
|
||||
}
|
||||
|
||||
#launcher-pane .sync-status-icon:not(.sync-status-in-progress):hover {
|
||||
background: unset;
|
||||
}
|
||||
@ -876,7 +892,6 @@ body.layout-horizontal .tab-row-widget .note-tab .note-tab-wrapper {
|
||||
*/
|
||||
|
||||
#center-pane {
|
||||
padding-top: 2px;
|
||||
background: var(--main-background-color);
|
||||
}
|
||||
|
||||
@ -899,6 +914,24 @@ body.mobile .note-title {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.note-split:not(.hidden-ext) + .note-split:not(.hidden-ext) {
|
||||
border-left: 4px solid var(--root-background);
|
||||
}
|
||||
|
||||
@keyframes note-entrance {
|
||||
from {
|
||||
opacity: 0;
|
||||
} to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.note-split {
|
||||
padding-top: 2px;
|
||||
animation: note-entrance 100ms linear;
|
||||
/* will-change: opacity; -- causes some weird artifacts to the note menu in split view */
|
||||
}
|
||||
|
||||
/*
|
||||
* Table of contents & Highlights list
|
||||
*/
|
||||
@ -1674,7 +1707,9 @@ div.bookmark-folder-widget .note-link .bx {
|
||||
transform: translateY(4%);
|
||||
}
|
||||
|
||||
/* Search */
|
||||
/*
|
||||
* Search
|
||||
*/
|
||||
|
||||
.search-result-widget-content .note-path .path-bracket {
|
||||
display: inline;
|
||||
@ -1685,7 +1720,51 @@ div.bookmark-folder-widget .note-link .bx {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* Help */
|
||||
/*
|
||||
* SQL Console
|
||||
*/
|
||||
|
||||
/* Table buttons */
|
||||
|
||||
.sql-table-schemas-widget .sql-table-schemas button {
|
||||
--color: var(--main-text-color);
|
||||
--background: var(--card-background-color);
|
||||
|
||||
display: inline-block;
|
||||
box-shadow: 2px 2px 2px var(--card-shadow-color);
|
||||
margin-top: 4px;
|
||||
vertical-align: baseline;
|
||||
border: unset;
|
||||
border-radius: 12px;
|
||||
padding: 2px 12px;
|
||||
background: var(--background) !important;
|
||||
color: var(--color) !important;
|
||||
line-height: unset;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.sql-table-schemas-widget .sql-table-schemas button:hover,
|
||||
.sql-table-schemas-widget .sql-table-schemas button:active,
|
||||
.sql-table-schemas-widget .sql-table-schemas button:focus-visible {
|
||||
--background: var(--card-background-press-color);
|
||||
--color: var(--main-text-color);
|
||||
}
|
||||
|
||||
/* Tooltip */
|
||||
|
||||
.tooltip .table-schema {
|
||||
font-family: var(--monospace-font-family);
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
/* Data type */
|
||||
.tooltip .table-schema td:nth-child(2) {
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
|
||||
/*
|
||||
* Help
|
||||
*/
|
||||
|
||||
.help-dialog .modal-content {
|
||||
--modal-background-color: var(--help-background-color);
|
||||
@ -1957,4 +2036,4 @@ div.promoted-attribute-cell .multiplicity:has(span) {
|
||||
margin-left: 8px;
|
||||
margin-right: calc(var(--pa-card-padding-left) - var(--pa-card-padding-right));
|
||||
font-size: 0; /* Prevent whitespaces creating a gap between buttons */
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user