mirror of
https://github.com/zadam/trilium.git
synced 2025-11-01 19:05:59 +01:00
Merge remote-tracking branch 'origin/develop' into feature/rtl
; Conflicts: ; src/public/app/widgets/floating_buttons/help_button.ts
This commit is contained in:
@@ -3,3 +3,4 @@
|
|||||||
*.yml
|
*.yml
|
||||||
libraries/*
|
libraries/*
|
||||||
docs/*
|
docs/*
|
||||||
|
src/public/app/doc_notes/**/*
|
||||||
@@ -66,8 +66,6 @@ chmod 755 $PKG_DIR/trilium.sh
|
|||||||
cp bin/tpl/anonymize-database.sql $PKG_DIR/
|
cp bin/tpl/anonymize-database.sql $PKG_DIR/
|
||||||
|
|
||||||
cp -r translations $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`
|
VERSION=`jq -r ".version" package.json`
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
import swaggerJsdoc from 'swagger-jsdoc';
|
import swaggerJsdoc from "swagger-jsdoc";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -12,27 +12,29 @@ import fs from "fs";
|
|||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
definition: {
|
definition: {
|
||||||
openapi: '3.1.1',
|
openapi: "3.1.1",
|
||||||
info: {
|
info: {
|
||||||
title: 'Trilium Notes - Sync server API',
|
title: "Trilium Notes - Sync server API",
|
||||||
version: '0.96.6',
|
version: "0.96.6",
|
||||||
description: "This is the internal sync server API used by Trilium Notes / TriliumNext Notes.\n\n_If you're looking for the officially supported External Trilium API, see [here](https://triliumnext.github.io/Docs/Wiki/etapi.html)._\n\nThis page does not yet list all routes. For a full list, see the [route controller](https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/routes.ts).",
|
description:
|
||||||
|
"This is the internal sync server API used by Trilium Notes / TriliumNext Notes.\n\n_If you're looking for the officially supported External Trilium API, see [here](https://triliumnext.github.io/Docs/Wiki/etapi.html)._\n\nThis page does not yet list all routes. For a full list, see the [route controller](https://github.com/TriliumNext/Notes/blob/v0.91.6/src/routes/routes.ts).",
|
||||||
contact: {
|
contact: {
|
||||||
name: "TriliumNext issue tracker",
|
name: "TriliumNext issue tracker",
|
||||||
url: "https://github.com/TriliumNext/Notes/issues",
|
url: "https://github.com/TriliumNext/Notes/issues"
|
||||||
},
|
},
|
||||||
license: {
|
license: {
|
||||||
name: "GNU Free Documentation License 1.3 (or later)",
|
name: "GNU Free Documentation License 1.3 (or later)",
|
||||||
url: "https://www.gnu.org/licenses/fdl-1.3",
|
url: "https://www.gnu.org/licenses/fdl-1.3"
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
apis: [
|
apis: [
|
||||||
// Put individual files here to have them ordered first.
|
// Put individual files here to have them ordered first.
|
||||||
'./src/routes/api/setup.ts',
|
"./src/routes/api/setup.ts",
|
||||||
// all other files
|
// all other files
|
||||||
'./src/routes/api/*.ts', './bin/generate-openapi.js'
|
"./src/routes/api/*.ts",
|
||||||
],
|
"./bin/generate-openapi.js"
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const openapiSpecification = swaggerJsdoc(options);
|
const openapiSpecification = swaggerJsdoc(options);
|
||||||
|
|||||||
@@ -11,16 +11,14 @@ test("Displays translation on desktop", async ({ page, context }) => {
|
|||||||
const app = new App(page, context);
|
const app = new App(page, context);
|
||||||
await app.goto();
|
await app.goto();
|
||||||
|
|
||||||
await expect(page.locator("#left-pane .quick-search input"))
|
await expect(page.locator("#left-pane .quick-search input")).toHaveAttribute("placeholder", "Quick search");
|
||||||
.toHaveAttribute("placeholder", "Quick search");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Displays translation on mobile", async ({ page, context }) => {
|
test("Displays translation on mobile", async ({ page, context }) => {
|
||||||
const app = new App(page, context);
|
const app = new App(page, context);
|
||||||
await app.goto({ isMobile: true });
|
await app.goto({ isMobile: true });
|
||||||
|
|
||||||
await expect(page.locator("#mobile-sidebar-wrapper .quick-search input"))
|
await expect(page.locator("#mobile-sidebar-wrapper .quick-search input")).toHaveAttribute("placeholder", "Quick search");
|
||||||
.toHaveAttribute("placeholder", "Quick search");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Displays translations in Settings", async ({ page, context }) => {
|
test("Displays translations in Settings", async ({ page, context }) => {
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ test("Can drag tabs around", async ({ page, context }) => {
|
|||||||
let tab = app.getTab(0);
|
let tab = app.getTab(0);
|
||||||
|
|
||||||
// Drag the first tab at the end
|
// Drag the first tab at the end
|
||||||
await tab.dragTo(app.getTab(2), { targetPosition: { x: 50, y: 0 }});
|
await tab.dragTo(app.getTab(2), { targetPosition: { x: 50, y: 0 } });
|
||||||
|
|
||||||
tab = app.getTab(2);
|
tab = app.getTab(2);
|
||||||
await expect(tab).toContainText(NOTE_TITLE);
|
await expect(tab).toContainText(NOTE_TITLE);
|
||||||
|
|
||||||
// Drag the tab to the left
|
// Drag the tab to the left
|
||||||
await tab.dragTo(app.getTab(0), { targetPosition: { x: 50, y: 0 }});
|
await tab.dragTo(app.getTab(0), { targetPosition: { x: 50, y: 0 } });
|
||||||
await expect(app.getTab(0)).toContainText(NOTE_TITLE);
|
await expect(app.getTab(0)).toContainText(NOTE_TITLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ test("Displays lint errors for backend script", async ({ page, context }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function expectTooltip(page: Page, tooltip: string) {
|
async function expectTooltip(page: Page, tooltip: string) {
|
||||||
await expect(page.locator(".CodeMirror-lint-tooltip:visible", {
|
await expect(
|
||||||
"hasText": tooltip
|
page.locator(".CodeMirror-lint-tooltip:visible", {
|
||||||
})).toBeVisible();
|
hasText: tooltip
|
||||||
|
})
|
||||||
|
).toBeVisible();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import App from "../support/app";
|
|||||||
|
|
||||||
test("renders ELK flowchart", async ({ page, context }) => {
|
test("renders ELK flowchart", async ({ page, context }) => {
|
||||||
await testAriaSnapshot({
|
await testAriaSnapshot({
|
||||||
page, context,
|
page,
|
||||||
|
context,
|
||||||
noteTitle: "Flowchart ELK on",
|
noteTitle: "Flowchart ELK on",
|
||||||
snapshot: `
|
snapshot: `
|
||||||
- document:
|
- document:
|
||||||
@@ -22,12 +23,13 @@ test("renders ELK flowchart", async ({ page, context }) => {
|
|||||||
- paragraph: Guarantee
|
- paragraph: Guarantee
|
||||||
- text: Interfaces for B
|
- text: Interfaces for B
|
||||||
`
|
`
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("renders standard flowchart", async ({ page, context }) => {
|
test("renders standard flowchart", async ({ page, context }) => {
|
||||||
await testAriaSnapshot({
|
await testAriaSnapshot({
|
||||||
page, context,
|
page,
|
||||||
|
context,
|
||||||
noteTitle: "Flowchart ELK off",
|
noteTitle: "Flowchart ELK off",
|
||||||
snapshot: `
|
snapshot: `
|
||||||
- document:
|
- document:
|
||||||
@@ -46,7 +48,7 @@ test("renders standard flowchart", async ({ page, context }) => {
|
|||||||
- paragraph: C
|
- paragraph: C
|
||||||
- text: Interfaces for B
|
- text: Interfaces for B
|
||||||
`
|
`
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
interface AriaTestOpts {
|
interface AriaTestOpts {
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ test("Highlights list is displayed", async ({ page, context }) => {
|
|||||||
|
|
||||||
await expect(app.sidebar).toContainText("Highlights List");
|
await expect(app.sidebar).toContainText("Highlights List");
|
||||||
const rootList = app.sidebar.locator(".highlights-list ol");
|
const rootList = app.sidebar.locator(".highlights-list ol");
|
||||||
let index=0;
|
let index = 0;
|
||||||
for (const highlightedEl of [ "Bold 1", "Italic 1", "Underline 1", "Colored text 1", "Background text 1", "Bold 2", "Italic 2", "Underline 2", "Colored text 2", "Background text 2" ]) {
|
for (const highlightedEl of ["Bold 1", "Italic 1", "Underline 1", "Colored text 1", "Background text 1", "Bold 2", "Italic 2", "Underline 2", "Colored text 2", "Background text 2"]) {
|
||||||
await expect(rootList.locator("li").nth(index++)).toContainText(highlightedEl);
|
await expect(rootList.locator("li").nth(index++)).toContainText(highlightedEl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -54,7 +54,7 @@ test("Displays math popup", async ({ page, context }) => {
|
|||||||
const app = new App(page, context);
|
const app = new App(page, context);
|
||||||
await app.goto();
|
await app.goto();
|
||||||
await app.goToNoteInNewTab("Empty text");
|
await app.goToNoteInNewTab("Empty text");
|
||||||
const noteContent = app.currentNoteSplit.locator(".note-detail-editable-text-editor")
|
const noteContent = app.currentNoteSplit.locator(".note-detail-editable-text-editor");
|
||||||
await noteContent.fill("Hello world");
|
await noteContent.fill("Hello world");
|
||||||
await noteContent.press("ControlOrMeta+M");
|
await noteContent.press("ControlOrMeta+M");
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default class App {
|
|||||||
this.tabBar = page.locator(".tab-row-widget-container");
|
this.tabBar = page.locator(".tab-row-widget-container");
|
||||||
this.noteTree = page.locator(".tree-wrapper");
|
this.noteTree = page.locator(".tree-wrapper");
|
||||||
this.launcherBar = page.locator("#launcher-container");
|
this.launcherBar = page.locator("#launcher-container");
|
||||||
this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)")
|
this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)");
|
||||||
this.sidebar = page.locator("#right-pane");
|
this.sidebar = page.locator("#right-pane");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,8 +46,7 @@ export default class App {
|
|||||||
|
|
||||||
// Wait for the page to load.
|
// Wait for the page to load.
|
||||||
if (url === "/") {
|
if (url === "/") {
|
||||||
await expect(this.page.locator(".tree"))
|
await expect(this.page.locator(".tree")).toContainText("Trilium Integration Test");
|
||||||
.toContainText("Trilium Integration Test");
|
|
||||||
await this.closeAllTabs();
|
await this.closeAllTabs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,11 +108,12 @@ export default class App {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(csrfToken).toBeTruthy();
|
expect(csrfToken).toBeTruthy();
|
||||||
await expect(await this.page.request.put(`${BASE_URL}/api/options/${key}/${value}`, {
|
await expect(
|
||||||
|
await this.page.request.put(`${BASE_URL}/api/options/${key}/${value}`, {
|
||||||
headers: {
|
headers: {
|
||||||
"x-csrf-token": csrfToken
|
"x-csrf-token": csrfToken
|
||||||
}
|
}
|
||||||
})).toBeOK();
|
})
|
||||||
|
).toBeOK();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
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/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
901
package-lock.json
generated
901
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -57,6 +57,8 @@
|
|||||||
"dev:watch-dist": "tsx ./bin/watch-dist.ts",
|
"dev:watch-dist": "tsx ./bin/watch-dist.ts",
|
||||||
"dev:prettier-check": "prettier . --check",
|
"dev:prettier-check": "prettier . --check",
|
||||||
"dev:prettier-fix": "prettier . --write",
|
"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:update-build-info": "tsx bin/update-build-info.ts",
|
||||||
"chore:ci-update-nightly-version": "tsx ./bin/update-nightly-version.ts",
|
"chore:ci-update-nightly-version": "tsx ./bin/update-nightly-version.ts",
|
||||||
"chore:generate-document": "cross-env nodemon ./bin/generate_document.ts 1000",
|
"chore:generate-document": "cross-env nodemon ./bin/generate_document.ts 1000",
|
||||||
@@ -103,7 +105,6 @@
|
|||||||
"express-session": "1.18.1",
|
"express-session": "1.18.1",
|
||||||
"force-graph": "1.49.2",
|
"force-graph": "1.49.2",
|
||||||
"fs-extra": "11.3.0",
|
"fs-extra": "11.3.0",
|
||||||
"happy-dom": "17.1.8",
|
|
||||||
"helmet": "8.0.0",
|
"helmet": "8.0.0",
|
||||||
"html": "1.0.0",
|
"html": "1.0.0",
|
||||||
"html2plaintext": "2.1.4",
|
"html2plaintext": "2.1.4",
|
||||||
@@ -123,7 +124,6 @@
|
|||||||
"jsdom": "26.0.0",
|
"jsdom": "26.0.0",
|
||||||
"jsplumb": "2.15.6",
|
"jsplumb": "2.15.6",
|
||||||
"katex": "0.16.21",
|
"katex": "0.16.21",
|
||||||
"knockout": "3.5.1",
|
|
||||||
"leaflet": "1.9.4",
|
"leaflet": "1.9.4",
|
||||||
"leaflet-gpx": "2.1.2",
|
"leaflet-gpx": "2.1.2",
|
||||||
"mark.js": "8.11.1",
|
"mark.js": "8.11.1",
|
||||||
@@ -150,7 +150,6 @@
|
|||||||
"striptags": "3.2.0",
|
"striptags": "3.2.0",
|
||||||
"swagger-ui-express": "5.0.1",
|
"swagger-ui-express": "5.0.1",
|
||||||
"tmp": "0.2.3",
|
"tmp": "0.2.3",
|
||||||
"ts-loader": "9.5.2",
|
|
||||||
"turndown": "7.2.0",
|
"turndown": "7.2.0",
|
||||||
"unescape": "1.0.1",
|
"unescape": "1.0.1",
|
||||||
"vanilla-js-wheel-zoom": "9.0.4",
|
"vanilla-js-wheel-zoom": "9.0.4",
|
||||||
@@ -168,6 +167,7 @@
|
|||||||
"@electron-forge/maker-zip": "7.7.0",
|
"@electron-forge/maker-zip": "7.7.0",
|
||||||
"@electron-forge/plugin-auto-unpack-natives": "7.7.0",
|
"@electron-forge/plugin-auto-unpack-natives": "7.7.0",
|
||||||
"@electron/rebuild": "3.7.1",
|
"@electron/rebuild": "3.7.1",
|
||||||
|
"@eslint/js": "9.21.0",
|
||||||
"@playwright/test": "1.50.1",
|
"@playwright/test": "1.50.1",
|
||||||
"@popperjs/core": "2.11.8",
|
"@popperjs/core": "2.11.8",
|
||||||
"@types/archiver": "6.0.3",
|
"@types/archiver": "6.0.3",
|
||||||
@@ -216,14 +216,17 @@
|
|||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"css-loader": "7.1.2",
|
"css-loader": "7.1.2",
|
||||||
"electron": "34.3.0",
|
"electron": "34.3.0",
|
||||||
|
"eslint": "9.21.0",
|
||||||
"esm": "3.2.25",
|
"esm": "3.2.25",
|
||||||
|
"happy-dom": "17.2.2",
|
||||||
"i18next-http-backend": "3.0.2",
|
"i18next-http-backend": "3.0.2",
|
||||||
"jsdoc": "4.0.4",
|
"jsdoc": "4.0.4",
|
||||||
|
"knockout": "3.5.1",
|
||||||
"lorem-ipsum": "2.0.8",
|
"lorem-ipsum": "2.0.8",
|
||||||
"mini-css-extract-plugin": "2.9.2",
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"nodemon": "3.1.9",
|
"nodemon": "3.1.9",
|
||||||
"postcss-loader": "8.1.1",
|
"postcss-loader": "8.1.1",
|
||||||
"prettier": "3.5.2",
|
"prettier": "3.5.3",
|
||||||
"rcedit": "4.0.1",
|
"rcedit": "4.0.1",
|
||||||
"rimraf": "6.0.1",
|
"rimraf": "6.0.1",
|
||||||
"sass": "1.85.1",
|
"sass": "1.85.1",
|
||||||
@@ -231,10 +234,12 @@
|
|||||||
"split.js": "1.6.5",
|
"split.js": "1.6.5",
|
||||||
"supertest": "7.0.0",
|
"supertest": "7.0.0",
|
||||||
"swagger-jsdoc": "6.2.8",
|
"swagger-jsdoc": "6.2.8",
|
||||||
|
"ts-loader": "9.5.2",
|
||||||
"tslib": "2.8.1",
|
"tslib": "2.8.1",
|
||||||
"tsx": "4.19.3",
|
"tsx": "4.19.3",
|
||||||
"typedoc": "0.27.9",
|
"typedoc": "0.27.9",
|
||||||
"typescript": "5.8.2",
|
"typescript": "5.8.2",
|
||||||
|
"typescript-eslint": "8.26.0",
|
||||||
"vitest": "3.0.7",
|
"vitest": "3.0.7",
|
||||||
"webpack": "5.98.0",
|
"webpack": "5.98.0",
|
||||||
"webpack-cli": "6.0.1",
|
"webpack-cli": "6.0.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test';
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
const SERVER_URL = 'http://127.0.0.1:8082';
|
const SERVER_URL = "http://127.0.0.1:8082";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read environment variables from file.
|
* Read environment variables from file.
|
||||||
@@ -14,7 +14,7 @@ const SERVER_URL = 'http://127.0.0.1:8082';
|
|||||||
* See https://playwright.dev/docs/test-configuration.
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
testDir: './e2e',
|
testDir: "./e2e",
|
||||||
/* Run tests in files in parallel */
|
/* Run tests in files in parallel */
|
||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
@@ -24,22 +24,22 @@ export default defineConfig({
|
|||||||
/* Opt out of parallel tests on CI. */
|
/* Opt out of parallel tests on CI. */
|
||||||
workers: process.env.CI ? 1 : undefined,
|
workers: process.env.CI ? 1 : undefined,
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
reporter: 'html',
|
reporter: "html",
|
||||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
use: {
|
use: {
|
||||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
baseURL: SERVER_URL,
|
baseURL: SERVER_URL,
|
||||||
|
|
||||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
trace: 'on-first-retry',
|
trace: "on-first-retry"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
/* Configure projects for major browsers */
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'chromium',
|
name: "chromium",
|
||||||
use: { ...devices['Desktop Chrome'] },
|
use: { ...devices["Desktop Chrome"] }
|
||||||
},
|
}
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// name: 'firefox',
|
// name: 'firefox',
|
||||||
@@ -73,9 +73,11 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
|
|
||||||
/* Run your local dev server before starting the tests */
|
/* Run your local dev server before starting the tests */
|
||||||
webServer: !process.env.TRILIUM_DOCKER ? {
|
webServer: !process.env.TRILIUM_DOCKER
|
||||||
command: 'npm run test:integration-mem-db-dev',
|
? {
|
||||||
|
command: "npm run test:integration-mem-db-dev",
|
||||||
url: SERVER_URL,
|
url: SERVER_URL,
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI
|
||||||
} : undefined,
|
}
|
||||||
|
: undefined
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default class BTask extends AbstractBeccaEntity<BOption> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static get hashedProperties() {
|
static get hashedProperties() {
|
||||||
return [ "taskId", "parentNoteId", "title", "dueDate", "isDone", "isDeleted" ];
|
return ["taskId", "parentNoteId", "title", "dueDate", "isDone", "isDeleted"];
|
||||||
}
|
}
|
||||||
|
|
||||||
taskId?: string;
|
taskId?: string;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import type NoteTreeWidget from "../widgets/note_tree.js";
|
|||||||
import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js";
|
import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js";
|
||||||
import type { ContextMenuEvent } from "../menus/context_menu.js";
|
import type { ContextMenuEvent } from "../menus/context_menu.js";
|
||||||
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
|
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
|
||||||
|
import type EditableTextTypeWidget from "../widgets/type_widgets/editable_text.js";
|
||||||
|
|
||||||
interface Layout {
|
interface Layout {
|
||||||
getRootWidget: (appContext: AppContext) => RootWidget;
|
getRootWidget: (appContext: AppContext) => RootWidget;
|
||||||
@@ -62,7 +63,7 @@ export interface NoteCommandData extends CommandData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ExecuteCommandData<T> extends CommandData {
|
export interface ExecuteCommandData<T> extends CommandData {
|
||||||
resolve: (data: T) => void
|
resolve: (data: T) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,7 +71,7 @@ export interface ExecuteCommandData<T> extends CommandData {
|
|||||||
*/
|
*/
|
||||||
export type CommandMappings = {
|
export type CommandMappings = {
|
||||||
"api-log-messages": CommandData;
|
"api-log-messages": CommandData;
|
||||||
focusTree: CommandData,
|
focusTree: CommandData;
|
||||||
focusOnTitle: CommandData;
|
focusOnTitle: CommandData;
|
||||||
focusOnDetail: CommandData;
|
focusOnDetail: CommandData;
|
||||||
focusOnSearchDefinition: Required<CommandData>;
|
focusOnSearchDefinition: Required<CommandData>;
|
||||||
@@ -108,7 +109,7 @@ export type CommandMappings = {
|
|||||||
showInfoDialog: ConfirmWithMessageOptions;
|
showInfoDialog: ConfirmWithMessageOptions;
|
||||||
showConfirmDialog: ConfirmWithMessageOptions;
|
showConfirmDialog: ConfirmWithMessageOptions;
|
||||||
showRecentChanges: CommandData & { ancestorNoteId: string };
|
showRecentChanges: CommandData & { ancestorNoteId: string };
|
||||||
showImportDialog: CommandData & { noteId: string; };
|
showImportDialog: CommandData & { noteId: string };
|
||||||
openNewNoteSplit: NoteCommandData;
|
openNewNoteSplit: NoteCommandData;
|
||||||
openInWindow: NoteCommandData;
|
openInWindow: NoteCommandData;
|
||||||
openNoteInNewTab: CommandData;
|
openNoteInNewTab: CommandData;
|
||||||
@@ -131,8 +132,10 @@ export type CommandMappings = {
|
|||||||
editNoteTitle: ContextMenuCommandData;
|
editNoteTitle: ContextMenuCommandData;
|
||||||
protectSubtree: ContextMenuCommandData;
|
protectSubtree: ContextMenuCommandData;
|
||||||
unprotectSubtree: ContextMenuCommandData;
|
unprotectSubtree: ContextMenuCommandData;
|
||||||
openBulkActionsDialog: ContextMenuCommandData | {
|
openBulkActionsDialog:
|
||||||
selectedOrActiveNoteIds?: string[]
|
| ContextMenuCommandData
|
||||||
|
| {
|
||||||
|
selectedOrActiveNoteIds?: string[];
|
||||||
};
|
};
|
||||||
editBranchPrefix: ContextMenuCommandData;
|
editBranchPrefix: ContextMenuCommandData;
|
||||||
convertNoteToAttachment: ContextMenuCommandData;
|
convertNoteToAttachment: ContextMenuCommandData;
|
||||||
@@ -221,11 +224,11 @@ export type CommandMappings = {
|
|||||||
moveTabToNewWindow: CommandData;
|
moveTabToNewWindow: CommandData;
|
||||||
copyTabToNewWindow: CommandData;
|
copyTabToNewWindow: CommandData;
|
||||||
closeActiveTab: CommandData & {
|
closeActiveTab: CommandData & {
|
||||||
$el: JQuery<HTMLElement>
|
$el: JQuery<HTMLElement>;
|
||||||
},
|
};
|
||||||
setZoomFactorAndSave: {
|
setZoomFactorAndSave: {
|
||||||
zoomFactor: string;
|
zoomFactor: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
reEvaluateRightPaneVisibility: CommandData;
|
reEvaluateRightPaneVisibility: CommandData;
|
||||||
runActiveNote: CommandData;
|
runActiveNote: CommandData;
|
||||||
@@ -234,18 +237,18 @@ export type CommandMappings = {
|
|||||||
};
|
};
|
||||||
scrollToEnd: CommandData;
|
scrollToEnd: CommandData;
|
||||||
closeThisNoteSplit: CommandData;
|
closeThisNoteSplit: CommandData;
|
||||||
moveThisNoteSplit: CommandData & { isMovingLeft: boolean; };
|
moveThisNoteSplit: CommandData & { isMovingLeft: boolean };
|
||||||
|
|
||||||
// Geomap
|
// Geomap
|
||||||
deleteFromMap: { noteId: string },
|
deleteFromMap: { noteId: string };
|
||||||
openGeoLocation: { noteId: string, event: JQuery.MouseDownEvent }
|
openGeoLocation: { noteId: string; event: JQuery.MouseDownEvent };
|
||||||
|
|
||||||
toggleZenMode: CommandData;
|
toggleZenMode: CommandData;
|
||||||
|
|
||||||
updateAttributeList: CommandData & { attributes: Attribute[] };
|
updateAttributeList: CommandData & { attributes: Attribute[] };
|
||||||
saveAttributes: CommandData;
|
saveAttributes: CommandData;
|
||||||
reloadAttributes: CommandData;
|
reloadAttributes: CommandData;
|
||||||
refreshNoteList: CommandData & { noteId: string; };
|
refreshNoteList: CommandData & { noteId: string };
|
||||||
|
|
||||||
refreshResults: {};
|
refreshResults: {};
|
||||||
refreshSearchDefinition: {};
|
refreshSearchDefinition: {};
|
||||||
@@ -348,7 +351,7 @@ type EventMappings = {
|
|||||||
ntxId: string | null | undefined; // TODO: deduplicate ntxId
|
ntxId: string | null | undefined; // TODO: deduplicate ntxId
|
||||||
};
|
};
|
||||||
tabReorder: {
|
tabReorder: {
|
||||||
ntxIdsInOrder: string[]
|
ntxIdsInOrder: string[];
|
||||||
};
|
};
|
||||||
refreshNoteList: {
|
refreshNoteList: {
|
||||||
noteId: string;
|
noteId: string;
|
||||||
@@ -359,6 +362,12 @@ type EventMappings = {
|
|||||||
relationMapResetPanZoom: { ntxId: string | null | undefined };
|
relationMapResetPanZoom: { ntxId: string | null | undefined };
|
||||||
relationMapResetZoomIn: { ntxId: string | null | undefined };
|
relationMapResetZoomIn: { ntxId: string | null | undefined };
|
||||||
relationMapResetZoomOut: { ntxId: string | null | undefined };
|
relationMapResetZoomOut: { ntxId: string | null | undefined };
|
||||||
|
activeNoteChangedEvent: {};
|
||||||
|
showAddLinkDialog: {
|
||||||
|
textTypeWidget: EditableTextTypeWidget;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EventListener<T extends EventNames> = {
|
export type EventListener<T extends EventNames> = {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
|
|||||||
children: ChildT[];
|
children: ChildT[];
|
||||||
initialized: Promise<void> | null;
|
initialized: Promise<void> | null;
|
||||||
parent?: TypedComponent<any>;
|
parent?: TypedComponent<any>;
|
||||||
position!: number;
|
_position!: number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.componentId = `${this.sanitizedClassName}-${utils.randomString(8)}`;
|
this.componentId = `${this.sanitizedClassName}-${utils.randomString(8)}`;
|
||||||
@@ -31,6 +31,14 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> {
|
|||||||
return this.constructor.name.replace(/[^A-Z0-9]/gi, "_");
|
return this.constructor.name.replace(/[^A-Z0-9]/gi, "_");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get position() {
|
||||||
|
return this._position;
|
||||||
|
}
|
||||||
|
|
||||||
|
set position(newPosition: number) {
|
||||||
|
this._position = newPosition;
|
||||||
|
}
|
||||||
|
|
||||||
setParent(parent: TypedComponent<any>) {
|
setParent(parent: TypedComponent<any>) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default class ShortcutComponent extends Component implements EventListene
|
|||||||
}
|
}
|
||||||
|
|
||||||
bindNoteShortcutHandler(labelOrRow: AttributeRow) {
|
bindNoteShortcutHandler(labelOrRow: AttributeRow) {
|
||||||
const handler = () => appContext.tabManager.getActiveContext().setNote(labelOrRow.noteId);
|
const handler = () => appContext.tabManager.getActiveContext()?.setNote(labelOrRow.noteId);
|
||||||
const namespace = labelOrRow.attributeId;
|
const namespace = labelOrRow.attributeId;
|
||||||
|
|
||||||
if (labelOrRow.isDeleted) {
|
if (labelOrRow.isDeleted) {
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ export default class TabManager extends Component {
|
|||||||
await noteContext.setEmpty();
|
await noteContext.setEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
async openEmptyTab(ntxId = null, hoistedNoteId = "root", mainNtxId = null) {
|
async openEmptyTab(ntxId = null, hoistedNoteId = "root", mainNtxId) {
|
||||||
const noteContext = new NoteContext(ntxId, hoistedNoteId, mainNtxId);
|
const noteContext = new NoteContext(ntxId, hoistedNoteId, mainNtxId);
|
||||||
|
|
||||||
const existingNoteContext = this.children.find((nc) => nc.ntxId === noteContext.ntxId);
|
const existingNoteContext = this.children.find((nc) => nc.ntxId === noteContext.ntxId);
|
||||||
|
|||||||
@@ -37,7 +37,25 @@ const NOTE_TYPE_ICONS = {
|
|||||||
* end user. Those types should be used only for checking against, they are
|
* end user. Those types should be used only for checking against, they are
|
||||||
* not for direct use.
|
* not for direct use.
|
||||||
*/
|
*/
|
||||||
export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "geoMap" | "taskList";
|
export type NoteType =
|
||||||
|
| "file"
|
||||||
|
| "image"
|
||||||
|
| "search"
|
||||||
|
| "noteMap"
|
||||||
|
| "launcher"
|
||||||
|
| "doc"
|
||||||
|
| "contentWidget"
|
||||||
|
| "text"
|
||||||
|
| "relationMap"
|
||||||
|
| "render"
|
||||||
|
| "canvas"
|
||||||
|
| "mermaid"
|
||||||
|
| "book"
|
||||||
|
| "webView"
|
||||||
|
| "code"
|
||||||
|
| "mindMap"
|
||||||
|
| "geoMap"
|
||||||
|
| "taskList";
|
||||||
|
|
||||||
export interface NotePathRecord {
|
export interface NotePathRecord {
|
||||||
isArchived: boolean;
|
isArchived: boolean;
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ export default class DesktopLayout {
|
|||||||
.child(new InfoDialog())
|
.child(new InfoDialog())
|
||||||
.child(new ConfirmDialog())
|
.child(new ConfirmDialog())
|
||||||
.child(new PromptDialog())
|
.child(new PromptDialog())
|
||||||
.child(new CloseZenButton())
|
.child(new CloseZenButton());
|
||||||
}
|
}
|
||||||
|
|
||||||
#buildLauncherPane(isHorizontal) {
|
#buildLauncherPane(isHorizontal) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "../stylesheets/bootstrap.scss";
|
|||||||
// Required for correct loading of scripts in Electron
|
// Required for correct loading of scripts in Electron
|
||||||
if (typeof module === 'object') {window.module = module; module = undefined;}
|
if (typeof module === 'object') {window.module = module; module = undefined;}
|
||||||
|
|
||||||
const device = getDeviceType()
|
const device = getDeviceType();
|
||||||
console.log("Setting device cookie to:", device);
|
console.log("Setting device cookie to:", device);
|
||||||
setCookie("trilium-device", device);
|
setCookie("trilium-device", device);
|
||||||
|
|
||||||
@@ -16,17 +16,17 @@ function setCookie(name: string, value?: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getDeviceType() {
|
function getDeviceType() {
|
||||||
if (window.location.search === '?desktop') return "desktop";
|
if (window.location.search === "?desktop") return "desktop";
|
||||||
if (window.location.search === '?mobile') return "mobile";
|
if (window.location.search === "?mobile") return "mobile";
|
||||||
return isMobile() ? "mobile" : "desktop";
|
return isMobile() ? "mobile" : "desktop";
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/a/73731646/944162
|
// https://stackoverflow.com/a/73731646/944162
|
||||||
function isMobile() {
|
function isMobile() {
|
||||||
const mQ = matchMedia?.('(pointer:coarse)');
|
const mQ = matchMedia?.("(pointer:coarse)");
|
||||||
if (mQ?.media === '(pointer:coarse)') return !!mQ.matches;
|
if (mQ?.media === "(pointer:coarse)") return !!mQ.matches;
|
||||||
|
|
||||||
if ('orientation' in window) return true;
|
if ("orientation" in window) return true;
|
||||||
const userAgentsRegEx = /\b(Android|iPhone|iPad|iPod|Windows Phone|BlackBerry|webOS|IEMobile)\b/i
|
const userAgentsRegEx = /\b(Android|iPhone|iPad|iPod|Windows Phone|BlackBerry|webOS|IEMobile)\b/i;
|
||||||
return userAgentsRegEx.test(navigator.userAgent)
|
return userAgentsRegEx.test(navigator.userAgent);
|
||||||
}
|
}
|
||||||
@@ -34,8 +34,8 @@ export default class LauncherContextMenu implements SelectMenuItemEventListener<
|
|||||||
|
|
||||||
const isVisibleRoot = note?.noteId === "_lbVisibleLaunchers";
|
const isVisibleRoot = note?.noteId === "_lbVisibleLaunchers";
|
||||||
const isAvailableRoot = note?.noteId === "_lbAvailableLaunchers";
|
const isAvailableRoot = note?.noteId === "_lbAvailableLaunchers";
|
||||||
const isVisibleItem = (parentNoteId === "_lbVisibleLaunchers" || parentNoteId === "_lbMobileVisibleLaunchers");
|
const isVisibleItem = parentNoteId === "_lbVisibleLaunchers" || parentNoteId === "_lbMobileVisibleLaunchers";
|
||||||
const isAvailableItem = (parentNoteId === "_lbAvailableLaunchers" || parentNoteId === "_lbMobileAvailableLaunchers");
|
const isAvailableItem = parentNoteId === "_lbAvailableLaunchers" || parentNoteId === "_lbMobileAvailableLaunchers";
|
||||||
const isItem = isVisibleItem || isAvailableItem;
|
const isItem = isVisibleItem || isAvailableItem;
|
||||||
const canBeDeleted = !note?.noteId.startsWith("_"); // fixed notes can't be deleted
|
const canBeDeleted = !note?.noteId.startsWith("_"); // fixed notes can't be deleted
|
||||||
const canBeReset = !canBeDeleted && note?.isLaunchBarConfig();
|
const canBeReset = !canBeDeleted && note?.isLaunchBarConfig();
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
|
|||||||
const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null;
|
const note = this.node.data.noteId ? await froca.getNote(this.node.data.noteId) : null;
|
||||||
const branch = froca.getBranch(this.node.data.branchId);
|
const branch = froca.getBranch(this.node.data.branchId);
|
||||||
const isNotRoot = note?.noteId !== "root";
|
const isNotRoot = note?.noteId !== "root";
|
||||||
const isHoisted = note?.noteId === appContext.tabManager.getActiveContext().hoistedNoteId;
|
const isHoisted = note?.noteId === appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||||
const parentNote = isNotRoot && branch ? await froca.getNote(branch.parentNoteId) : null;
|
const parentNote = isNotRoot && branch ? await froca.getNote(branch.parentNoteId) : null;
|
||||||
|
|
||||||
// some actions don't support multi-note, so they are disabled when notes are selected,
|
// some actions don't support multi-note, so they are disabled when notes are selected,
|
||||||
@@ -226,8 +226,8 @@ export default class TreeContextMenu implements SelectMenuItemEventListener<Tree
|
|||||||
templateNoteId: templateNoteId
|
templateNoteId: templateNoteId
|
||||||
});
|
});
|
||||||
} else if (command === "openNoteInSplit") {
|
} else if (command === "openNoteInSplit") {
|
||||||
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
|
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
||||||
const { ntxId } = subContexts[subContexts.length - 1];
|
const { ntxId } = subContexts?.[subContexts.length - 1] ?? {};
|
||||||
|
|
||||||
this.treeWidget.triggerCommand("openNewNoteSplit", { ntxId, notePath });
|
this.treeWidget.triggerCommand("openNewNoteSplit", { ntxId, notePath });
|
||||||
} else if (command === "convertNoteToAttachment") {
|
} else if (command === "convertNoteToAttachment") {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import attributeParser from "./attribute_parser.js";
|
import attributeParser from "./attribute_parser.js";
|
||||||
|
|
||||||
|
|
||||||
describe("Lexing", () => {
|
describe("Lexing", () => {
|
||||||
it("simple label", () => {
|
it("simple label", () => {
|
||||||
expect(attributeParser.lex("#label").map((t: any) => t.text)).toEqual(["#label"]);
|
expect(attributeParser.lex("#label").map((t: any) => t.text)).toEqual(["#label"]);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ async function renderAttributes(attributes: FAttribute[], renderIsInheritable: b
|
|||||||
return $container;
|
return $container;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HIDDEN_ATTRIBUTES = [ "originalFileName", "fileSize", "template", "inherit", "cssClass", "iconClass", "pageSize", "viewType", "geolocation", "docName" ];
|
const HIDDEN_ATTRIBUTES = ["originalFileName", "fileSize", "template", "inherit", "cssClass", "iconClass", "pageSize", "viewType", "geolocation", "docName"];
|
||||||
|
|
||||||
async function renderNormalAttributes(note: FNote) {
|
async function renderNormalAttributes(note: FNote) {
|
||||||
const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes();
|
const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes();
|
||||||
|
|||||||
@@ -152,10 +152,10 @@ async function deleteNotes(branchIdsToDelete: string[], forceDeleteAllClones = f
|
|||||||
async function activateParentNotePath() {
|
async function activateParentNotePath() {
|
||||||
// this is not perfect, maybe we should find the next/previous sibling, but that's more complex
|
// this is not perfect, maybe we should find the next/previous sibling, but that's more complex
|
||||||
const activeContext = appContext.tabManager.getActiveContext();
|
const activeContext = appContext.tabManager.getActiveContext();
|
||||||
const parentNotePathArr = activeContext.notePathArray.slice(0, -1);
|
const parentNotePathArr = activeContext?.notePathArray.slice(0, -1);
|
||||||
|
|
||||||
if (parentNotePathArr.length > 0) {
|
if (parentNotePathArr && parentNotePathArr.length > 0) {
|
||||||
activeContext.setNote(parentNotePathArr.join("/"));
|
activeContext?.setNote(parentNotePathArr.join("/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -457,13 +457,13 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
|
|||||||
this.BasicWidget = BasicWidget;
|
this.BasicWidget = BasicWidget;
|
||||||
|
|
||||||
this.activateNote = async (notePath) => {
|
this.activateNote = async (notePath) => {
|
||||||
await appContext.tabManager.getActiveContext().setNote(notePath);
|
await appContext.tabManager.getActiveContext()?.setNote(notePath);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.activateNewNote = async (notePath) => {
|
this.activateNewNote = async (notePath) => {
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
await appContext.tabManager.getActiveContext().setNote(notePath);
|
await appContext.tabManager.getActiveContext()?.setNote(notePath);
|
||||||
await appContext.triggerEvent("focusAndSelectTitle", {});
|
await appContext.triggerEvent("focusAndSelectTitle", {});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -480,8 +480,8 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
|
|||||||
this.openSplitWithNote = async (notePath, activate) => {
|
this.openSplitWithNote = async (notePath, activate) => {
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
|
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
||||||
const { ntxId } = subContexts[subContexts.length - 1];
|
const { ntxId } = subContexts?.[subContexts.length - 1] ?? {};
|
||||||
|
|
||||||
await appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath });
|
await appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath });
|
||||||
|
|
||||||
@@ -591,15 +591,48 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
|
|||||||
|
|
||||||
this.addTextToActiveContextEditor = (text) => appContext.triggerCommand("addTextToActiveEditor", { text });
|
this.addTextToActiveContextEditor = (text) => appContext.triggerCommand("addTextToActiveEditor", { text });
|
||||||
|
|
||||||
this.getActiveContextNote = () => appContext.tabManager.getActiveContextNote();
|
this.getActiveContextNote = (): FNote => {
|
||||||
this.getActiveContext = () => appContext.tabManager.getActiveContext();
|
const note = appContext.tabManager.getActiveContextNote();
|
||||||
this.getActiveMainContext = () => appContext.tabManager.getActiveMainContext();
|
if (!note) {
|
||||||
|
throw new Error("No active context note found");
|
||||||
|
}
|
||||||
|
return note;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getActiveContext = (): NoteContext => {
|
||||||
|
const context = appContext.tabManager.getActiveContext();
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("No active context found");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getActiveMainContext = (): NoteContext => {
|
||||||
|
const context = appContext.tabManager.getActiveMainContext();
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("No active main context found");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
this.getNoteContexts = () => appContext.tabManager.getNoteContexts();
|
this.getNoteContexts = () => appContext.tabManager.getNoteContexts();
|
||||||
this.getMainNoteContexts = () => appContext.tabManager.getMainNoteContexts();
|
this.getMainNoteContexts = () => appContext.tabManager.getMainNoteContexts();
|
||||||
|
|
||||||
this.getActiveContextTextEditor = () => appContext.tabManager.getActiveContext()?.getTextEditor();
|
this.getActiveContextTextEditor = () => {
|
||||||
this.getActiveContextCodeEditor = () => appContext.tabManager.getActiveContext()?.getCodeEditor();
|
const context = appContext.tabManager.getActiveContext();
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("No active context found");
|
||||||
|
}
|
||||||
|
return context.getTextEditor();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getActiveContextCodeEditor = () => {
|
||||||
|
const context = appContext.tabManager.getActiveContext();
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("No active context found");
|
||||||
|
}
|
||||||
|
return context.getCodeEditor();
|
||||||
|
};
|
||||||
|
|
||||||
this.getActiveNoteDetailWidget = () => new Promise((resolve) => appContext.triggerCommand("executeInActiveNoteDetailWidget", { callback: resolve }));
|
this.getActiveNoteDetailWidget = () => new Promise((resolve) => appContext.triggerCommand("executeInActiveNoteDetailWidget", { callback: resolve }));
|
||||||
this.getActiveContextNotePath = () => appContext.tabManager.getActiveContextNotePath();
|
this.getActiveContextNotePath = () => appContext.tabManager.getActiveContextNotePath();
|
||||||
@@ -665,5 +698,5 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default FrontendScriptApi as any as {
|
export default FrontendScriptApi as any as {
|
||||||
new (startNote: FNote, currentNote: FNote, originEntity: Entity | null, $container: JQuery<HTMLElement> | null): Api;
|
new(startNote: FNote, currentNote: FNote, originEntity: Entity | null, $container: JQuery<HTMLElement> | null): Api;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ ws.subscribeToMessages(async (message) => {
|
|||||||
toastService.showPersistent(toast);
|
toastService.showPersistent(toast);
|
||||||
|
|
||||||
if (message.result.importedNoteId) {
|
if (message.result.importedNoteId) {
|
||||||
await appContext.tabManager.getActiveContext().setNote(message.result.importedNoteId);
|
await appContext.tabManager.getActiveContext()?.setNote(message.result.importedNoteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -102,7 +102,7 @@ ws.subscribeToMessages(async (message) => {
|
|||||||
toastService.showPersistent(toast);
|
toastService.showPersistent(toast);
|
||||||
|
|
||||||
if (message.result.parentNoteId) {
|
if (message.result.parentNoteId) {
|
||||||
await appContext.tabManager.getActiveContext().setNote(message.result.importedNoteId, {
|
await appContext.tabManager.getActiveContext()?.setNote(message.result.importedNoteId, {
|
||||||
viewScope: {
|
viewScope: {
|
||||||
viewMode: "attachments"
|
viewMode: "attachments"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ const HIGHLIGHT_JS: Library = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const LEAFLET: Library = {
|
const LEAFLET: Library = {
|
||||||
css: [ "node_modules/leaflet/dist/leaflet.css" ],
|
css: ["node_modules/leaflet/dist/leaflet.css"]
|
||||||
}
|
};
|
||||||
|
|
||||||
async function requireLibrary(library: Library) {
|
async function requireLibrary(library: Library) {
|
||||||
if (library.css) {
|
if (library.css) {
|
||||||
|
|||||||
@@ -274,8 +274,8 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent
|
|||||||
const { notePath, viewScope } = parseNavigationStateFromUrl(hrefLink);
|
const { notePath, viewScope } = parseNavigationStateFromUrl(hrefLink);
|
||||||
|
|
||||||
const ctrlKey = utils.isCtrlKey(evt);
|
const ctrlKey = utils.isCtrlKey(evt);
|
||||||
const isLeftClick = ("which" in evt && evt.which === 1);
|
const isLeftClick = "which" in evt && evt.which === 1;
|
||||||
const isMiddleClick = ("which" in evt && evt.which === 2);
|
const isMiddleClick = "which" in evt && evt.which === 2;
|
||||||
const openInNewTab = (isLeftClick && ctrlKey) || isMiddleClick;
|
const openInNewTab = (isLeftClick && ctrlKey) || isMiddleClick;
|
||||||
|
|
||||||
if (notePath) {
|
if (notePath) {
|
||||||
|
|||||||
@@ -322,9 +322,7 @@ function init() {
|
|||||||
|
|
||||||
$.fn.setSelectedNotePath = function (notePath) {
|
$.fn.setSelectedNotePath = function (notePath) {
|
||||||
notePath = notePath || "";
|
notePath = notePath || "";
|
||||||
|
|
||||||
$(this).attr(SELECTED_NOTE_PATH_KEY, notePath);
|
$(this).attr(SELECTED_NOTE_PATH_KEY, notePath);
|
||||||
|
|
||||||
$(this).closest(".input-group").find(".go-to-selected-note-button").toggleClass("disabled", !notePath.trim()).attr("href", `#${notePath}`); // we also set href here so tooltip can be displayed
|
$(this).closest(".input-group").find(".go-to-selected-note-button").toggleClass("disabled", !notePath.trim()).attr("href", `#${notePath}`); // we also set href here so tooltip can be displayed
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -336,11 +334,9 @@ function init() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$.fn.setSelectedExternalLink = function (externalLink) {
|
$.fn.setSelectedExternalLink = function (externalLink: string | null) {
|
||||||
if (externalLink) {
|
$(this).attr(SELECTED_EXTERNAL_LINK_KEY, externalLink);
|
||||||
// TODO: This doesn't seem to do anything with the external link, is it normal?
|
|
||||||
$(this).closest(".input-group").find(".go-to-selected-note-button").toggleClass("disabled", true);
|
$(this).closest(".input-group").find(".go-to-selected-note-button").toggleClass("disabled", true);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$.fn.setNote = async function (noteId) {
|
$.fn.setNote = async function (noteId) {
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ async function createNote(parentNotePath: string | undefined, options: CreateNot
|
|||||||
|
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
if (options.activate) {
|
|
||||||
const activeNoteContext = appContext.tabManager.getActiveContext();
|
const activeNoteContext = appContext.tabManager.getActiveContext();
|
||||||
|
if (activeNoteContext && options.activate) {
|
||||||
await activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`);
|
await activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`);
|
||||||
|
|
||||||
if (options.focus === "title") {
|
if (options.focus === "title") {
|
||||||
@@ -152,8 +152,7 @@ async function duplicateSubtree(noteId: string, parentNotePath: string) {
|
|||||||
|
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
const activeNoteContext = appContext.tabManager.getActiveContext();
|
appContext.tabManager.getActiveContext()?.setNote(`${parentNotePath}/${note.noteId}`);
|
||||||
activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`);
|
|
||||||
|
|
||||||
const origNote = await froca.getNote(noteId);
|
const origNote = await froca.getNote(noteId);
|
||||||
toastService.showMessage(t("note_create.duplicated", { title: origNote?.title }));
|
toastService.showMessage(t("note_create.duplicated", { title: origNote?.title }));
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default class NoteListRenderer {
|
|||||||
parentNote,
|
parentNote,
|
||||||
noteIds,
|
noteIds,
|
||||||
showNotePath
|
showNotePath
|
||||||
}
|
};
|
||||||
|
|
||||||
if (this.viewType === "list" || this.viewType === "grid") {
|
if (this.viewType === "list" || this.viewType === "grid") {
|
||||||
this.viewMode = new ListOrGridView(this.viewType, args);
|
this.viewMode = new ListOrGridView(this.viewType, args);
|
||||||
|
|||||||
@@ -147,11 +147,11 @@ async function renderTooltip(note: FNote | null) {
|
|||||||
tooltip: true,
|
tooltip: true,
|
||||||
trim: true
|
trim: true
|
||||||
});
|
});
|
||||||
const isContentEmpty = ($renderedContent[0].innerHTML.length === 0);
|
const isContentEmpty = $renderedContent[0].innerHTML.length === 0;
|
||||||
|
|
||||||
let content = "";
|
let content = "";
|
||||||
if (noteTitleWithPathAsSuffix) {
|
if (noteTitleWithPathAsSuffix) {
|
||||||
const classes = [ "note-tooltip-title" ];
|
const classes = ["note-tooltip-title"];
|
||||||
if (isContentEmpty) {
|
if (isContentEmpty) {
|
||||||
classes.push("note-no-content");
|
classes.push("note-no-content");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,20 +36,20 @@ function parseDate(str: string) {
|
|||||||
|
|
||||||
// Source: https://stackoverflow.com/a/30465299/4898894
|
// Source: https://stackoverflow.com/a/30465299/4898894
|
||||||
function getMonthsInDateRange(startDate: string, endDate: string) {
|
function getMonthsInDateRange(startDate: string, endDate: string) {
|
||||||
const start = startDate.split('-');
|
const start = startDate.split("-");
|
||||||
const end = endDate.split('-');
|
const end = endDate.split("-");
|
||||||
const startYear = parseInt(start[0]);
|
const startYear = parseInt(start[0]);
|
||||||
const endYear = parseInt(end[0]);
|
const endYear = parseInt(end[0]);
|
||||||
const dates = [];
|
const dates = [];
|
||||||
|
|
||||||
for (let i = startYear; i <= endYear; i++) {
|
for (let i = startYear; i <= endYear; i++) {
|
||||||
const endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
|
const endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
|
||||||
const startMon = i === startYear ? parseInt(start[1])-1 : 0;
|
const startMon = i === startYear ? parseInt(start[1]) - 1 : 0;
|
||||||
|
|
||||||
for(let j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j+1) {
|
for (let j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j + 1) {
|
||||||
const month = j+1;
|
const month = j + 1;
|
||||||
const displayMonth = month < 10 ? '0'+month : month;
|
const displayMonth = month < 10 ? "0" + month : month;
|
||||||
dates.push([i, displayMonth].join('-'));
|
dates.push([i, displayMonth].join("-"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dates;
|
return dates;
|
||||||
@@ -164,7 +164,7 @@ function escapeHtml(str: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function escapeQuotes(value: string) {
|
export function escapeQuotes(value: string) {
|
||||||
return value.replaceAll("\"", """);
|
return value.replaceAll('"', """);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSize(size: number) {
|
function formatSize(size: number) {
|
||||||
|
|||||||
6
src/public/app/types-fancytree.d.ts
vendored
6
src/public/app/types-fancytree.d.ts
vendored
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
interface JQueryStatic {
|
interface JQueryStatic {
|
||||||
ui: JQueryUI.UI;
|
ui: JQueryUI.UI;
|
||||||
};
|
}
|
||||||
|
|
||||||
declare namespace JQueryUI {
|
declare namespace JQueryUI {
|
||||||
interface UI {
|
interface UI {
|
||||||
@@ -679,13 +679,13 @@ declare namespace Fancytree {
|
|||||||
activate = 1,
|
activate = 1,
|
||||||
expand = 2,
|
expand = 2,
|
||||||
activate_and_expand = 3,
|
activate_and_expand = 3,
|
||||||
activate_dblclick_expands = 4,
|
activate_dblclick_expands = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FancytreeSelectMode {
|
enum FancytreeSelectMode {
|
||||||
single = 1,
|
single = 1,
|
||||||
multi = 2,
|
multi = 2,
|
||||||
mutlti_hier = 3,
|
mutlti_hier = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Context object passed to events and hook functions. */
|
/** Context object passed to events and hook functions. */
|
||||||
|
|||||||
4
src/public/app/types-lib.d.ts
vendored
4
src/public/app/types-lib.d.ts
vendored
@@ -13,7 +13,7 @@ declare module "draggabilly" {
|
|||||||
containment: HTMLElement
|
containment: HTMLElement
|
||||||
});
|
});
|
||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
on(event: "pointerDown" | "dragStart" | "dragEnd" | "dragMove", callback: Callback)
|
on(event: "pointerDown" | "dragStart" | "dragEnd" | "dragMove", callback: Callback);
|
||||||
dragEnd();
|
dragEnd();
|
||||||
isDragging: boolean;
|
isDragging: boolean;
|
||||||
positionDrag: () => void;
|
positionDrag: () => void;
|
||||||
@@ -21,6 +21,6 @@ declare module "draggabilly" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@mind-elixir/node-menu' {
|
declare module "@mind-elixir/node-menu" {
|
||||||
export default mindmap;
|
export default mindmap;
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/public/app/types.d.ts
vendored
2
src/public/app/types.d.ts
vendored
@@ -85,7 +85,7 @@ declare global {
|
|||||||
getSelectedNotePath(): string | undefined;
|
getSelectedNotePath(): string | undefined;
|
||||||
getSelectedNoteId(): string | null;
|
getSelectedNoteId(): string | null;
|
||||||
setSelectedNotePath(notePath: string | null | undefined);
|
setSelectedNotePath(notePath: string | null | undefined);
|
||||||
getSelectedExternalLink(this: HTMLElement): string | undefined;
|
getSelectedExternalLink(): string | undefined;
|
||||||
setSelectedExternalLink(externalLink: string | null | undefined);
|
setSelectedExternalLink(externalLink: string | null | undefined);
|
||||||
setNote(noteId: string);
|
setNote(noteId: string);
|
||||||
markRegExp(regex: RegExp, opts: {
|
markRegExp(regex: RegExp, opts: {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default class BookmarkButtons extends FlexContainer<Component> {
|
|||||||
: new OpenNoteButtonWidget(note).class("launcher-button");
|
: new OpenNoteButtonWidget(note).class("launcher-button");
|
||||||
|
|
||||||
if (this.settings.titlePlacement) {
|
if (this.settings.titlePlacement) {
|
||||||
if (!('settings' in buttonWidget)) {
|
if (!("settings" in buttonWidget)) {
|
||||||
(buttonWidget as any).settings = {};
|
(buttonWidget as any).settings = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import appContext from "../../components/app_context.js";
|
|||||||
import openService from "../../services/open.js";
|
import openService from "../../services/open.js";
|
||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import { Dropdown } from "bootstrap";
|
import { Dropdown } from "bootstrap";
|
||||||
import type attachmentsApiRoute from "../../../../routes/api/attachments.js"
|
import type attachmentsApiRoute from "../../../../routes/api/attachments.js";
|
||||||
import type FAttachment from "../../entities/fattachment.js";
|
import type FAttachment from "../../entities/fattachment.js";
|
||||||
import type AttachmentDetailWidget from "../attachment_detail.js";
|
import type AttachmentDetailWidget from "../attachment_detail.js";
|
||||||
|
|
||||||
@@ -105,7 +105,6 @@ export default class AttachmentActionsWidget extends BasicWidget {
|
|||||||
|
|
||||||
this.$uploadNewRevisionInput = this.$widget.find(".attachment-upload-new-revision-input");
|
this.$uploadNewRevisionInput = this.$widget.find(".attachment-upload-new-revision-input");
|
||||||
this.$uploadNewRevisionInput.on("change", async () => {
|
this.$uploadNewRevisionInput.on("change", async () => {
|
||||||
|
|
||||||
const fileToUpload = this.$uploadNewRevisionInput[0].files?.item(0); // copy to allow reset below
|
const fileToUpload = this.$uploadNewRevisionInput[0].files?.item(0); // copy to allow reset below
|
||||||
this.$uploadNewRevisionInput.val("");
|
this.$uploadNewRevisionInput.val("");
|
||||||
if (fileToUpload) {
|
if (fileToUpload) {
|
||||||
@@ -131,7 +130,6 @@ export default class AttachmentActionsWidget extends BasicWidget {
|
|||||||
const $openAttachmentCustomButton = this.$widget.find("[data-trigger-command='openAttachmentCustom']");
|
const $openAttachmentCustomButton = this.$widget.find("[data-trigger-command='openAttachmentCustom']");
|
||||||
$openAttachmentCustomButton.addClass("disabled").append($('<span class="bx bx-info-circle disabled-tooltip" />').attr("title", t("attachments_actions.open_custom_client_only")));
|
$openAttachmentCustomButton.addClass("disabled").append($('<span class="bx bx-info-circle disabled-tooltip" />').attr("title", t("attachments_actions.open_custom_client_only")));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async openAttachmentCommand() {
|
async openAttachmentCommand() {
|
||||||
@@ -170,11 +168,10 @@ export default class AttachmentActionsWidget extends BasicWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const { note: newNote } = await server.post<ReturnType<typeof attachmentsApiRoute.convertAttachmentToNote>>(`attachments/${this.attachmentId}/convert-to-note`);
|
const { note: newNote } = await server.post<ReturnType<typeof attachmentsApiRoute.convertAttachmentToNote>>(`attachments/${this.attachmentId}/convert-to-note`);
|
||||||
toastService.showMessage(t("attachments_actions.convert_success", { title: this.attachment.title }));
|
toastService.showMessage(t("attachments_actions.convert_success", { title: this.attachment.title }));
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
await appContext.tabManager.getActiveContext().setNote(newNote.noteId);
|
await appContext.tabManager.getActiveContext()?.setNote(newNote.noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async renameAttachmentCommand() {
|
async renameAttachmentCommand() {
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export default class CalendarWidget extends RightDropdownButtonWidget {
|
|||||||
const note = await dateNoteService.getDayNote(date);
|
const note = await dateNoteService.getDayNote(date);
|
||||||
|
|
||||||
if (note) {
|
if (note) {
|
||||||
appContext.tabManager.getActiveContext().setNote(note.noteId);
|
appContext.tabManager.getActiveContext()?.setNote(note.noteId);
|
||||||
this.dropdown?.hide();
|
this.dropdown?.hide();
|
||||||
} else {
|
} else {
|
||||||
toastService.showError(t("calendar.cannot_find_day_note"));
|
toastService.showError(t("calendar.cannot_find_day_note"));
|
||||||
@@ -189,10 +189,7 @@ export default class CalendarWidget extends RightDropdownButtonWidget {
|
|||||||
|
|
||||||
async dropdownShown() {
|
async dropdownShown() {
|
||||||
await libraryLoader.requireLibrary(libraryLoader.CALENDAR_WIDGET);
|
await libraryLoader.requireLibrary(libraryLoader.CALENDAR_WIDGET);
|
||||||
|
this.init(appContext.tabManager.getActiveContextNote()?.getOwnedLabelValue("dateNote") ?? null);
|
||||||
const activeNote = appContext.tabManager.getActiveContextNote();
|
|
||||||
|
|
||||||
this.init(activeNote?.getOwnedLabelValue("dateNote"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(activeDate: string | null) {
|
init(activeDate: string | null) {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export default class NoteLauncher extends AbstractLauncher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getHoistedNoteId() {
|
getHoistedNoteId() {
|
||||||
return this.launcherNote.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext().hoistedNoteId;
|
return this.launcherNote.getRelationValue("hoistedNote") || appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTitle() {
|
getTitle() {
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ export default class TodayLauncher extends NoteLauncher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getHoistedNoteId() {
|
getHoistedNoteId() {
|
||||||
return appContext.tabManager.getActiveContext().hoistedNoteId;
|
return appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
toastService.showMessage(t("note_actions.convert_into_attachment_successful", { title: newAttachment.title }));
|
toastService.showMessage(t("note_actions.convert_into_attachment_successful", { title: newAttachment.title }));
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
await appContext.tabManager.getActiveContext().setNote(newAttachment.ownerId, {
|
await appContext.tabManager.getActiveContext()?.setNote(newAttachment.ownerId, {
|
||||||
viewScope: {
|
viewScope: {
|
||||||
viewMode: "attachments",
|
viewMode: "attachments",
|
||||||
attachmentId: newAttachment.attachmentId
|
attachmentId: newAttachment.attachmentId
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ export default class LeftPaneContainer extends FlexContainer<Component> {
|
|||||||
if (visible) {
|
if (visible) {
|
||||||
this.triggerEvent("focusTree", {});
|
this.triggerEvent("focusTree", {});
|
||||||
} else {
|
} else {
|
||||||
const activeNoteContext = appContext.tabManager.getActiveContext();
|
this.triggerEvent("focusOnDetail", { ntxId: appContext.tabManager.getActiveContext()?.ntxId });
|
||||||
this.triggerEvent("focusOnDetail", { ntxId: activeNoteContext.ntxId });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,8 @@ export default class RibbonContainer extends NoteContextAwareWidget {
|
|||||||
return super.isEnabled() && this.noteContext?.viewScope?.viewMode === "default";
|
return super.isEnabled() && this.noteContext?.viewScope?.viewMode === "default";
|
||||||
}
|
}
|
||||||
|
|
||||||
ribbon(widget: NoteContextAwareWidget) { // TODO: Base class
|
ribbon(widget: NoteContextAwareWidget) {
|
||||||
|
// TODO: Base class
|
||||||
super.child(widget);
|
super.child(widget);
|
||||||
|
|
||||||
this.ribbonWidgets.push(widget);
|
this.ribbonWidgets.push(widget);
|
||||||
@@ -236,7 +237,12 @@ export default class RibbonContainer extends NoteContextAwareWidget {
|
|||||||
const $ribbonTitle = $('<div class="ribbon-tab-title">')
|
const $ribbonTitle = $('<div class="ribbon-tab-title">')
|
||||||
.attr("data-ribbon-component-id", ribbonWidget.componentId)
|
.attr("data-ribbon-component-id", ribbonWidget.componentId)
|
||||||
.attr("data-ribbon-component-name", (ribbonWidget as any).name as string) // TODO: base class for ribbon widgets
|
.attr("data-ribbon-component-name", (ribbonWidget as any).name as string) // TODO: base class for ribbon widgets
|
||||||
.append($('<span class="ribbon-tab-title-icon">').addClass(ret.icon).attr("title", ret.title).attr("data-toggle-command", (ribbonWidget as any).toggleCommand)) // TODO: base class
|
.append(
|
||||||
|
$('<span class="ribbon-tab-title-icon">')
|
||||||
|
.addClass(ret.icon)
|
||||||
|
.attr("title", ret.title)
|
||||||
|
.attr("data-toggle-command", (ribbonWidget as any).toggleCommand)
|
||||||
|
) // TODO: base class
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append($('<span class="ribbon-tab-title-label">').text(ret.title));
|
.append($('<span class="ribbon-tab-title-label">').text(ret.title));
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,29 @@
|
|||||||
import FlexContainer from "./flex_container.js";
|
import FlexContainer from "./flex_container.js";
|
||||||
import appContext from "../../components/app_context.js";
|
import appContext from "../../components/app_context.js";
|
||||||
|
import NoteContext from "../../components/note_context.js";
|
||||||
|
import type { CommandMappings, EventNames, EventData } from "../../components/app_context.js";
|
||||||
|
import type BasicWidget from "../basic_widget.js";
|
||||||
|
|
||||||
export default class SplitNoteContainer extends FlexContainer {
|
interface NoteContextEvent {
|
||||||
constructor(widgetFactory) {
|
noteContext: NoteContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SplitNoteWidget extends BasicWidget {
|
||||||
|
hasBeenAlreadyShown?: boolean;
|
||||||
|
ntxId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type WidgetFactory = () => SplitNoteWidget;
|
||||||
|
|
||||||
|
interface Widgets {
|
||||||
|
[key: string]: SplitNoteWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
||||||
|
private widgetFactory: WidgetFactory;
|
||||||
|
private widgets: Widgets;
|
||||||
|
|
||||||
|
constructor(widgetFactory: WidgetFactory) {
|
||||||
super("row");
|
super("row");
|
||||||
|
|
||||||
this.widgetFactory = widgetFactory;
|
this.widgetFactory = widgetFactory;
|
||||||
@@ -13,7 +34,7 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
this.collapsible();
|
this.collapsible();
|
||||||
}
|
}
|
||||||
|
|
||||||
async newNoteContextCreatedEvent({ noteContext }) {
|
async newNoteContextCreatedEvent({ noteContext }: NoteContextEvent) {
|
||||||
const widget = this.widgetFactory();
|
const widget = this.widgetFactory();
|
||||||
|
|
||||||
const $renderedWidget = widget.render();
|
const $renderedWidget = widget.render();
|
||||||
@@ -23,20 +44,32 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
|
|
||||||
this.$widget.append($renderedWidget);
|
this.$widget.append($renderedWidget);
|
||||||
|
|
||||||
widget.handleEvent("initialRenderComplete");
|
widget.handleEvent("initialRenderComplete", {});
|
||||||
|
|
||||||
widget.toggleExt(false);
|
widget.toggleExt(false);
|
||||||
|
|
||||||
|
if (noteContext.ntxId) {
|
||||||
this.widgets[noteContext.ntxId] = widget;
|
this.widgets[noteContext.ntxId] = widget;
|
||||||
|
}
|
||||||
|
|
||||||
await widget.handleEvent("setNoteContext", { noteContext });
|
await widget.handleEvent("setNoteContext", { noteContext });
|
||||||
|
|
||||||
this.child(widget);
|
this.child(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }) {
|
async openNewNoteSplitEvent({ ntxId, notePath, hoistedNoteId, viewScope }: {
|
||||||
|
ntxId: string;
|
||||||
|
notePath?: string;
|
||||||
|
hoistedNoteId?: string;
|
||||||
|
viewScope?: any;
|
||||||
|
}) {
|
||||||
const mainNtxId = appContext.tabManager.getActiveMainContext().ntxId;
|
const mainNtxId = appContext.tabManager.getActiveMainContext().ntxId;
|
||||||
|
|
||||||
|
if (!mainNtxId) {
|
||||||
|
logError("empty mainNtxId!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ntxId) {
|
if (!ntxId) {
|
||||||
logError("empty ntxId!");
|
logError("empty ntxId!");
|
||||||
|
|
||||||
@@ -53,7 +86,7 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
// insert the note context after the originating note context
|
// insert the note context after the originating note context
|
||||||
ntxIds.splice(ntxIds.indexOf(ntxId) + 1, 0, noteContext.ntxId);
|
ntxIds.splice(ntxIds.indexOf(ntxId) + 1, 0, noteContext.ntxId);
|
||||||
|
|
||||||
this.triggerCommand("noteContextReorder", { ntxIdsInOrder: ntxIds });
|
this.triggerCommand("noteContextReorder" as keyof CommandMappings, { ntxIdsInOrder: ntxIds });
|
||||||
|
|
||||||
// move the note context rendered widget after the originating widget
|
// move the note context rendered widget after the originating widget
|
||||||
this.$widget.find(`[data-ntx-id="${noteContext.ntxId}"]`).insertAfter(this.$widget.find(`[data-ntx-id="${ntxId}"]`));
|
this.$widget.find(`[data-ntx-id="${noteContext.ntxId}"]`).insertAfter(this.$widget.find(`[data-ntx-id="${ntxId}"]`));
|
||||||
@@ -67,11 +100,11 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
closeThisNoteSplitCommand({ ntxId }) {
|
closeThisNoteSplitCommand({ ntxId }: { ntxId: string }): void {
|
||||||
appContext.tabManager.removeNoteContext(ntxId);
|
appContext.tabManager.removeNoteContext(ntxId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async moveThisNoteSplitCommand({ ntxId, isMovingLeft }) {
|
async moveThisNoteSplitCommand({ ntxId, isMovingLeft }: { ntxId: string; isMovingLeft: boolean }): Promise<void> {
|
||||||
if (!ntxId) {
|
if (!ntxId) {
|
||||||
logError("empty ntxId!");
|
logError("empty ntxId!");
|
||||||
return;
|
return;
|
||||||
@@ -96,7 +129,7 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
const newNtxIds = [...ntxIds.slice(0, leftIndex), ntxIds[leftIndex + 1], ntxIds[leftIndex], ...ntxIds.slice(leftIndex + 2)];
|
const newNtxIds = [...ntxIds.slice(0, leftIndex), ntxIds[leftIndex + 1], ntxIds[leftIndex], ...ntxIds.slice(leftIndex + 2)];
|
||||||
const isChangingMainContext = !contexts[leftIndex].mainNtxId;
|
const isChangingMainContext = !contexts[leftIndex].mainNtxId;
|
||||||
|
|
||||||
this.triggerCommand("noteContextReorder", {
|
this.triggerCommand("noteContextReorder" as keyof CommandMappings, {
|
||||||
ntxIdsInOrder: newNtxIds,
|
ntxIdsInOrder: newNtxIds,
|
||||||
oldMainNtxId: isChangingMainContext ? ntxIds[leftIndex] : null,
|
oldMainNtxId: isChangingMainContext ? ntxIds[leftIndex] : null,
|
||||||
newMainNtxId: isChangingMainContext ? ntxIds[leftIndex + 1] : null
|
newMainNtxId: isChangingMainContext ? ntxIds[leftIndex + 1] : null
|
||||||
@@ -109,16 +142,16 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]);
|
await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
activeContextChangedEvent() {
|
activeContextChangedEvent(): void {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
noteSwitchedAndActivatedEvent() {
|
noteSwitchedAndActivatedEvent(): void {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
noteContextRemovedEvent({ ntxIds }) {
|
noteContextRemovedEvent({ ntxIds }: { ntxIds: string[] }): void {
|
||||||
this.children = this.children.filter((c) => !ntxIds.includes(c.ntxId));
|
this.children = this.children.filter((c) => c.ntxId && !ntxIds.includes(c.ntxId));
|
||||||
|
|
||||||
for (const ntxId of ntxIds) {
|
for (const ntxId of ntxIds) {
|
||||||
this.$widget.find(`[data-ntx-id="${ntxId}"]`).remove();
|
this.$widget.find(`[data-ntx-id="${ntxId}"]`).remove();
|
||||||
@@ -127,7 +160,7 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contextsReopenedEvent({ ntxId, afterNtxId }) {
|
contextsReopenedEvent({ ntxId, afterNtxId }: { ntxId?: string; afterNtxId?: string }): void {
|
||||||
if (ntxId === undefined || afterNtxId === undefined) {
|
if (ntxId === undefined || afterNtxId === undefined) {
|
||||||
// no single split reopened
|
// no single split reopened
|
||||||
return;
|
return;
|
||||||
@@ -135,13 +168,11 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
this.$widget.find(`[data-ntx-id="${ntxId}"]`).insertAfter(this.$widget.find(`[data-ntx-id="${afterNtxId}"]`));
|
this.$widget.find(`[data-ntx-id="${ntxId}"]`).insertAfter(this.$widget.find(`[data-ntx-id="${afterNtxId}"]`));
|
||||||
}
|
}
|
||||||
|
|
||||||
async refresh() {
|
async refresh(): Promise<void> {
|
||||||
this.toggleExt(true);
|
this.toggleExt(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleInt(show) {} // not needed
|
toggleExt(show: boolean): void {
|
||||||
|
|
||||||
toggleExt(show) {
|
|
||||||
const activeMainContext = appContext.tabManager.getActiveMainContext();
|
const activeMainContext = appContext.tabManager.getActiveMainContext();
|
||||||
const activeNtxId = activeMainContext ? activeMainContext.ntxId : null;
|
const activeNtxId = activeMainContext ? activeMainContext.ntxId : null;
|
||||||
|
|
||||||
@@ -149,7 +180,7 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
||||||
|
|
||||||
const widget = this.widgets[ntxId];
|
const widget = this.widgets[ntxId];
|
||||||
widget.toggleExt(show && activeNtxId && [noteContext.ntxId, noteContext.mainNtxId].includes(activeNtxId));
|
widget.toggleExt(show && activeNtxId !== null && [noteContext.ntxId, noteContext.mainNtxId].includes(activeNtxId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,41 +189,50 @@ export default class SplitNoteContainer extends FlexContainer {
|
|||||||
* are not executed, we're waiting for the first tab activation, and then we update the tab. After this initial
|
* are not executed, we're waiting for the first tab activation, and then we update the tab. After this initial
|
||||||
* activation, further note switches are always propagated to the tabs.
|
* activation, further note switches are always propagated to the tabs.
|
||||||
*/
|
*/
|
||||||
handleEventInChildren(name, data) {
|
handleEventInChildren<T extends EventNames>(name: T, data: EventData<T>): Promise<any> | null {
|
||||||
if (["noteSwitched", "noteSwitchedAndActivated"].includes(name)) {
|
if (["noteSwitched", "noteSwitchedAndActivated"].includes(name)) {
|
||||||
// this event is propagated only to the widgets of a particular tab
|
// this event is propagated only to the widgets of a particular tab
|
||||||
const widget = this.widgets[data.noteContext.ntxId];
|
const noteContext = (data as NoteContextEvent).noteContext;
|
||||||
|
const widget = noteContext.ntxId ? this.widgets[noteContext.ntxId] : undefined;
|
||||||
|
|
||||||
if (!widget) {
|
if (!widget) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.hasBeenAlreadyShown || name === "noteSwitchedAndActivated" || appContext.tabManager.getActiveMainContext() === data.noteContext.getMainContext()) {
|
if (widget.hasBeenAlreadyShown || name === "noteSwitchedAndActivatedEvent" || appContext.tabManager.getActiveMainContext() === noteContext.getMainContext()) {
|
||||||
widget.hasBeenAlreadyShown = true;
|
widget.hasBeenAlreadyShown = true;
|
||||||
|
|
||||||
return [widget.handleEvent("noteSwitched", data), this.refreshNotShown(data)];
|
return Promise.all([
|
||||||
|
widget.handleEvent("noteSwitched", { noteContext, notePath: noteContext.notePath }),
|
||||||
|
this.refreshNotShown({ noteContext })
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === "activeContextChanged") {
|
if (name === "activeContextChanged") {
|
||||||
return this.refreshNotShown(data);
|
return this.refreshNotShown(data as NoteContextEvent);
|
||||||
} else {
|
} else {
|
||||||
return super.handleEventInChildren(name, data);
|
return super.handleEventInChildren(name, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshNotShown(data) {
|
private refreshNotShown(data: NoteContextEvent): Promise<any> {
|
||||||
const promises = [];
|
const promises: Promise<any>[] = [];
|
||||||
|
|
||||||
for (const subContext of data.noteContext.getMainContext().getSubContexts()) {
|
for (const subContext of data.noteContext.getMainContext().getSubContexts()) {
|
||||||
|
if (!subContext.ntxId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const widget = this.widgets[subContext.ntxId];
|
const widget = this.widgets[subContext.ntxId];
|
||||||
|
|
||||||
if (!widget.hasBeenAlreadyShown) {
|
if (!widget.hasBeenAlreadyShown) {
|
||||||
widget.hasBeenAlreadyShown = true;
|
widget.hasBeenAlreadyShown = true;
|
||||||
|
|
||||||
promises.push(widget.handleEvent("activeContextChanged", { noteContext: subContext }));
|
const eventPromise = widget.handleEvent("activeContextChanged", { noteContext: subContext });
|
||||||
|
promises.push(eventPromise || Promise.resolve());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5,6 +5,15 @@ import openService from "../../services/open.js";
|
|||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
|
|
||||||
|
interface AppInfo {
|
||||||
|
appVersion: string;
|
||||||
|
dbVersion: number;
|
||||||
|
syncVersion: number;
|
||||||
|
buildDate: string;
|
||||||
|
buildRevision: string;
|
||||||
|
dataDirectory: string;
|
||||||
|
}
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="about-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
<div class="about-dialog modal fade mx-auto" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog modal-lg" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
@@ -35,12 +44,10 @@ const TPL = `
|
|||||||
<th>${t("about.build_date")}</th>
|
<th>${t("about.build_date")}</th>
|
||||||
<td class="build-date"></td>
|
<td class="build-date"></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>${t("about.build_revision")}</th>
|
<th>${t("about.build_revision")}</th>
|
||||||
<td><a class="tn-link" href="" class="build-revision external" target="_blank"></a></td>
|
<td><a class="tn-link build-revision external" href="" target="_blank"></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>${t("about.data_directory")}</th>
|
<th>${t("about.data_directory")}</th>
|
||||||
<td class="data-directory"></td>
|
<td class="data-directory"></td>
|
||||||
@@ -59,7 +66,14 @@ const TPL = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export default class AboutDialog extends BasicWidget {
|
export default class AboutDialog extends BasicWidget {
|
||||||
doRender() {
|
private $appVersion!: JQuery<HTMLElement>;
|
||||||
|
private $dbVersion!: JQuery<HTMLElement>;
|
||||||
|
private $syncVersion!: JQuery<HTMLElement>;
|
||||||
|
private $buildDate!: JQuery<HTMLElement>;
|
||||||
|
private $buildRevision!: JQuery<HTMLElement>;
|
||||||
|
private $dataDirectory!: JQuery<HTMLElement>;
|
||||||
|
|
||||||
|
doRender(): void {
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
this.$appVersion = this.$widget.find(".app-version");
|
this.$appVersion = this.$widget.find(".app-version");
|
||||||
this.$dbVersion = this.$widget.find(".db-version");
|
this.$dbVersion = this.$widget.find(".db-version");
|
||||||
@@ -70,11 +84,11 @@ export default class AboutDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async refresh() {
|
async refresh() {
|
||||||
const appInfo = await server.get("app-info");
|
const appInfo = await server.get<AppInfo>("app-info");
|
||||||
|
|
||||||
this.$appVersion.text(appInfo.appVersion);
|
this.$appVersion.text(appInfo.appVersion);
|
||||||
this.$dbVersion.text(appInfo.dbVersion);
|
this.$dbVersion.text(appInfo.dbVersion.toString());
|
||||||
this.$syncVersion.text(appInfo.syncVersion);
|
this.$syncVersion.text(appInfo.syncVersion.toString());
|
||||||
this.$buildDate.text(formatDateTime(appInfo.buildDate));
|
this.$buildDate.text(formatDateTime(appInfo.buildDate));
|
||||||
this.$buildRevision.text(appInfo.buildRevision);
|
this.$buildRevision.text(appInfo.buildRevision);
|
||||||
this.$buildRevision.attr("href", `https://github.com/TriliumNext/Notes/commit/${appInfo.buildRevision}`);
|
this.$buildRevision.attr("href", `https://github.com/TriliumNext/Notes/commit/${appInfo.buildRevision}`);
|
||||||
@@ -84,9 +98,9 @@ export default class AboutDialog extends BasicWidget {
|
|||||||
href: "#",
|
href: "#",
|
||||||
class: "tn-link",
|
class: "tn-link",
|
||||||
text: appInfo.dataDirectory
|
text: appInfo.dataDirectory
|
||||||
})
|
}).prop("outerHTML")
|
||||||
);
|
);
|
||||||
this.$dataDirectory.find("a").on("click", (event) => {
|
this.$dataDirectory.find("a").on("click", (event: JQuery.ClickEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
openService.openDirectory(appInfo.dataDirectory);
|
openService.openDirectory(appInfo.dataDirectory);
|
||||||
});
|
});
|
||||||
@@ -97,7 +111,6 @@ export default class AboutDialog extends BasicWidget {
|
|||||||
|
|
||||||
async openAboutDialogEvent() {
|
async openAboutDialogEvent() {
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
|
|
||||||
utils.openDialog(this.$widget);
|
utils.openDialog(this.$widget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,9 @@ import treeService from "../../services/tree.js";
|
|||||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import BasicWidget from "../basic_widget.js";
|
import BasicWidget from "../basic_widget.js";
|
||||||
|
import type { Suggestion } from "../../services/note_autocomplete.js";
|
||||||
|
import type { default as TextTypeWidget } from "../type_widgets/editable_text.js";
|
||||||
|
import type { EventData } from "../../components/app_context.js";
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="add-link-dialog modal mx-auto" tabindex="-1" role="dialog">
|
<div class="add-link-dialog modal mx-auto" tabindex="-1" role="dialog">
|
||||||
@@ -56,6 +59,14 @@ const TPL = `
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
export default class AddLinkDialog extends BasicWidget {
|
export default class AddLinkDialog extends BasicWidget {
|
||||||
|
private $form!: JQuery<HTMLElement>;
|
||||||
|
private $autoComplete!: JQuery<HTMLElement>;
|
||||||
|
private $linkTitle!: JQuery<HTMLElement>;
|
||||||
|
private $addLinkTitleSettings!: JQuery<HTMLElement>;
|
||||||
|
private $addLinkTitleRadios!: JQuery<HTMLElement>;
|
||||||
|
private $addLinkTitleFormGroup!: JQuery<HTMLElement>;
|
||||||
|
private textTypeWidget: TextTypeWidget | null = null;
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
this.$form = this.$widget.find(".add-link-form");
|
this.$form = this.$widget.find(".add-link-form");
|
||||||
@@ -65,20 +76,17 @@ export default class AddLinkDialog extends BasicWidget {
|
|||||||
this.$addLinkTitleRadios = this.$widget.find(".add-link-title-radios");
|
this.$addLinkTitleRadios = this.$widget.find(".add-link-title-radios");
|
||||||
this.$addLinkTitleFormGroup = this.$widget.find(".add-link-title-form-group");
|
this.$addLinkTitleFormGroup = this.$widget.find(".add-link-title-form-group");
|
||||||
|
|
||||||
/** @var TextTypeWidget */
|
|
||||||
this.textTypeWidget = null;
|
|
||||||
|
|
||||||
this.$form.on("submit", () => {
|
this.$form.on("submit", () => {
|
||||||
if (this.$autoComplete.getSelectedNotePath()) {
|
if (this.$autoComplete.getSelectedNotePath()) {
|
||||||
this.$widget.modal("hide");
|
this.$widget.modal("hide");
|
||||||
|
|
||||||
const linkTitle = this.getLinkType() === "reference-link" ? null : this.$linkTitle.val();
|
const linkTitle = this.getLinkType() === "reference-link" ? null : this.$linkTitle.val();
|
||||||
|
|
||||||
this.textTypeWidget.addLink(this.$autoComplete.getSelectedNotePath(), linkTitle);
|
this.textTypeWidget?.addLink(this.$autoComplete.getSelectedNotePath()!, linkTitle);
|
||||||
} else if (this.$autoComplete.getSelectedExternalLink()) {
|
} else if (this.$autoComplete.getSelectedExternalLink()) {
|
||||||
this.$widget.modal("hide");
|
this.$widget.modal("hide");
|
||||||
|
|
||||||
this.textTypeWidget.addLink(this.$autoComplete.getSelectedExternalLink(), this.$linkTitle.val());
|
this.textTypeWidget?.addLink(this.$autoComplete.getSelectedExternalLink()!, this.$linkTitle.val(), true);
|
||||||
} else {
|
} else {
|
||||||
logError("No link to add.");
|
logError("No link to add.");
|
||||||
}
|
}
|
||||||
@@ -87,12 +95,12 @@ export default class AddLinkDialog extends BasicWidget {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async showAddLinkDialogEvent({ textTypeWidget, text = "" }) {
|
async showAddLinkDialogEvent({ textTypeWidget, text = "" }: EventData<"showAddLinkDialog">) {
|
||||||
this.textTypeWidget = textTypeWidget;
|
this.textTypeWidget = textTypeWidget;
|
||||||
|
|
||||||
this.$addLinkTitleSettings.toggle(!this.textTypeWidget.hasSelection());
|
this.$addLinkTitleSettings.toggle(!this.textTypeWidget.hasSelection());
|
||||||
|
|
||||||
this.$addLinkTitleSettings.find("input[type=radio]").on("change", (e) => this.updateTitleSettingsVisibility(e));
|
this.$addLinkTitleSettings.find("input[type=radio]").on("change", () => this.updateTitleSettingsVisibility());
|
||||||
|
|
||||||
// with selection hyperlink is implied
|
// with selection hyperlink is implied
|
||||||
if (this.textTypeWidget.hasSelection()) {
|
if (this.textTypeWidget.hasSelection()) {
|
||||||
@@ -108,9 +116,8 @@ export default class AddLinkDialog extends BasicWidget {
|
|||||||
this.$autoComplete.val("");
|
this.$autoComplete.val("");
|
||||||
this.$linkTitle.val("");
|
this.$linkTitle.val("");
|
||||||
|
|
||||||
const setDefaultLinkTitle = async (noteId) => {
|
const setDefaultLinkTitle = async (noteId: string) => {
|
||||||
const noteTitle = await treeService.getNoteTitle(noteId);
|
const noteTitle = await treeService.getNoteTitle(noteId);
|
||||||
|
|
||||||
this.$linkTitle.val(noteTitle);
|
this.$linkTitle.val(noteTitle);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,7 +126,7 @@ export default class AddLinkDialog extends BasicWidget {
|
|||||||
allowCreatingNotes: true
|
allowCreatingNotes: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$autoComplete.on("autocomplete:noteselected", (event, suggestion, dataset) => {
|
this.$autoComplete.on("autocomplete:noteselected", (event: JQuery.Event, suggestion: Suggestion) => {
|
||||||
if (!suggestion.notePath) {
|
if (!suggestion.notePath) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -133,7 +140,7 @@ export default class AddLinkDialog extends BasicWidget {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$autoComplete.on("autocomplete:externallinkselected", (event, suggestion, dataset) => {
|
this.$autoComplete.on("autocomplete:externallinkselected", (event: JQuery.Event, suggestion: Suggestion) => {
|
||||||
if (!suggestion.externalLink) {
|
if (!suggestion.externalLink) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -143,11 +150,11 @@ export default class AddLinkDialog extends BasicWidget {
|
|||||||
this.$linkTitle.val(suggestion.externalLink);
|
this.$linkTitle.val(suggestion.externalLink);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$autoComplete.on("autocomplete:cursorchanged", (event, suggestion, dataset) => {
|
this.$autoComplete.on("autocomplete:cursorchanged", (event: JQuery.Event, suggestion: Suggestion) => {
|
||||||
if (suggestion.externalLink) {
|
if (suggestion.externalLink) {
|
||||||
this.$linkTitle.val(suggestion.externalLink);
|
this.$linkTitle.val(suggestion.externalLink);
|
||||||
} else {
|
} else {
|
||||||
const noteId = treeService.getNoteIdFromUrl(suggestion.notePath);
|
const noteId = treeService.getNoteIdFromUrl(suggestion.notePath!);
|
||||||
|
|
||||||
if (noteId) {
|
if (noteId) {
|
||||||
setDefaultLinkTitle(noteId);
|
setDefaultLinkTitle(noteId);
|
||||||
@@ -164,7 +171,7 @@ export default class AddLinkDialog extends BasicWidget {
|
|||||||
this.$autoComplete.trigger("focus").trigger("select"); // to be able to quickly remove entered text
|
this.$autoComplete.trigger("focus").trigger("select"); // to be able to quickly remove entered text
|
||||||
}
|
}
|
||||||
|
|
||||||
getLinkType() {
|
private getLinkType() {
|
||||||
if (this.$autoComplete.getSelectedExternalLink()) {
|
if (this.$autoComplete.getSelectedExternalLink()) {
|
||||||
return "external-link";
|
return "external-link";
|
||||||
}
|
}
|
||||||
@@ -172,7 +179,7 @@ export default class AddLinkDialog extends BasicWidget {
|
|||||||
return this.$addLinkTitleSettings.find("input[type=radio]:checked").val();
|
return this.$addLinkTitleSettings.find("input[type=radio]:checked").val();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTitleSettingsVisibility() {
|
private updateTitleSettingsVisibility() {
|
||||||
const linkType = this.getLinkType();
|
const linkType = this.getLinkType();
|
||||||
|
|
||||||
this.$addLinkTitleFormGroup.toggle(linkType !== "reference-link");
|
this.$addLinkTitleFormGroup.toggle(linkType !== "reference-link");
|
||||||
@@ -250,7 +250,7 @@ ws.subscribeToMessages(async (message) => {
|
|||||||
message: message,
|
message: message,
|
||||||
icon: "arrow-square-up-right"
|
icon: "arrow-square-up-right"
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
if (message.taskType !== "export") {
|
if (message.taskType !== "export") {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -320,7 +320,8 @@ export default class RevisionsDialog extends BasicWidget {
|
|||||||
// as a URL to be used in a note. Instead, if they copy and paste it into a note, it will be uploaded as a new note
|
// as a URL to be used in a note. Instead, if they copy and paste it into a note, it will be uploaded as a new note
|
||||||
.attr("src", `data:${fullRevision.mime};base64,${fullRevision.content}`)
|
.attr("src", `data:${fullRevision.mime};base64,${fullRevision.content}`)
|
||||||
.css("max-width", "100%")
|
.css("max-width", "100%")
|
||||||
.css("max-height", "100%").html()
|
.css("max-height", "100%")
|
||||||
|
.html()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (revisionItem.type === "file") {
|
} else if (revisionItem.type === "file") {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default class CodeButtonsWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
await appContext.tabManager.getActiveContext().setNote(notePath);
|
await appContext.tabManager.getActiveContext()?.setNote(notePath);
|
||||||
|
|
||||||
toastService.showMessage(t("code_buttons.sql_console_saved_message", { notePath: await treeService.getNotePathTitle(notePath) }));
|
toastService.showMessage(t("code_buttons.sql_console_saved_message", { notePath: await treeService.getNotePathTitle(notePath) }));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { t } from "../../services/i18n.js";
|
import { t } from "../../services/i18n.js";
|
||||||
import NoteContextAwareWidget from "../note_context_aware_widget.js"
|
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||||
|
|
||||||
const TPL = `\
|
const TPL = `\
|
||||||
<div class="geo-map-buttons">
|
<div class="geo-map-buttons">
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ export default class BacklinksWidget extends NoteContextAwareWidget {
|
|||||||
if (backlink.relationName) {
|
if (backlink.relationName) {
|
||||||
$item.append($("<p>").text(`${t("zpetne_odkazy.relation")}: ${backlink.relationName}`));
|
$item.append($("<p>").text(`${t("zpetne_odkazy.relation")}: ${backlink.relationName}`));
|
||||||
} else {
|
} else {
|
||||||
$item.append(...backlink.excerpts ?? []);
|
$item.append(...(backlink.excerpts ?? []));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$items.append($item);
|
this.$items.append($item);
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ const TPL = `\
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="geo-map-container"></div>
|
<div class="geo-map-container"></div>
|
||||||
</div>`
|
</div>`;
|
||||||
|
|
||||||
export type Leaflet = typeof import("leaflet");
|
export type Leaflet = typeof import("leaflet");
|
||||||
export type InitCallback = ((L: Leaflet) => void);
|
export type InitCallback = (L: Leaflet) => void;
|
||||||
|
|
||||||
export default class GeoMapWidget extends NoteContextAwareWidget {
|
export default class GeoMapWidget extends NoteContextAwareWidget {
|
||||||
|
|
||||||
@@ -40,8 +40,7 @@ export default class GeoMapWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
this.$container = this.$widget.find(".geo-map-container");
|
this.$container = this.$widget.find(".geo-map-container");
|
||||||
|
|
||||||
library_loader.requireLibrary(library_loader.LEAFLET)
|
library_loader.requireLibrary(library_loader.LEAFLET).then(async () => {
|
||||||
.then(async () => {
|
|
||||||
const L = (await import("leaflet")).default;
|
const L = (await import("leaflet")).default;
|
||||||
|
|
||||||
const map = L.map(this.$container[0], {
|
const map = L.map(this.$container[0], {
|
||||||
@@ -53,7 +52,7 @@ export default class GeoMapWidget extends NoteContextAwareWidget {
|
|||||||
this.initCallback(L);
|
this.initCallback(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||||
detectRetina: true
|
detectRetina: true
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export default class MermaidWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
mermaid.mermaidAPI.initialize({
|
mermaid.mermaidAPI.initialize({
|
||||||
startOnLoad: false,
|
startOnLoad: false,
|
||||||
...getMermaidConfig() as any
|
...(getMermaidConfig() as any)
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$display.empty();
|
this.$display.empty();
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ class MobileDetailMenuWidget extends BasicWidget {
|
|||||||
x: e.pageX,
|
x: e.pageX,
|
||||||
y: e.pageY,
|
y: e.pageY,
|
||||||
items: [
|
items: [
|
||||||
{ title: t("mobile_detail_menu.insert_child_note"), command: "insertChildNote", uiIcon: "bx bx-plus", enabled: note.type !== "search" },
|
{ title: t("mobile_detail_menu.insert_child_note"), command: "insertChildNote", uiIcon: "bx bx-plus", enabled: note?.type !== "search" },
|
||||||
{ title: t("mobile_detail_menu.delete_this_note"), command: "delete", uiIcon: "bx bx-trash", enabled: note.noteId !== "root" }
|
{ title: t("mobile_detail_menu.delete_this_note"), command: "delete", uiIcon: "bx bx-trash", enabled: note?.noteId !== "root" }
|
||||||
],
|
],
|
||||||
selectMenuItemHandler: async ({ command }) => {
|
selectMenuItemHandler: async ({ command }) => {
|
||||||
if (command === "insertChildNote") {
|
if (command === "insertChildNote") {
|
||||||
|
|||||||
@@ -81,7 +81,16 @@ const typeWidgetClasses = {
|
|||||||
* A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one,
|
* A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one,
|
||||||
* for protected session or attachment information.
|
* for protected session or attachment information.
|
||||||
*/
|
*/
|
||||||
type ExtendedNoteType = Exclude<NoteType, "mermaid" | "launcher" | "text" | "code"> | "empty" | "readOnlyCode" | "readOnlyText" | "editableText" | "editableCode" | "attachmentDetail" | "attachmentList" | "protectedSession";
|
type ExtendedNoteType =
|
||||||
|
| Exclude<NoteType, "mermaid" | "launcher" | "text" | "code">
|
||||||
|
| "empty"
|
||||||
|
| "readOnlyCode"
|
||||||
|
| "readOnlyText"
|
||||||
|
| "editableText"
|
||||||
|
| "editableCode"
|
||||||
|
| "attachmentDetail"
|
||||||
|
| "attachmentList"
|
||||||
|
| "protectedSession";
|
||||||
|
|
||||||
export default class NoteDetailWidget extends NoteContextAwareWidget {
|
export default class NoteDetailWidget extends NoteContextAwareWidget {
|
||||||
|
|
||||||
@@ -332,7 +341,9 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
const label = attrs.find(
|
const label = attrs.find(
|
||||||
(attr) =>
|
(attr) =>
|
||||||
attr.type === "label" && ["readOnly", "autoReadOnlyDisabled", "cssClass", "displayRelations", "hideRelations"].includes(attr.name ?? "") && attributeService.isAffecting(attr, this.note)
|
attr.type === "label" &&
|
||||||
|
["readOnly", "autoReadOnlyDisabled", "cssClass", "displayRelations", "hideRelations"].includes(attr.name ?? "") &&
|
||||||
|
attributeService.isAffecting(attr, this.note)
|
||||||
);
|
);
|
||||||
|
|
||||||
const relation = attrs.find((attr) => attr.type === "relation" && ["template", "inherit", "renderNote"].includes(attr.name ?? "") && attributeService.isAffecting(attr, this.note));
|
const relation = attrs.find((attr) => attr.type === "relation" && ["template", "inherit", "renderNote"].includes(attr.name ?? "") && attributeService.isAffecting(attr, this.note));
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ interface NotesAndRelationsData {
|
|||||||
source: string;
|
source: string;
|
||||||
target: string;
|
target: string;
|
||||||
name: string;
|
name: string;
|
||||||
}[]
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace
|
// Replace
|
||||||
@@ -152,7 +152,7 @@ interface ResponseLink {
|
|||||||
|
|
||||||
interface PostNotesMapResponse {
|
interface PostNotesMapResponse {
|
||||||
notes: string[];
|
notes: string[];
|
||||||
links: ResponseLink[],
|
links: ResponseLink[];
|
||||||
noteIdToDescendantCountMap: Record<string, number>;
|
noteIdToDescendantCountMap: Record<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ interface GroupedLink {
|
|||||||
id: string;
|
id: string;
|
||||||
sourceNoteId: string;
|
sourceNoteId: string;
|
||||||
targetNoteId: string;
|
targetNoteId: string;
|
||||||
names: string[]
|
names: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CssData {
|
interface CssData {
|
||||||
@@ -313,9 +313,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
|
|||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
if (node.x && node.y) {
|
if (node.x && node.y) {
|
||||||
ctx.arc(node.x, node.y,
|
ctx.arc(node.x, node.y, this.noteIdToSizeMap[node.id], 0, 2 * Math.PI, false);
|
||||||
this.noteIdToSizeMap[node.id], 0,
|
|
||||||
2 * Math.PI, false);
|
|
||||||
}
|
}
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
})
|
})
|
||||||
@@ -324,7 +322,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
|
|||||||
.warmupTicks(30)
|
.warmupTicks(30)
|
||||||
.onNodeClick((node) => {
|
.onNodeClick((node) => {
|
||||||
if (node.id) {
|
if (node.id) {
|
||||||
appContext.tabManager.getActiveContext().setNote((node as Node).id);
|
appContext.tabManager.getActiveContext()?.setNote((node as Node).id);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.onNodeRightClick((node, e) => {
|
.onNodeRightClick((node, e) => {
|
||||||
@@ -373,7 +371,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
|
|||||||
if (mapRootNoteId === "hoisted") {
|
if (mapRootNoteId === "hoisted") {
|
||||||
mapRootNoteId = hoistedNoteService.getHoistedNoteId();
|
mapRootNoteId = hoistedNoteService.getHoistedNoteId();
|
||||||
} else if (!mapRootNoteId) {
|
} else if (!mapRootNoteId) {
|
||||||
mapRootNoteId = appContext.tabManager.getActiveContext().parentNoteId;
|
mapRootNoteId = appContext.tabManager.getActiveContext()?.parentNoteId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapRootNoteId ?? "";
|
return mapRootNoteId ?? "";
|
||||||
@@ -467,13 +465,13 @@ export default class NoteMapWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (source.x && source.y && target.x && target.y) {
|
if (source.x && source.y && target.x && target.y) {
|
||||||
const x = ((source.x) + (target.x)) / 2;
|
const x = (source.x + target.x) / 2;
|
||||||
const y = ((source.y) + (target.y)) / 2;
|
const y = (source.y + target.y) / 2;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.translate(x, y);
|
ctx.translate(x, y);
|
||||||
|
|
||||||
const deltaY = (source.y) - (target.y);
|
const deltaY = source.y - target.y;
|
||||||
const deltaX = (source.x) - (target.x);
|
const deltaX = source.x - target.x;
|
||||||
|
|
||||||
let angle = Math.atan2(deltaY, deltaX);
|
let angle = Math.atan2(deltaY, deltaX);
|
||||||
let moveY = 2;
|
let moveY = 2;
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export default class NoteTitleWidget extends NoteContextAwareWidget {
|
|||||||
|| utils.isLaunchBarConfig(note.noteId)
|
|| utils.isLaunchBarConfig(note.noteId)
|
||||||
|| this.noteContext?.viewScope?.viewMode !== "default";
|
|| this.noteContext?.viewScope?.viewMode !== "default";
|
||||||
|
|
||||||
this.$noteTitle.val(isReadOnly ? await this.noteContext?.getNavigationTitle() || "" : note.title);
|
this.$noteTitle.val(isReadOnly ? (await this.noteContext?.getNavigationTitle()) || "" : note.title);
|
||||||
this.$noteTitle.prop("readonly", isReadOnly);
|
this.$noteTitle.prop("readonly", isReadOnly);
|
||||||
|
|
||||||
this.setProtectedStatus(note);
|
this.setProtectedStatus(note);
|
||||||
|
|||||||
@@ -159,11 +159,11 @@ interface CreateLauncherResponse {
|
|||||||
message: string;
|
message: string;
|
||||||
note: {
|
note: {
|
||||||
noteId: string;
|
noteId: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExpandedSubtreeResponse {
|
interface ExpandedSubtreeResponse {
|
||||||
branchIds: string[]
|
branchIds: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Node extends Fancytree.NodeData {
|
interface Node extends Fancytree.NodeData {
|
||||||
@@ -180,7 +180,6 @@ interface RefreshContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class NoteTreeWidget extends NoteContextAwareWidget {
|
export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||||
|
|
||||||
private $tree!: JQuery<HTMLElement>;
|
private $tree!: JQuery<HTMLElement>;
|
||||||
private $treeActions!: JQuery<HTMLElement>;
|
private $treeActions!: JQuery<HTMLElement>;
|
||||||
private $treeSettingsButton!: JQuery<HTMLElement>;
|
private $treeSettingsButton!: JQuery<HTMLElement>;
|
||||||
@@ -425,10 +424,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
const activeNoteContext = appContext.tabManager.getActiveContext();
|
const activeNoteContext = appContext.tabManager.getActiveContext();
|
||||||
const opts: SetNoteOpts = {};
|
const opts: SetNoteOpts = {};
|
||||||
if (activeNoteContext.viewScope?.viewMode === "contextual-help") {
|
if (activeNoteContext?.viewScope?.viewMode === "contextual-help") {
|
||||||
opts.viewScope = activeNoteContext.viewScope;
|
opts.viewScope = activeNoteContext.viewScope;
|
||||||
}
|
}
|
||||||
await activeNoteContext.setNote(notePath, opts);
|
await activeNoteContext?.setNote(notePath, opts);
|
||||||
},
|
},
|
||||||
expand: (event, data) => this.setExpanded(data.node.data.branchId, true),
|
expand: (event, data) => this.setExpanded(data.node.data.branchId, true),
|
||||||
collapse: (event, data) => this.setExpanded(data.node.data.branchId, false),
|
collapse: (event, data) => this.setExpanded(data.node.data.branchId, false),
|
||||||
@@ -571,10 +570,13 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
clones: {
|
clones: {
|
||||||
highlightActiveClones: true
|
highlightActiveClones: true
|
||||||
},
|
},
|
||||||
enhanceTitle: async function (event: Event, data: {
|
enhanceTitle: async function (
|
||||||
|
event: Event,
|
||||||
|
data: {
|
||||||
node: Fancytree.FancytreeNode;
|
node: Fancytree.FancytreeNode;
|
||||||
noteId: string;
|
noteId: string;
|
||||||
}) {
|
}
|
||||||
|
) {
|
||||||
const node = data.node;
|
const node = data.node;
|
||||||
|
|
||||||
if (!node.data.noteId) {
|
if (!node.data.noteId) {
|
||||||
@@ -1756,6 +1758,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
appContext.tabManager.getActiveContext().setNote(resp.note.noteId);
|
appContext.tabManager.getActiveContext()?.setNote(resp.note.noteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![ "list", "grid", "calendar"].includes(type)) {
|
if (!["list", "grid", "calendar"].includes(type)) {
|
||||||
throw new Error(t("book_properties.invalid_view_type", { type }));
|
throw new Error(t("book_properties.invalid_view_type", { type }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export default class NoteMapRibbonWidget extends NoteContextAwareWidget {
|
|||||||
const { top } = this.$widget[0].getBoundingClientRect();
|
const { top } = this.$widget[0].getBoundingClientRect();
|
||||||
|
|
||||||
const height = ($(window).height() ?? 0) - top;
|
const height = ($(window).height() ?? 0) - top;
|
||||||
const width = (this.$widget.width() ?? 0);
|
const width = this.$widget.width() ?? 0;
|
||||||
|
|
||||||
this.$widget.find(".note-map-container")
|
this.$widget.find(".note-map-container")
|
||||||
.height(height)
|
.height(height)
|
||||||
|
|||||||
@@ -135,8 +135,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||||
if (loadResults.getBranchRows().find((branch) => branch.noteId === this.noteId) ||
|
if (loadResults.getBranchRows().find((branch) => branch.noteId === this.noteId) || (this.noteId != null && loadResults.isNoteReloaded(this.noteId))) {
|
||||||
(this.noteId != null && loadResults.isNoteReloaded(this.noteId))) {
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ export default class NotePropertiesWidget extends NoteContextAwareWidget {
|
|||||||
async refreshWithNote(note: FNote) {
|
async refreshWithNote(note: FNote) {
|
||||||
const pageUrl = note.getLabelValue("pageUrl");
|
const pageUrl = note.getLabelValue("pageUrl");
|
||||||
|
|
||||||
this.$pageUrl.attr("href", pageUrl).attr("title", pageUrl).text(pageUrl ?? "");
|
this.$pageUrl
|
||||||
|
.attr("href", pageUrl)
|
||||||
|
.attr("title", pageUrl)
|
||||||
|
.text(pageUrl ?? "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ export default class SearchDefinitionWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
await appContext.tabManager.getActiveContext().setNote(notePath);
|
await appContext.tabManager.getActiveContext()?.setNote(notePath);
|
||||||
// Note the {{- notePathTitle}} in json file is not typo, it's unescaping
|
// Note the {{- notePathTitle}} in json file is not typo, it's unescaping
|
||||||
// See https://www.i18next.com/translation-function/interpolation#unescape
|
// See https://www.i18next.com/translation-function/interpolation#unescape
|
||||||
toastService.showMessage(t("search_definition.search_note_saved", { notePathTitle: await treeService.getNotePathTitle(notePath) }));
|
toastService.showMessage(t("search_definition.search_note_saved", { notePathTitle: await treeService.getNotePathTitle(notePath) }));
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ interface SimilarNote {
|
|||||||
noteId: string;
|
noteId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default class SimilarNotesWidget extends NoteContextAwareWidget {
|
export default class SimilarNotesWidget extends NoteContextAwareWidget {
|
||||||
|
|
||||||
private $similarNotesWrapper!: JQuery<HTMLElement>;
|
private $similarNotesWrapper!: JQuery<HTMLElement>;
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ export default class SwitchWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
/** Gets or sets whether the switch is enabled. */
|
/** Gets or sets whether the switch is enabled. */
|
||||||
get canToggle() {
|
get canToggle() {
|
||||||
return (!this.$switchButton.hasClass("disabled"));
|
return !this.$switchButton.hasClass("disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
set canToggle(isEnabled) {
|
set canToggle(isEnabled) {
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export default class SyncStatusWidget extends BasicWidget {
|
|||||||
settings: {
|
settings: {
|
||||||
// TriliumNextTODO: narrow types and use TitlePlacement Type
|
// TriliumNextTODO: narrow types and use TitlePlacement Type
|
||||||
titlePlacement: string;
|
titlePlacement: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@@ -106,7 +106,6 @@ export default class SyncStatusWidget extends BasicWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Tooltip.getOrCreateInstance(this.$widget.find(`.sync-status-${className}`)[0], {
|
Tooltip.getOrCreateInstance(this.$widget.find(`.sync-status-${className}`)[0], {
|
||||||
html: true,
|
html: true,
|
||||||
placement: this.settings.titlePlacement,
|
placement: this.settings.titlePlacement,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const TAB_CONTAINER_MIN_WIDTH = 24;
|
|||||||
const TAB_CONTAINER_MAX_WIDTH = 240;
|
const TAB_CONTAINER_MAX_WIDTH = 240;
|
||||||
const TAB_CONTAINER_LEFT_PADDING = 5;
|
const TAB_CONTAINER_LEFT_PADDING = 5;
|
||||||
const NEW_TAB_WIDTH = 32;
|
const NEW_TAB_WIDTH = 32;
|
||||||
const MIN_FILLER_WIDTH = (isDesktop ? 50 : 15);
|
const MIN_FILLER_WIDTH = isDesktop ? 50 : 15;
|
||||||
const MARGIN_WIDTH = 5;
|
const MARGIN_WIDTH = 5;
|
||||||
|
|
||||||
const TAB_SIZE_SMALL = 84;
|
const TAB_SIZE_SMALL = 84;
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ const TPL = `<div class="toc-widget">
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
interface Toc {
|
interface Toc {
|
||||||
$toc: JQuery<HTMLElement>,
|
$toc: JQuery<HTMLElement>;
|
||||||
headingCount: number
|
headingCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TocWidget extends RightPanelWidget {
|
export default class TocWidget extends RightPanelWidget {
|
||||||
@@ -89,8 +89,8 @@ export default class TocWidget extends RightPanelWidget {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isHelpNote = (this.note.type === "doc" && this.note.noteId.startsWith("_help"));
|
const isHelpNote = this.note.type === "doc" && this.note.noteId.startsWith("_help");
|
||||||
const isTextNote = (this.note.type === "text");
|
const isTextNote = this.note.type === "text";
|
||||||
const isNoteSupported = isTextNote || isHelpNote;
|
const isNoteSupported = isTextNote || isHelpNote;
|
||||||
|
|
||||||
return isNoteSupported
|
return isNoteSupported
|
||||||
@@ -156,7 +156,7 @@ export default class TocWidget extends RightPanelWidget {
|
|||||||
|
|
||||||
const tocLabelValue = this.tocLabelValue;
|
const tocLabelValue = this.tocLabelValue;
|
||||||
|
|
||||||
const visible = (tocLabelValue === "" || tocLabelValue === "show") || headingCount >= (options.getInt("minTocHeadings") ?? 0);
|
const visible = tocLabelValue === "" || tocLabelValue === "show" || headingCount >= (options.getInt("minTocHeadings") ?? 0);
|
||||||
this.toggleInt(visible);
|
this.toggleInt(visible);
|
||||||
if (this.noteContext?.viewScope) {
|
if (this.noteContext?.viewScope) {
|
||||||
this.noteContext.viewScope.tocPreviousVisible = visible;
|
this.noteContext.viewScope.tocPreviousVisible = visible;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
|
|||||||
|
|
||||||
this.$linksWrapper.empty().append(
|
this.$linksWrapper.empty().append(
|
||||||
t("attachment_detail.owning_note"),
|
t("attachment_detail.owning_note"),
|
||||||
(await linkService.createLink(this.noteId)),
|
await linkService.createLink(this.noteId),
|
||||||
t("attachment_detail.you_can_also_open"),
|
t("attachment_detail.you_can_also_open"),
|
||||||
await linkService.createLink(this.noteId, {
|
await linkService.createLink(this.noteId, {
|
||||||
title: t("attachment_detail.list_of_all_attachments"),
|
title: t("attachment_detail.list_of_all_attachments"),
|
||||||
@@ -74,7 +74,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
|
|||||||
$helpButton
|
$helpButton
|
||||||
);
|
);
|
||||||
|
|
||||||
const attachment = (this.attachmentId) ? await froca.getAttachment(this.attachmentId, true) : null;
|
const attachment = this.attachmentId ? await froca.getAttachment(this.attachmentId, true) : null;
|
||||||
|
|
||||||
if (!attachment) {
|
if (!attachment) {
|
||||||
this.$wrapper.html("<strong>" + t("attachment_detail.attachment_deleted") + "</strong>");
|
this.$wrapper.html("<strong>" + t("attachment_detail.attachment_deleted") + "</strong>");
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export default class AttachmentListTypeWidget extends TypeWidget {
|
|||||||
.text(t("attachment_list.upload_attachments"))
|
.text(t("attachment_list.upload_attachments"))
|
||||||
.on("click", () => {
|
.on("click", () => {
|
||||||
if (this.noteId) {
|
if (this.noteId) {
|
||||||
this.triggerCommand("showUploadAttachmentsDialog", { noteId: this.noteId })
|
this.triggerCommand("showUploadAttachmentsDialog", { noteId: this.noteId });
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$helpButton
|
$helpButton
|
||||||
|
|||||||
@@ -36,9 +36,7 @@ export default class BookTypeWidget extends TypeWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async doRefresh(note: FNote) {
|
async doRefresh(note: FNote) {
|
||||||
this.$helpNoChildren.toggle(
|
this.$helpNoChildren.toggle(!this.note?.hasChildren() && this.note?.getAttributeValue("label", "viewType") !== "calendar");
|
||||||
!this.note?.hasChildren()
|
|
||||||
&& this.note?.getAttributeValue("label", "viewType") !== "calendar");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||||
|
|||||||
@@ -59,9 +59,9 @@ const TPL = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
interface CanvasContent {
|
interface CanvasContent {
|
||||||
elements: ExcalidrawElement[],
|
elements: ExcalidrawElement[];
|
||||||
files: BinaryFileData[],
|
files: BinaryFileData[];
|
||||||
appState: Partial<AppState>
|
appState: Partial<AppState>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AttachmentMetadata {
|
interface AttachmentMetadata {
|
||||||
@@ -198,7 +198,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
|||||||
}
|
}
|
||||||
(window.process.env as any).PREACT = false;
|
(window.process.env as any).PREACT = false;
|
||||||
|
|
||||||
const excalidraw = (await import("@excalidraw/excalidraw"));
|
const excalidraw = await import("@excalidraw/excalidraw");
|
||||||
this.excalidrawLib = excalidraw;
|
this.excalidrawLib = excalidraw;
|
||||||
|
|
||||||
const { createRoot } = await import("react-dom/client");
|
const { createRoot } = await import("react-dom/client");
|
||||||
@@ -476,7 +476,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
|||||||
createExcalidrawReactApp(react: typeof React, excalidrawComponent: React.MemoExoticComponent<(props: ExcalidrawProps) => JSX.Element>) {
|
createExcalidrawReactApp(react: typeof React, excalidrawComponent: React.MemoExoticComponent<(props: ExcalidrawProps) => JSX.Element>) {
|
||||||
const excalidrawWrapperRef = react.useRef<HTMLElement>(null);
|
const excalidrawWrapperRef = react.useRef<HTMLElement>(null);
|
||||||
this.excalidrawWrapperRef = excalidrawWrapperRef;
|
this.excalidrawWrapperRef = excalidrawWrapperRef;
|
||||||
const [dimensions, setDimensions] = react.useState<{ width?: number, height?: number}>({
|
const [dimensions, setDimensions] = react.useState<{ width?: number; height?: number }>({
|
||||||
width: undefined,
|
width: undefined,
|
||||||
height: undefined
|
height: undefined
|
||||||
});
|
});
|
||||||
@@ -541,10 +541,6 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
|
|||||||
excalidrawAPI: (api: ExcalidrawImperativeAPI) => {
|
excalidrawAPI: (api: ExcalidrawImperativeAPI) => {
|
||||||
this.excalidrawApi = api;
|
this.excalidrawApi = api;
|
||||||
},
|
},
|
||||||
onPaste: (data: unknown, event: unknown) => {
|
|
||||||
console.log("Verbose: excalidraw internal paste. No trilium action implemented.", data, event);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
onLibraryChange: () => {
|
onLibraryChange: () => {
|
||||||
this.libraryChanged = true;
|
this.libraryChanged = true;
|
||||||
|
|
||||||
|
|||||||
@@ -7,91 +7,78 @@ export function buildConfig() {
|
|||||||
image: {
|
image: {
|
||||||
styles: {
|
styles: {
|
||||||
options: [
|
options: [
|
||||||
'inline',
|
"inline",
|
||||||
'alignBlockLeft',
|
"alignBlockLeft",
|
||||||
'alignCenter',
|
"alignCenter",
|
||||||
'alignBlockRight',
|
"alignBlockRight",
|
||||||
'alignLeft',
|
"alignLeft",
|
||||||
'alignRight',
|
"alignRight",
|
||||||
'full', // full and side are for BC since the old images have been created with these styles
|
"full", // full and side are for BC since the old images have been created with these styles
|
||||||
'side'
|
"side"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
resizeOptions: [
|
resizeOptions: [
|
||||||
{
|
{
|
||||||
name: 'imageResize:original',
|
name: "imageResize:original",
|
||||||
value: null,
|
value: null,
|
||||||
icon: 'original'
|
icon: "original"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'imageResize:25',
|
name: "imageResize:25",
|
||||||
value: '25',
|
value: "25",
|
||||||
icon: 'small'
|
icon: "small"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'imageResize:50',
|
name: "imageResize:50",
|
||||||
value: '50',
|
value: "50",
|
||||||
icon: 'medium'
|
icon: "medium"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'imageResize:75',
|
name: "imageResize:75",
|
||||||
value: '75',
|
value: "75",
|
||||||
icon: 'medium'
|
icon: "medium"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
toolbar: [
|
toolbar: [
|
||||||
// Image styles, see https://ckeditor.com/docs/ckeditor5/latest/features/images/images-styles.html#demo.
|
// Image styles, see https://ckeditor.com/docs/ckeditor5/latest/features/images/images-styles.html#demo.
|
||||||
'imageStyle:inline',
|
"imageStyle:inline",
|
||||||
'imageStyle:alignCenter',
|
"imageStyle:alignCenter",
|
||||||
{
|
{
|
||||||
name: "imageStyle:wrapText",
|
name: "imageStyle:wrapText",
|
||||||
title: "Wrap text",
|
title: "Wrap text",
|
||||||
items: [
|
items: ["imageStyle:alignLeft", "imageStyle:alignRight"],
|
||||||
'imageStyle:alignLeft',
|
defaultItem: "imageStyle:alignRight"
|
||||||
'imageStyle:alignRight',
|
|
||||||
],
|
|
||||||
defaultItem: 'imageStyle:alignRight'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "imageStyle:block",
|
name: "imageStyle:block",
|
||||||
title: "Block align",
|
title: "Block align",
|
||||||
items: [
|
items: ["imageStyle:alignBlockLeft", "imageStyle:alignBlockRight"],
|
||||||
'imageStyle:alignBlockLeft',
|
defaultItem: "imageStyle:alignBlockLeft"
|
||||||
'imageStyle:alignBlockRight'
|
|
||||||
],
|
|
||||||
defaultItem: "imageStyle:alignBlockLeft",
|
|
||||||
},
|
},
|
||||||
'|',
|
"|",
|
||||||
'imageResize:25',
|
"imageResize:25",
|
||||||
'imageResize:50',
|
"imageResize:50",
|
||||||
'imageResize:original',
|
"imageResize:original",
|
||||||
'|',
|
"|",
|
||||||
'toggleImageCaption'
|
"toggleImageCaption"
|
||||||
],
|
],
|
||||||
upload: {
|
upload: {
|
||||||
types: [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff', 'svg', 'svg+xml', 'avif' ]
|
types: ["jpeg", "png", "gif", "bmp", "webp", "tiff", "svg", "svg+xml", "avif"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
heading: {
|
heading: {
|
||||||
options: [
|
options: [
|
||||||
{ model: 'paragraph' as const, title: 'Paragraph', class: 'ck-heading_paragraph' },
|
{ model: "paragraph" as const, title: "Paragraph", class: "ck-heading_paragraph" },
|
||||||
// // heading1 is not used since that should be a note's title
|
// // heading1 is not used since that should be a note's title
|
||||||
{ model: 'heading2' as const, view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
|
{ model: "heading2" as const, view: "h2", title: "Heading 2", class: "ck-heading_heading2" },
|
||||||
{ model: 'heading3' as const, view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
|
{ model: "heading3" as const, view: "h3", title: "Heading 3", class: "ck-heading_heading3" },
|
||||||
{ model: 'heading4' as const, view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
|
{ model: "heading4" as const, view: "h4", title: "Heading 4", class: "ck-heading_heading4" },
|
||||||
{ model: 'heading5' as const, view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' },
|
{ model: "heading5" as const, view: "h5", title: "Heading 5", class: "ck-heading_heading5" },
|
||||||
{ model: 'heading6' as const, view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' }
|
{ model: "heading6" as const, view: "h6", title: "Heading 6", class: "ck-heading_heading6" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
contentToolbar: [
|
contentToolbar: ["tableColumn", "tableRow", "mergeTableCells", "tableProperties", "tableCellProperties", "toggleTableCaption"]
|
||||||
'tableColumn',
|
|
||||||
'tableRow',
|
|
||||||
'mergeTableCells',
|
|
||||||
'tableProperties',
|
|
||||||
'tableCellProperties',
|
|
||||||
'toggleTableCaption'
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
properties: {
|
properties: {
|
||||||
@@ -101,17 +88,17 @@ export function buildConfig() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
defaultProtocol: 'https://',
|
defaultProtocol: "https://",
|
||||||
allowedProtocols: ALLOWED_PROTOCOLS
|
allowedProtocols: ALLOWED_PROTOCOLS
|
||||||
},
|
},
|
||||||
// This value must be kept in sync with the language defined in webpack.config.js.
|
// This value must be kept in sync with the language defined in webpack.config.js.
|
||||||
language: 'en'
|
language: "en"
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildToolbarConfig(isClassicToolbar: boolean) {
|
export function buildToolbarConfig(isClassicToolbar: boolean) {
|
||||||
if (isClassicToolbar) {
|
if (isClassicToolbar) {
|
||||||
const multilineToolbar = utils.isDesktop() && options.get("textNoteEditorMultilineToolbar") === "true"
|
const multilineToolbar = utils.isDesktop() && options.get("textNoteEditorMultilineToolbar") === "true";
|
||||||
return buildClassicToolbar(multilineToolbar);
|
return buildClassicToolbar(multilineToolbar);
|
||||||
} else {
|
} else {
|
||||||
return buildFloatingToolbar();
|
return buildFloatingToolbar();
|
||||||
@@ -123,101 +110,92 @@ function buildClassicToolbar(multilineToolbar: boolean) {
|
|||||||
return {
|
return {
|
||||||
toolbar: {
|
toolbar: {
|
||||||
items: [
|
items: [
|
||||||
'heading', 'fontSize',
|
"heading",
|
||||||
'|',
|
"fontSize",
|
||||||
'bold', 'italic',
|
"|",
|
||||||
|
"bold",
|
||||||
|
"italic",
|
||||||
{
|
{
|
||||||
label: "Text formatting",
|
label: "Text formatting",
|
||||||
icon: "text",
|
icon: "text",
|
||||||
items: [
|
items: ["underline", "strikethrough", "superscript", "subscript", "code"]
|
||||||
'underline',
|
|
||||||
'strikethrough',
|
|
||||||
'superscript',
|
|
||||||
'subscript',
|
|
||||||
'code',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'|',
|
"|",
|
||||||
'fontColor', 'fontBackgroundColor', 'removeFormat',
|
"fontColor",
|
||||||
'|',
|
"fontBackgroundColor",
|
||||||
'bulletedList', 'numberedList', 'todoList',
|
"removeFormat",
|
||||||
'|',
|
"|",
|
||||||
'blockQuote', 'insertTable', 'codeBlock', 'footnote',
|
"bulletedList",
|
||||||
|
"numberedList",
|
||||||
|
"todoList",
|
||||||
|
"|",
|
||||||
|
"blockQuote",
|
||||||
|
"insertTable",
|
||||||
|
"codeBlock",
|
||||||
|
"footnote",
|
||||||
{
|
{
|
||||||
label: "Insert",
|
label: "Insert",
|
||||||
icon: "plus",
|
icon: "plus",
|
||||||
items: [
|
items: ["imageUpload", "|", "link", "internallink", "includeNote", "|", "specialCharacters", "math", "mermaid", "horizontalLine", "pageBreak"]
|
||||||
'imageUpload',
|
|
||||||
'|',
|
|
||||||
'link',
|
|
||||||
'internallink',
|
|
||||||
'includeNote',
|
|
||||||
'|',
|
|
||||||
'specialCharacters',
|
|
||||||
'math',
|
|
||||||
'mermaid',
|
|
||||||
'horizontalLine',
|
|
||||||
'pageBreak'
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
'|',
|
"|",
|
||||||
'outdent', 'indent',
|
"outdent",
|
||||||
'|',
|
"indent",
|
||||||
'markdownImport', 'cuttonote', 'findAndReplace'
|
"|",
|
||||||
|
"markdownImport",
|
||||||
|
"cuttonote",
|
||||||
|
"findAndReplace"
|
||||||
],
|
],
|
||||||
shouldNotGroupWhenFull: multilineToolbar
|
shouldNotGroupWhenFull: multilineToolbar
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFloatingToolbar() {
|
function buildFloatingToolbar() {
|
||||||
return {
|
return {
|
||||||
toolbar: {
|
toolbar: {
|
||||||
items: [
|
items: [
|
||||||
'fontSize',
|
"fontSize",
|
||||||
'bold',
|
"bold",
|
||||||
'italic',
|
"italic",
|
||||||
'underline',
|
"underline",
|
||||||
'strikethrough',
|
"strikethrough",
|
||||||
'superscript',
|
"superscript",
|
||||||
'subscript',
|
"subscript",
|
||||||
'fontColor',
|
"fontColor",
|
||||||
'fontBackgroundColor',
|
"fontBackgroundColor",
|
||||||
'code',
|
"code",
|
||||||
'link',
|
"link",
|
||||||
'removeFormat',
|
"removeFormat",
|
||||||
'internallink',
|
"internallink",
|
||||||
'cuttonote'
|
"cuttonote"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
blockToolbar: [
|
blockToolbar: [
|
||||||
'heading',
|
"heading",
|
||||||
'|',
|
"|",
|
||||||
'bulletedList', 'numberedList', 'todoList',
|
"bulletedList",
|
||||||
'|',
|
"numberedList",
|
||||||
'blockQuote', 'codeBlock', 'insertTable',
|
"todoList",
|
||||||
'footnote',
|
"|",
|
||||||
|
"blockQuote",
|
||||||
|
"codeBlock",
|
||||||
|
"insertTable",
|
||||||
|
"footnote",
|
||||||
{
|
{
|
||||||
label: "Insert",
|
label: "Insert",
|
||||||
icon: "plus",
|
icon: "plus",
|
||||||
items: [
|
items: ["internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak"]
|
||||||
'internallink',
|
|
||||||
'includeNote',
|
|
||||||
'|',
|
|
||||||
'math',
|
|
||||||
'mermaid',
|
|
||||||
'horizontalLine',
|
|
||||||
'pageBreak'
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
'|',
|
"|",
|
||||||
'outdent', 'indent',
|
"outdent",
|
||||||
'|',
|
"indent",
|
||||||
'imageUpload',
|
"|",
|
||||||
'markdownImport',
|
"imageUpload",
|
||||||
'specialCharacters',
|
"markdownImport",
|
||||||
'findAndReplace'
|
"specialCharacters",
|
||||||
|
"findAndReplace"
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import CodeMimeTypesOptions from "./options/code_notes/code_mime_types.js";
|
|||||||
import ImageOptions from "./options/images/images.js";
|
import ImageOptions from "./options/images/images.js";
|
||||||
import SpellcheckOptions from "./options/spellcheck.js";
|
import SpellcheckOptions from "./options/spellcheck.js";
|
||||||
import PasswordOptions from "./options/password/password.js";
|
import PasswordOptions from "./options/password/password.js";
|
||||||
import ProtectedSessionTimeoutOptions from "./options/password/protected_session_timeout.js"
|
import ProtectedSessionTimeoutOptions from "./options/password/protected_session_timeout.js";
|
||||||
import EtapiOptions from "./options/etapi.js";
|
import EtapiOptions from "./options/etapi.js";
|
||||||
import BackupOptions from "./options/backup.js";
|
import BackupOptions from "./options/backup.js";
|
||||||
import SyncOptions from "./options/sync.js";
|
import SyncOptions from "./options/sync.js";
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
this.watchdog?.editor.editing.view.focus();
|
this.watchdog?.editor.editing.view.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {}
|
show() { }
|
||||||
|
|
||||||
getEditor() {
|
getEditor() {
|
||||||
return this.watchdog?.editor;
|
return this.watchdog?.editor;
|
||||||
@@ -360,14 +360,14 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
this.addTextToEditor(text);
|
this.addTextToEditor(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addLink(notePath, linkTitle) {
|
async addLink(notePath, linkTitle, externalLink = false) {
|
||||||
await this.initialized;
|
await this.initialized;
|
||||||
|
|
||||||
if (linkTitle) {
|
if (linkTitle) {
|
||||||
if (this.hasSelection()) {
|
if (this.hasSelection()) {
|
||||||
this.watchdog.editor.execute("link", `#${notePath}`);
|
this.watchdog.editor.execute("link", externalLink ? `${notePath}` : `#${notePath}`);
|
||||||
} else {
|
} else {
|
||||||
await this.addLinkToEditor(`#${notePath}`, linkTitle);
|
await this.addLinkToEditor(externalLink ? `${notePath}` : `#${notePath}`, linkTitle);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.watchdog.editor.execute("referenceLink", { href: "#" + notePath });
|
this.watchdog.editor.execute("referenceLink", { href: "#" + notePath });
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { GPX, Marker, type LatLng, type LeafletMouseEvent } from "leaflet";
|
import { GPX, Marker, type LatLng, type LeafletMouseEvent } from "leaflet";
|
||||||
import type FNote from "../../entities/fnote.js";
|
import type FNote from "../../entities/fnote.js";
|
||||||
import GeoMapWidget, { type InitCallback, type Leaflet } from "../geo_map.js";
|
import GeoMapWidget, { type InitCallback, type Leaflet } from "../geo_map.js";
|
||||||
import TypeWidget from "./type_widget.js"
|
import TypeWidget from "./type_widget.js";
|
||||||
import server from "../../services/server.js";
|
import server from "../../services/server.js";
|
||||||
import toastService from "../../services/toast.js";
|
import toastService from "../../services/toast.js";
|
||||||
import dialogService from "../../services/dialog.js";
|
import dialogService from "../../services/dialog.js";
|
||||||
@@ -75,21 +75,21 @@ const TPL = `\
|
|||||||
|
|
||||||
const LOCATION_ATTRIBUTE = "geolocation";
|
const LOCATION_ATTRIBUTE = "geolocation";
|
||||||
const CHILD_NOTE_ICON = "bx bx-pin";
|
const CHILD_NOTE_ICON = "bx bx-pin";
|
||||||
const DEFAULT_COORDINATES: [ number, number ] = [ 3.878638227135724, 446.6630455551659 ];
|
const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659];
|
||||||
const DEFAULT_ZOOM = 2;
|
const DEFAULT_ZOOM = 2;
|
||||||
|
|
||||||
interface MapData {
|
interface MapData {
|
||||||
view?: {
|
view?: {
|
||||||
center?: LatLng | [ number, number ];
|
center?: LatLng | [number, number];
|
||||||
zoom?: number;
|
zoom?: number;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Deduplicate
|
// TODO: Deduplicate
|
||||||
interface CreateChildResponse {
|
interface CreateChildResponse {
|
||||||
note: {
|
note: {
|
||||||
noteId: string;
|
noteId: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
@@ -220,7 +220,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ lat, lng ] = latLng.split(",", 2).map((el) => parseFloat(el));
|
const [lat, lng] = latLng.split(",", 2).map((el) => parseFloat(el));
|
||||||
const L = this.L;
|
const L = this.L;
|
||||||
const icon = this.#buildIcon(note.getIcon(), note.getColorClass(), note.title);
|
const icon = this.#buildIcon(note.getIcon(), note.getColorClass(), note.title);
|
||||||
|
|
||||||
@@ -228,10 +228,10 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
|||||||
icon,
|
icon,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
autoPan: true,
|
autoPan: true,
|
||||||
autoPanSpeed: 5,
|
autoPanSpeed: 5
|
||||||
})
|
})
|
||||||
.addTo(map)
|
.addTo(map)
|
||||||
.on("moveend", e => {
|
.on("moveend", (e) => {
|
||||||
this.moveMarker(note.noteId, (e.target as Marker).getLatLng());
|
this.moveMarker(note.noteId, (e.target as Marker).getLatLng());
|
||||||
});
|
});
|
||||||
marker.on("mousedown", ({ originalEvent }) => {
|
marker.on("mousedown", ({ originalEvent }) => {
|
||||||
@@ -264,9 +264,9 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
|||||||
<img class="icon-shadow" src="${asset_path}/node_modules/leaflet/dist/images/marker-shadow.png" />
|
<img class="icon-shadow" src="${asset_path}/node_modules/leaflet/dist/images/marker-shadow.png" />
|
||||||
<span class="bx ${bxIconClass} ${colorClass}"></span>
|
<span class="bx ${bxIconClass} ${colorClass}"></span>
|
||||||
<span class="title-label">${title}</span>`,
|
<span class="title-label">${title}</span>`,
|
||||||
iconSize: [ 25, 41 ],
|
iconSize: [25, 41],
|
||||||
iconAnchor: [ 12, 41 ]
|
iconAnchor: [12, 41]
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#changeState(newState: State) {
|
#changeState(newState: State) {
|
||||||
@@ -296,7 +296,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async moveMarker(noteId: string, latLng: LatLng | null) {
|
async moveMarker(noteId: string, latLng: LatLng | null) {
|
||||||
const value = (latLng ? [latLng.lat, latLng.lng].join(",") : "");
|
const value = latLng ? [latLng.lat, latLng.lng].join(",") : "";
|
||||||
await attributes.setLabel(noteId, LOCATION_ATTRIBUTE, value);
|
await attributes.setLabel(noteId, LOCATION_ATTRIBUTE, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +361,7 @@ export default class GeoMapTypeWidget extends TypeWidget {
|
|||||||
// If any of note has its location attribute changed.
|
// If any of note has its location attribute changed.
|
||||||
// TODO: Should probably filter by parent here as well.
|
// TODO: Should probably filter by parent here as well.
|
||||||
const attributeRows = loadResults.getAttributeRows();
|
const attributeRows = loadResults.getAttributeRows();
|
||||||
if (attributeRows.find((at) => [ LOCATION_ATTRIBUTE, "color" ].includes(at.name ?? ""))) {
|
if (attributeRows.find((at) => [LOCATION_ATTRIBUTE, "color"].includes(at.name ?? ""))) {
|
||||||
this.#reloadMarkers();
|
this.#reloadMarkers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ export default class MindMapWidget extends TypeWidget {
|
|||||||
return await this.mind.exportSvg().text();
|
return await this.mind.exportSvg().text();
|
||||||
}
|
}
|
||||||
|
|
||||||
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded"> ) {
|
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||||
if (this.noteId && loadResults.isNoteReloaded(this.noteId)) {
|
if (this.noteId && loadResults.isNoteReloaded(this.noteId)) {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ interface Theme {
|
|||||||
val: string;
|
val: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response = Record<string, Theme[]>
|
type Response = Record<string, Theme[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains appearance settings for code blocks within text notes, such as the theme for the syntax highlighter.
|
* Contains appearance settings for code blocks within text notes, such as the theme for the syntax highlighter.
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const TPL = `
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-micro restart-app-button">${t("electron_integration.restart-app-button")}</button>
|
<button class="btn btn-secondary btn-micro restart-app-button">${t("electron_integration.restart-app-button")}</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -94,10 +94,12 @@ export default class ThemeOptions extends OptionsWidget {
|
|||||||
this.$themeSelect.empty();
|
this.$themeSelect.empty();
|
||||||
|
|
||||||
for (const theme of themes) {
|
for (const theme of themes) {
|
||||||
this.$themeSelect.append($("<option>")
|
this.$themeSelect.append(
|
||||||
|
$("<option>")
|
||||||
.attr("value", theme.val)
|
.attr("value", theme.val)
|
||||||
.attr("data-note-id", theme.noteId || "")
|
.attr("data-note-id", theme.noteId || "")
|
||||||
.text(theme.title));
|
.text(theme.title)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$themeSelect.val(options.theme);
|
this.$themeSelect.val(options.theme);
|
||||||
|
|||||||
@@ -97,9 +97,8 @@ export default class CodeMimeTypesOptions extends OptionsWidget {
|
|||||||
const checkbox = $(`<label class="tn-checkbox">`)
|
const checkbox = $(`<label class="tn-checkbox">`)
|
||||||
.append($('<input type="checkbox" class="form-check-input">').attr("id", id).attr("data-mime-type", mimeType.mime).prop("checked", mimeType.enabled))
|
.append($('<input type="checkbox" class="form-check-input">').attr("id", id).attr("data-mime-type", mimeType.mime).prop("checked", mimeType.enabled))
|
||||||
.on("change", () => this.save())
|
.on("change", () => this.save())
|
||||||
.append(mimeType.title)
|
.append(mimeType.title);
|
||||||
|
|
||||||
return $("<li>")
|
return $("<li>").append(checkbox);
|
||||||
.append(checkbox);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,12 +102,19 @@ export const DEFAULT_ALLOWED_TAGS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const TPL = `
|
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>
|
<h4>${t("import.html_import_tags.title")}</h4>
|
||||||
|
|
||||||
<p>${t("import.html_import_tags.description")}</p>
|
<p>${t("import.html_import_tags.description")}</p>
|
||||||
|
|
||||||
<textarea class="allowed-html-tags form-control" style="height: 150px; font-family: monospace;"
|
<textarea class="allowed-html-tags form-control" spellcheck="false"
|
||||||
placeholder="${t("import.html_import_tags.placeholder")}"></textarea>
|
placeholder="${t("import.html_import_tags.placeholder")}"></textarea>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default class NoteErasureTimeoutOptions extends TimeSelector {
|
|||||||
const $timeSelector = this.$widget;
|
const $timeSelector = this.$widget;
|
||||||
// inject TimeSelector widget template
|
// inject TimeSelector widget template
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
|
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector);
|
||||||
|
|
||||||
this.$eraseDeletedNotesButton = this.$widget.find("#erase-deleted-notes-now-button");
|
this.$eraseDeletedNotesButton = this.$widget.find("#erase-deleted-notes-now-button");
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,6 @@ export default class RevisionsSnapshotIntervalOptions extends TimeSelector {
|
|||||||
const $timeSelector = this.$widget;
|
const $timeSelector = this.$widget;
|
||||||
// inject TimeSelector widget template
|
// inject TimeSelector widget template
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
|
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export default class ShareSettingsOptions extends OptionsWidget {
|
|||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
this.contentSized();
|
this.contentSized();
|
||||||
|
|
||||||
this.$shareRootCheck = this.$widget.find('.share-root-check');
|
this.$shareRootCheck = this.$widget.find(".share-root-check");
|
||||||
this.$shareRootStatus = this.$widget.find('.share-root-status');
|
this.$shareRootStatus = this.$widget.find(".share-root-status");
|
||||||
|
|
||||||
// Add change handlers for both checkboxes
|
// Add change handlers for both checkboxes
|
||||||
this.$widget.find('input[type="checkbox"]').on("change", (e: JQuery.ChangeEvent) => {
|
this.$widget.find('input[type="checkbox"]').on("change", (e: JQuery.ChangeEvent) => {
|
||||||
@@ -38,7 +38,7 @@ export default class ShareSettingsOptions extends OptionsWidget {
|
|||||||
|
|
||||||
// Show/hide share root status section based on redirectBareDomain checkbox
|
// Show/hide share root status section based on redirectBareDomain checkbox
|
||||||
const target = e.target as HTMLInputElement;
|
const target = e.target as HTMLInputElement;
|
||||||
if (target.name === 'redirectBareDomain') {
|
if (target.name === "redirectBareDomain") {
|
||||||
this.$shareRootCheck.toggle(target.checked);
|
this.$shareRootCheck.toggle(target.checked);
|
||||||
if (target.checked) {
|
if (target.checked) {
|
||||||
this.checkShareRoot();
|
this.checkShareRoot();
|
||||||
@@ -47,7 +47,7 @@ export default class ShareSettingsOptions extends OptionsWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add click handler for check share root button
|
// Add click handler for check share root button
|
||||||
this.$widget.find('.check-share-root').on("click", () => this.checkShareRoot());
|
this.$widget.find(".check-share-root").on("click", () => this.checkShareRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
async optionsLoaded(options: OptionMap) {
|
async optionsLoaded(options: OptionMap) {
|
||||||
@@ -62,28 +62,26 @@ export default class ShareSettingsOptions extends OptionsWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async checkShareRoot() {
|
async checkShareRoot() {
|
||||||
const $button = this.$widget.find('.check-share-root');
|
const $button = this.$widget.find(".check-share-root");
|
||||||
$button.prop('disabled', true);
|
$button.prop("disabled", true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const shareRootNotes = await searchService.searchForNotes("#shareRoot");
|
const shareRootNotes = await searchService.searchForNotes("#shareRoot");
|
||||||
const sharedShareRootNote = shareRootNotes.find(note => note.isShared());
|
const sharedShareRootNote = shareRootNotes.find((note) => note.isShared());
|
||||||
|
|
||||||
if (sharedShareRootNote) {
|
if (sharedShareRootNote) {
|
||||||
this.$shareRootStatus
|
this.$shareRootStatus
|
||||||
.removeClass('text-danger')
|
.removeClass("text-danger")
|
||||||
.addClass('text-success')
|
.addClass("text-success")
|
||||||
.text(t("share.share_root_found", {noteTitle: sharedShareRootNote.title}));
|
.text(t("share.share_root_found", { noteTitle: sharedShareRootNote.title }));
|
||||||
} else {
|
} else {
|
||||||
this.$shareRootStatus
|
this.$shareRootStatus
|
||||||
.removeClass('text-success')
|
.removeClass("text-success")
|
||||||
.addClass('text-danger')
|
.addClass("text-danger")
|
||||||
.text(shareRootNotes.length > 0
|
.text(shareRootNotes.length > 0 ? t("share.share_root_not_shared", { noteTitle: shareRootNotes[0].title }) : t("share.share_root_not_found"));
|
||||||
? t("share.share_root_not_shared", {noteTitle: shareRootNotes[0].title})
|
|
||||||
: t("share.share_root_not_found"));
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
$button.prop('disabled', false);
|
$button.prop("disabled", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,6 @@ export default class ProtectedSessionTimeoutOptions extends TimeSelector {
|
|||||||
const $timeSelector = this.$widget;
|
const $timeSelector = this.$widget;
|
||||||
// inject TimeSelector widget template
|
// inject TimeSelector widget template
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector)
|
this.$widget.find("#time-selector-placeholder").replaceWith($timeSelector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default class TimeSelector extends OptionsWidget {
|
|||||||
this.optionValueId = options.optionValueId;
|
this.optionValueId = options.optionValueId;
|
||||||
this.optionTimeScaleId = options.optionTimeScaleId;
|
this.optionTimeScaleId = options.optionTimeScaleId;
|
||||||
this.includedTimeScales = options.includedTimeScales || new Set(["seconds", "minutes", "hours", "days"]);
|
this.includedTimeScales = options.includedTimeScales || new Set(["seconds", "minutes", "hours", "days"]);
|
||||||
this.minimumSeconds = options.minimumSeconds || 0
|
this.minimumSeconds = options.minimumSeconds || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
@@ -131,10 +131,10 @@ export default class TimeSelector extends OptionsWidget {
|
|||||||
|
|
||||||
private setInternalTimeInSeconds(time: number) {
|
private setInternalTimeInSeconds(time: number) {
|
||||||
if (time < this.minimumSeconds) {
|
if (time < this.minimumSeconds) {
|
||||||
toastService.showError(t("time_selector.minimum_input", {minimumSeconds: this.minimumSeconds}));
|
toastService.showError(t("time_selector.minimum_input", { minimumSeconds: this.minimumSeconds }));
|
||||||
return this.internalTimeInSeconds = this.minimumSeconds;
|
return (this.internalTimeInSeconds = this.minimumSeconds);
|
||||||
}
|
}
|
||||||
return this.internalTimeInSeconds = time;
|
return (this.internalTimeInSeconds = time);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ const TPL = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
function buildTasks(tasks: FTask[]) {
|
function buildTasks(tasks: FTask[]) {
|
||||||
let html = '';
|
let html = "";
|
||||||
|
|
||||||
const now = dayjs();
|
const now = dayjs();
|
||||||
const dateFormat = "DD-MM-YYYY";
|
const dateFormat = "DD-MM-YYYY";
|
||||||
@@ -137,7 +137,9 @@ export default class TaskListWidget extends TypeWidget {
|
|||||||
private $taskContainer!: JQuery<HTMLElement>;
|
private $taskContainer!: JQuery<HTMLElement>;
|
||||||
private $addNewTask!: JQuery<HTMLElement>;
|
private $addNewTask!: JQuery<HTMLElement>;
|
||||||
|
|
||||||
static getType() { return "taskList" }
|
static getType() {
|
||||||
|
return "taskList";
|
||||||
|
}
|
||||||
|
|
||||||
doRender() {
|
doRender() {
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
@@ -231,8 +233,7 @@ export default class TaskListWidget extends TypeWidget {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await froca.getTasks(this.noteId))
|
return (await froca.getTasks(this.noteId)).toSorted((a, b) => {
|
||||||
.toSorted((a, b) => {
|
|
||||||
// Sort by due date, closest date first.
|
// Sort by due date, closest date first.
|
||||||
if (!a.dueDate) {
|
if (!a.dueDate) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const TPL = `
|
|||||||
interface CreateChildResponse {
|
interface CreateChildResponse {
|
||||||
note: {
|
note: {
|
||||||
noteId: string;
|
noteId: string;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CalendarView extends ViewMode {
|
export default class CalendarView extends ViewMode {
|
||||||
@@ -126,7 +126,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
weekNumbers: this.parentNote.hasAttribute("label", "calendar:weekNumbers"),
|
weekNumbers: this.parentNote.hasAttribute("label", "calendar:weekNumbers"),
|
||||||
locale: await CalendarView.#getLocale(),
|
locale: await CalendarView.#getLocale(),
|
||||||
height: "100%",
|
height: "100%",
|
||||||
eventContent: (e => {
|
eventContent: (e) => {
|
||||||
let html = "";
|
let html = "";
|
||||||
const { iconClass, promotedAttributes } = e.event.extendedProps;
|
const { iconClass, promotedAttributes } = e.event.extendedProps;
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
|
|
||||||
// Promoted attributes
|
// Promoted attributes
|
||||||
if (promotedAttributes) {
|
if (promotedAttributes) {
|
||||||
for (const [ name, value ] of Object.entries(promotedAttributes)) {
|
for (const [name, value] of Object.entries(promotedAttributes)) {
|
||||||
html += `\
|
html += `\
|
||||||
<div class="promoted-attribute">
|
<div class="promoted-attribute">
|
||||||
<span class="promoted-attribute-name">${name}</span>: <span class="promoted-attribute-value">${value}</span>
|
<span class="promoted-attribute-name">${name}</span>: <span class="promoted-attribute-value">${value}</span>
|
||||||
@@ -147,7 +147,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return { html };
|
return { html };
|
||||||
}),
|
},
|
||||||
dateClick: async (e) => {
|
dateClick: async (e) => {
|
||||||
if (!this.isCalendarRoot) {
|
if (!this.isCalendarRoot) {
|
||||||
return;
|
return;
|
||||||
@@ -155,7 +155,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
|
|
||||||
const note = await date_notes.getDayNote(e.dateStr);
|
const note = await date_notes.getDayNote(e.dateStr);
|
||||||
if (note) {
|
if (note) {
|
||||||
appContext.tabManager.getActiveContext().setNote(note.noteId);
|
appContext.tabManager.getActiveContext()?.setNote(note.noteId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -260,7 +260,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
// TODO: Deduplicate get type.
|
// TODO: Deduplicate get type.
|
||||||
const dateNotesForMonth = await server.get<Record<string, string>>(`special-notes/notes-for-month/${month}?calendarRoot=${this.parentNote.noteId}`);
|
const dateNotesForMonth = await server.get<Record<string, string>>(`special-notes/notes-for-month/${month}?calendarRoot=${this.parentNote.noteId}`);
|
||||||
const dateNoteIds = Object.values(dateNotesForMonth);
|
const dateNoteIds = Object.values(dateNotesForMonth);
|
||||||
allDateNoteIds = [ ...allDateNoteIds, ...dateNoteIds ];
|
allDateNoteIds = [...allDateNoteIds, ...dateNoteIds];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request all the date notes.
|
// Request all the date notes.
|
||||||
@@ -379,7 +379,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
const result: Record<string, string> = {};
|
const result: Record<string, string> = {};
|
||||||
|
|
||||||
for (const promotedAttribute of filteredPromotedAttributes) {
|
for (const promotedAttribute of filteredPromotedAttributes) {
|
||||||
const [ type, name ] = promotedAttribute.name.split(":", 2);
|
const [type, name] = promotedAttribute.name.split(":", 2);
|
||||||
const definition = promotedAttribute.getDefinition();
|
const definition = promotedAttribute.getDefinition();
|
||||||
|
|
||||||
if (definition.multiplicity !== "single") {
|
if (definition.multiplicity !== "single") {
|
||||||
@@ -411,7 +411,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
if (customTitleValue.startsWith("#")) {
|
if (customTitleValue.startsWith("#")) {
|
||||||
const labelValue = note.getAttributeValue("label", attributeName);
|
const labelValue = note.getAttributeValue("label", attributeName);
|
||||||
if (labelValue) {
|
if (labelValue) {
|
||||||
return [ labelValue ];
|
return [labelValue];
|
||||||
}
|
}
|
||||||
} else if (allowRelations && customTitleValue.startsWith("~")) {
|
} else if (allowRelations && customTitleValue.startsWith("~")) {
|
||||||
const relations = note.getRelations(attributeName);
|
const relations = note.getRelations(attributeName);
|
||||||
@@ -432,7 +432,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [ note.title ];
|
return [note.title];
|
||||||
}
|
}
|
||||||
|
|
||||||
static #formatDateToLocalISO(date: Date | null | undefined) {
|
static #formatDateToLocalISO(date: Date | null | undefined) {
|
||||||
@@ -442,7 +442,7 @@ export default class CalendarView extends ViewMode {
|
|||||||
|
|
||||||
const offset = date.getTimezoneOffset();
|
const offset = date.getTimezoneOffset();
|
||||||
const localDate = new Date(date.getTime() - offset * 60 * 1000);
|
const localDate = new Date(date.getTime() - offset * 60 * 1000);
|
||||||
return localDate.toISOString().split('T')[0];
|
return localDate.toISOString().split("T")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static #offsetDate(date: Date | string | null | undefined, offset: number) {
|
static #offsetDate(date: Date | string | null | undefined, offset: number) {
|
||||||
|
|||||||
@@ -75,7 +75,6 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.calendar-dropdown-widget .calendar-week span {
|
.calendar-dropdown-widget .calendar-week span {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 0 0 14.28%;
|
flex: 0 0 14.28%;
|
||||||
|
|||||||
@@ -165,6 +165,11 @@ span[style] {
|
|||||||
overflow: unset !important;
|
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.
|
* Code note specific fixes.
|
||||||
*/
|
*/
|
||||||
@@ -202,9 +207,8 @@ span[style] {
|
|||||||
|
|
||||||
@supports selector(.todo-list__label__description:has(*)) and (height: 1lh) {
|
@supports selector(.todo-list__label__description:has(*)) and (height: 1lh) {
|
||||||
.note-detail-printable .todo-list__label__description {
|
.note-detail-printable .todo-list__label__description {
|
||||||
|
|
||||||
/* The percentage of the line height that the check box occupies */
|
/* The percentage of the line height that the check box occupies */
|
||||||
--box-ratio: .75;
|
--box-ratio: 0.75;
|
||||||
/* The size of the gap between the check box and the caption */
|
/* The size of the gap between the check box and the caption */
|
||||||
--box-text-gap: 0.25em;
|
--box-text-gap: 0.25em;
|
||||||
|
|
||||||
@@ -301,9 +305,16 @@ blockquote {
|
|||||||
pre > code {
|
pre > code {
|
||||||
widows: 6;
|
widows: 6;
|
||||||
orphans: 6;
|
orphans: 6;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
page-break-after: avoid;
|
page-break-after: avoid;
|
||||||
break-after: avoid;
|
break-after: avoid;
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
--cmd-button-icon-color: white;
|
--cmd-button-icon-color: white;
|
||||||
--cmd-button-keyboard-shortcut-background: #0000004d;
|
--cmd-button-keyboard-shortcut-background: #0000004d;
|
||||||
--cmd-button-keyboard-shortcut-color: white;
|
--cmd-button-keyboard-shortcut-color: white;
|
||||||
--cmd-button-disabled-opacity: .5;
|
--cmd-button-disabled-opacity: 0.5;
|
||||||
|
|
||||||
--icon-button-color: currentColor;
|
--icon-button-color: currentColor;
|
||||||
--icon-button-hover-background: var(--hover-item-background-color);
|
--icon-button-hover-background: var(--hover-item-background-color);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
--cmd-button-icon-color: black;
|
--cmd-button-icon-color: black;
|
||||||
--cmd-button-keyboard-shortcut-background: #00000017;
|
--cmd-button-keyboard-shortcut-background: #00000017;
|
||||||
--cmd-button-keyboard-shortcut-color: black;
|
--cmd-button-keyboard-shortcut-color: black;
|
||||||
--cmd-button-disabled-opacity: .5;
|
--cmd-button-disabled-opacity: 0.5;
|
||||||
|
|
||||||
--icon-button-color: currentColor;
|
--icon-button-color: currentColor;
|
||||||
--icon-button-hover-background: var(--hover-item-background-color);
|
--icon-button-hover-background: var(--hover-item-background-color);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
--help-backdrop-blur: 10px;
|
--help-backdrop-blur: 10px;
|
||||||
|
|
||||||
--icon-button-size: 32px;
|
--icon-button-size: 32px;
|
||||||
--icon-button-icon-ratio: .65;
|
--icon-button-icon-ratio: 0.65;
|
||||||
|
|
||||||
/* Theme capabilities */
|
/* Theme capabilities */
|
||||||
--tab-note-icons: true;
|
--tab-note-icons: true;
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ button.btn.btn-primary:active,
|
|||||||
button.btn.btn-secondary:active,
|
button.btn.btn-secondary:active,
|
||||||
button.btn.btn-sm:not(.select-button):active,
|
button.btn.btn-sm:not(.select-button):active,
|
||||||
button.btn.btn-success:active {
|
button.btn.btn-success:active {
|
||||||
opacity: .85;
|
opacity: 0.85;
|
||||||
box-shadow: unset;
|
box-shadow: unset;
|
||||||
background: var(--cmd-button-background-color) !important;
|
background: var(--cmd-button-background-color) !important;
|
||||||
color: var(--cmd-button-text-color) !important;
|
color: var(--cmd-button-text-color) !important;
|
||||||
transform: scale(.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.btn.btn-primary:disabled,
|
button.btn.btn-primary:disabled,
|
||||||
@@ -57,7 +57,7 @@ button.btn.btn-secondary span.bx,
|
|||||||
button.btn.btn-sm span.bx,
|
button.btn.btn-sm span.bx,
|
||||||
button.btn.btn-success span.bx {
|
button.btn.btn-success span.bx {
|
||||||
color: var(--cmd-button-icon-color);
|
color: var(--cmd-button-icon-color);
|
||||||
padding-right: .35em;
|
padding-right: 0.35em;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,12 +66,12 @@ button.btn.btn-primary kbd,
|
|||||||
button.btn.btn-secondary kbd,
|
button.btn.btn-secondary kbd,
|
||||||
button.btn.btn-sm kbd,
|
button.btn.btn-sm kbd,
|
||||||
button.btn.btn-success kbd {
|
button.btn.btn-success kbd {
|
||||||
margin-left: .5em;
|
margin-left: 0.5em;
|
||||||
background: var(--cmd-button-keyboard-shortcut-background);
|
background: var(--cmd-button-keyboard-shortcut-background);
|
||||||
color: var(--cmd-button-keyboard-shortcut-color);
|
color: var(--cmd-button-keyboard-shortcut-color);
|
||||||
font-size: .6em;
|
font-size: 0.6em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: .5pt;
|
letter-spacing: 0.5pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -101,7 +101,7 @@ button.btn.btn-success kbd {
|
|||||||
--icon-button-hover-background: var(--tab-close-button-hover-background);
|
--icon-button-hover-background: var(--tab-close-button-hover-background);
|
||||||
--icon-button-hover-color: var(--tab-close-button-hover-color);
|
--icon-button-hover-color: var(--tab-close-button-hover-color);
|
||||||
--icon-button-size: 24px;
|
--icon-button-size: 24px;
|
||||||
--icon-button-icon-ratio: .8;
|
--icon-button-icon-ratio: 0.8;
|
||||||
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ button.btn.btn-success kbd {
|
|||||||
|
|
||||||
:root .icon-action:not(.global-menu-button):active::before,
|
:root .icon-action:not(.global-menu-button):active::before,
|
||||||
:root .tn-tool-button:active::before {
|
:root .tn-tool-button:active::before {
|
||||||
transform: scale(.85);
|
transform: scale(0.85);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root .icon-action:not(.global-menu-button):focus-visible,
|
:root .icon-action:not(.global-menu-button):focus-visible,
|
||||||
@@ -137,7 +137,7 @@ button.btn.btn-success kbd {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
input:disabled {
|
input:disabled {
|
||||||
opacity: .33;
|
opacity: 0.33;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Text boxes */
|
/* Text boxes */
|
||||||
@@ -190,7 +190,8 @@ textarea:focus,
|
|||||||
outline-offset: 0;
|
outline-offset: 0;
|
||||||
background: var(--input-focus-background);
|
background: var(--input-focus-background);
|
||||||
color: var(--input-focus-color);
|
color: var(--input-focus-color);
|
||||||
transition: outline-color 50ms linear,
|
transition:
|
||||||
|
outline-color 50ms linear,
|
||||||
outline-offset 200ms ease-out;
|
outline-offset 200ms ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,12 +230,12 @@ input::selection,
|
|||||||
outline: 3px solid var(--input-focus-outline-color);
|
outline: 3px solid var(--input-focus-outline-color);
|
||||||
outline-offset: 0;
|
outline-offset: 0;
|
||||||
background: var(--input-focus-background);
|
background: var(--input-focus-background);
|
||||||
transition: outline-color 50ms linear,
|
transition:
|
||||||
|
outline-color 50ms linear,
|
||||||
outline-offset 200ms ease-out;
|
outline-offset 200ms ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group input
|
.input-group input .input-group input:hover,
|
||||||
.input-group input:hover,
|
|
||||||
.input-group input:focus,
|
.input-group input:focus,
|
||||||
.input-group .form-control,
|
.input-group .form-control,
|
||||||
.input-group .form-control:hover,
|
.input-group .form-control:hover,
|
||||||
@@ -277,7 +278,7 @@ input::selection,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.input-group a.disabled {
|
.input-group a.disabled {
|
||||||
opacity: .5;
|
opacity: 0.5;
|
||||||
/* Workaround to set the "background" property. */
|
/* Workaround to set the "background" property. */
|
||||||
--button-disabled-background-color: transparent;
|
--button-disabled-background-color: transparent;
|
||||||
--button-disabled-text-color: var(--input-action-button-color);
|
--button-disabled-text-color: var(--input-action-button-color);
|
||||||
@@ -319,8 +320,7 @@ select.form-control,
|
|||||||
outline: 3px solid transparent;
|
outline: 3px solid transparent;
|
||||||
outline-offset: 6px;
|
outline-offset: 6px;
|
||||||
padding-right: calc(15px + 1.5rem);
|
padding-right: calc(15px + 1.5rem);
|
||||||
background: var(--input-background-color)
|
background: var(--input-background-color) var(--dropdown-arrow);
|
||||||
var(--dropdown-arrow);
|
|
||||||
color: var(--input-text-color);
|
color: var(--input-text-color);
|
||||||
border: unset;
|
border: unset;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
@@ -330,8 +330,7 @@ select:hover,
|
|||||||
select.form-select:hover,
|
select.form-select:hover,
|
||||||
select.form-control:hover,
|
select.form-control:hover,
|
||||||
.select-button.dropdown-toggle.btn:hover {
|
.select-button.dropdown-toggle.btn:hover {
|
||||||
background: var(--input-hover-background)
|
background: var(--input-hover-background) var(--dropdown-arrow);
|
||||||
var(--dropdown-arrow);
|
|
||||||
color: var(--input-hover-color);
|
color: var(--input-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,10 +346,10 @@ select.form-control:focus,
|
|||||||
box-shadow: unset;
|
box-shadow: unset;
|
||||||
outline: 3px solid var(--input-focus-outline-color);
|
outline: 3px solid var(--input-focus-outline-color);
|
||||||
outline-offset: 0;
|
outline-offset: 0;
|
||||||
background: var(--select-focus-background)
|
background: var(--select-focus-background) var(--dropdown-arrow);
|
||||||
var(--dropdown-arrow);
|
|
||||||
color: var(--select-focus-text-color);
|
color: var(--select-focus-text-color);
|
||||||
transition: outline-color 50ms linear,
|
transition:
|
||||||
|
outline-color 50ms linear,
|
||||||
outline-offset 200ms ease-out;
|
outline-offset 200ms ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,7 +359,7 @@ option {
|
|||||||
|
|
||||||
optgroup {
|
optgroup {
|
||||||
color: var(--select-group-heading-text-color);
|
color: var(--select-group-heading-text-color);
|
||||||
font-size: .75em;
|
font-size: 0.75em;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
@@ -374,9 +373,9 @@ optgroup {
|
|||||||
* </label>
|
* </label>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.tn-file-input {
|
.tn-file-input {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: .375rem 2.25rem .375rem .75rem;
|
padding: 0.375rem 2.25rem 0.375rem 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tn-file-input input[type="file"] {
|
.tn-file-input input[type="file"] {
|
||||||
@@ -416,19 +415,17 @@ optgroup {
|
|||||||
/* Check boxes and radio buttons */
|
/* Check boxes and radio buttons */
|
||||||
|
|
||||||
@supports selector(label:has(*)) {
|
@supports selector(label:has(*)) {
|
||||||
|
|
||||||
/* Check box & radio button commons */
|
/* Check box & radio button commons */
|
||||||
|
|
||||||
/* The parent label */
|
/* The parent label */
|
||||||
label.tn-radio,
|
label.tn-radio,
|
||||||
label.tn-checkbox {
|
label.tn-checkbox {
|
||||||
--box-size: 1em;
|
--box-size: 1em;
|
||||||
--box-label-gap: .5em;
|
--box-label-gap: 0.5em;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: calc(var(--box-size) + var(--box-label-gap)) !important;
|
padding-left: calc(var(--box-size) + var(--box-label-gap)) !important;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The original input */
|
/* The original input */
|
||||||
@@ -455,8 +452,8 @@ optgroup {
|
|||||||
height: var(--box-size);
|
height: var(--box-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
label.tn-radio:has(>input[type="radio"]:focus-visible)::before,
|
label.tn-radio:has(> input[type="radio"]:focus-visible)::before,
|
||||||
label.tn-checkbox:has(>input[type="checkbox"]:focus-visible)::before {
|
label.tn-checkbox:has(> input[type="checkbox"]:focus-visible)::before {
|
||||||
outline: 2px solid var(--input-focus-outline-color);
|
outline: 2px solid var(--input-focus-outline-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,14 +465,15 @@ optgroup {
|
|||||||
background: var(--radio-checkbox-background);
|
background: var(--radio-checkbox-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
label.tn-checkbox:has(>input[type="checkbox"]:not(:disabled)):hover:before {
|
label.tn-checkbox:has(> input[type="checkbox"]:not(:disabled)):hover:before {
|
||||||
background: var(--radio-checkbox-hover-background);
|
background: var(--radio-checkbox-hover-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes checkbox-checked {
|
@keyframes checkbox-checked {
|
||||||
from {
|
from {
|
||||||
transform: scale(2);
|
transform: scale(2);
|
||||||
} to {
|
}
|
||||||
|
to {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,15 +482,16 @@ optgroup {
|
|||||||
label.tn-checkbox::after {
|
label.tn-checkbox::after {
|
||||||
mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3e%3ctitle%3echeck-bold%3c/title%3e%3cpath d='M9%2c20.42L2.79%2c14.21L5.62%2c11.38L9%2c14.77L18.88%2c4.88L21.71%2c7.71L9%2c20.42Z' /%3e%3c/svg%3e");
|
mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3e%3ctitle%3echeck-bold%3c/title%3e%3cpath d='M9%2c20.42L2.79%2c14.21L5.62%2c11.38L9%2c14.77L18.88%2c4.88L21.71%2c7.71L9%2c20.42Z' /%3e%3c/svg%3e");
|
||||||
mask-position: center center;
|
mask-position: center center;
|
||||||
mask-size: .95em;
|
mask-size: 0.95em;
|
||||||
background-color: var(--radio-checkbox-indicator-color);
|
background-color: var(--radio-checkbox-indicator-color);
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: transform 300ms ease-out,
|
transition:
|
||||||
|
transform 300ms ease-out,
|
||||||
opacity 300ms linear;
|
opacity 300ms linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
label.tn-checkbox:has(>input[type="checkbox"]:checked)::after {
|
label.tn-checkbox:has(> input[type="checkbox"]:checked)::after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
transition: opacity 100ms ease-in-out;
|
transition: opacity 100ms ease-in-out;
|
||||||
@@ -511,15 +510,16 @@ optgroup {
|
|||||||
background: var(--radio-checkbox-background);
|
background: var(--radio-checkbox-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
label.tn-radio:has(>input[type="radio"]:not(:disabled)):hover::before {
|
label.tn-radio:has(> input[type="radio"]:not(:disabled)):hover::before {
|
||||||
background: var(--radio-checkbox-hover-background);
|
background: var(--radio-checkbox-hover-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes radio-checked {
|
@keyframes radio-checked {
|
||||||
from {
|
from {
|
||||||
transform: scale(1.5);
|
transform: scale(1.5);
|
||||||
} to {
|
}
|
||||||
transform: scale(.5);
|
to {
|
||||||
|
transform: scale(0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,12 +528,13 @@ optgroup {
|
|||||||
background: var(--radio-checkbox-indicator-color);
|
background: var(--radio-checkbox-indicator-color);
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 300ms linear,
|
transition:
|
||||||
|
opacity 300ms linear,
|
||||||
transform 300ms ease-in;
|
transform 300ms ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
label.tn-radio:has(>input[type="radio"]:checked)::after {
|
label.tn-radio:has(> input[type="radio"]:checked)::after {
|
||||||
transform: scale(.5);
|
transform: scale(0.5);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 150ms linear;
|
transition: opacity 150ms linear;
|
||||||
animation: radio-checked 200ms ease-out;
|
animation: radio-checked 200ms ease-out;
|
||||||
@@ -545,9 +546,8 @@ optgroup {
|
|||||||
label.tn-radio:has(> input[type="radio"]:disabled)::after,
|
label.tn-radio:has(> input[type="radio"]:disabled)::after,
|
||||||
label.tn-checkbox:has(> input[type="checkbox"]:disabled)::before,
|
label.tn-checkbox:has(> input[type="checkbox"]:disabled)::before,
|
||||||
label.tn-checkbox:has(> input[type="checkbox"]:disabled)::after {
|
label.tn-checkbox:has(> input[type="checkbox"]:disabled)::after {
|
||||||
opacity: .5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switches */
|
/* Switches */
|
||||||
@@ -580,7 +580,8 @@ body a.tn-link:visited,
|
|||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
|
||||||
transition: background-color 200ms ease-out,
|
transition:
|
||||||
|
background-color 200ms ease-out,
|
||||||
box-shadow 200ms ease-out,
|
box-shadow 200ms ease-out,
|
||||||
color 300ms ease-out;
|
color 300ms ease-out;
|
||||||
}
|
}
|
||||||
@@ -597,7 +598,8 @@ body a.tn-link:hover,
|
|||||||
--background: var(--link-hover-background);
|
--background: var(--link-hover-background);
|
||||||
color: var(--link-hover-color);
|
color: var(--link-hover-color);
|
||||||
|
|
||||||
transition: background-color 100ms ease-in,
|
transition:
|
||||||
|
background-color 100ms ease-in,
|
||||||
box-shadow 100ms ease-in,
|
box-shadow 100ms ease-in,
|
||||||
color 150ms ease-in;
|
color 150ms ease-in;
|
||||||
}
|
}
|
||||||
@@ -607,16 +609,18 @@ a.tn-link[href^="http://"]:not(.no-arrow)::after,
|
|||||||
a.tn-link[href^="https://"]:not(.no-arrow)::after,
|
a.tn-link[href^="https://"]:not(.no-arrow)::after,
|
||||||
.use-tn-links a.external:not(.no-arrow)::after,
|
.use-tn-links a.external:not(.no-arrow)::after,
|
||||||
.use-tn-links a[href^="http://"]:not(.no-arrow)::after,
|
.use-tn-links a[href^="http://"]:not(.no-arrow)::after,
|
||||||
.use-tn-links a[href^="https://"]:not(.no-arrow)::after {
|
.use-tn-links a[href^="https://"]:not(.no-arrow)::after
|
||||||
|
{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
opacity: .5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes link-arrow-blink {
|
@keyframes link-arrow-blink {
|
||||||
from {
|
from {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
} to {
|
}
|
||||||
opacity: .5;
|
to {
|
||||||
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,7 +629,8 @@ a.tn-link:hover[href^="http://"]:not(.no-arrow)::after,
|
|||||||
a.tn-link:hover[href^="https://"]:not(.no-arrow)::after,
|
a.tn-link:hover[href^="https://"]:not(.no-arrow)::after,
|
||||||
.use-tn-links a:hover.external:not(.no-arrow)::after,
|
.use-tn-links a:hover.external:not(.no-arrow)::after,
|
||||||
.use-tn-links a:hover[href^="http://"]:not(.no-arrow)::after,
|
.use-tn-links a:hover[href^="http://"]:not(.no-arrow)::after,
|
||||||
.use-tn-links a:hover[href^="https://"]:not(.no-arrow)::after {
|
.use-tn-links a:hover[href^="https://"]:not(.no-arrow)::after
|
||||||
|
{
|
||||||
animation: link-arrow-blink 500ms linear alternate infinite;
|
animation: link-arrow-blink 500ms linear alternate infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,6 +138,11 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
|
|||||||
* Search in text panel
|
* Search in text panel
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.find-replace-widget {
|
||||||
|
container-type: inline-size;
|
||||||
|
border-top: 3px solid var(--root-background) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.find-replace-widget > div {
|
.find-replace-widget > div {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
@@ -146,21 +151,86 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
|
|||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.find-replace-widget div.find-widget-found-wrapper {
|
div.find-replace-widget div.find-widget-found-wrapper > span {
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The up / down buttons of the "Find in text" input */
|
/* The up / down buttons of the "Find in text" input */
|
||||||
.find-replace-widget .input-group button {
|
.find-replace-widget .input-group button {
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.find-replace-widget .form-check {
|
.find-replace-widget .form-check {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.find-replace-widget .form-check .form-check-input {
|
.find-replace-widget .form-check .form-check-input {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Narrow version */
|
||||||
|
@container (max-width: 600px) {
|
||||||
|
.find-replace-widget > *,
|
||||||
|
.find-replace-widget input,
|
||||||
|
.find-replace-widget button.btn.btn-sm {
|
||||||
|
font-size: .9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-box {
|
||||||
|
position: relative;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-box,
|
||||||
|
.replace-widget-box {
|
||||||
|
padding-right: 3em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: .85em;
|
||||||
|
right: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-box > * {
|
||||||
|
margin: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.find-widget-search-term-input-group {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
max-width: unset;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-found-wrapper,
|
||||||
|
.find-widget-found-wrapper > span {
|
||||||
|
min-width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.find-widget-spacer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-check {
|
||||||
|
min-height: unset;
|
||||||
|
margin-bottom: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.replace-widget-box {
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.replace-widget-box > * {
|
||||||
|
margin-right: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.replace-widget-box button.btn.btn-sm {
|
||||||
|
min-width: unset;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ div.note-type-dropdown .check {
|
|||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Editability dropdown */
|
/* Editability dropdown */
|
||||||
|
|
||||||
div.editability-dropdown a.dropdown-item {
|
div.editability-dropdown a.dropdown-item {
|
||||||
padding: 4px 16px 4px 0;
|
padding: 4px 16px 4px 0;
|
||||||
@@ -29,8 +29,8 @@ div.editability-dropdown a.dropdown-item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editability-dropdown .dropdown-item .description {
|
.editability-dropdown .dropdown-item .description {
|
||||||
opacity: .75;
|
opacity: 0.75;
|
||||||
font-size: .85em;
|
font-size: 0.85em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -39,7 +39,7 @@ div.editability-dropdown a.dropdown-item {
|
|||||||
|
|
||||||
.attribute-list .add-new-attribute-button,
|
.attribute-list .add-new-attribute-button,
|
||||||
.attribute-list .save-attributes-button {
|
.attribute-list .save-attributes-button {
|
||||||
bottom: .3em;
|
bottom: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.attribute-list .save-attributes-button {
|
.attribute-list .save-attributes-button {
|
||||||
@@ -70,12 +70,52 @@ div.editability-dropdown a.dropdown-item {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.note-info-widget-table th {
|
.note-info-widget-table th {
|
||||||
opacity: .65;
|
opacity: 0.65;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root .note-info-widget-table button.calculate-button {
|
:root .note-info-widget-table button.calculate-button {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 4px 10px !important;
|
padding: 4px 10px !important;
|
||||||
font-size: .8em;
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Labels */
|
||||||
|
.attr-edit-table th {
|
||||||
|
padding-right: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user