Compare commits

..

39 Commits

Author SHA1 Message Date
SiriusXT
91237918d8 fix(window): windowId is always main on mobile devices 2026-01-14 14:46:43 +08:00
SiriusXT
3f207663aa Merge branch 'main' into feat/extra-window 2026-01-14 14:28:28 +08:00
SiriusXT
21cb896849 feat(window): add the extra-window class to the body of extra windows 2026-01-12 09:58:43 +08:00
SiriusXT
b9bcb07b6d Merge branch 'main' into feat/extra-window 2026-01-12 09:20:14 +08:00
SiriusXT
787b180378 Merge branch 'main' into feat/extra-window 2026-01-09 16:45:20 +08:00
SiriusXT
80404b83b0 Merge branch 'main' into feat/extra-window 2026-01-05 11:28:47 +08:00
SiriusXT
c612bdbfc1 fix(window): normalize closedAt of OpenNoteContexts for abnormally closed windows 2026-01-05 11:21:40 +08:00
SiriusXT
3a9e686533 chore(window): simplify replacement logic for open windows 2026-01-04 18:49:08 +08:00
SiriusXT
9e8d89a170 chore(window): avoid missing windowId 2026-01-04 15:18:57 +08:00
SiriusXT
31c70938d6 Merge branch 'main' into feat/extra-window 2026-01-04 14:13:25 +08:00
SiriusXT
07f3c48d0b chore(window): import randomString only when needed 2026-01-04 14:13:18 +08:00
SiriusXT
2821b6da9d chore(window): add TS type WindowState 2026-01-03 20:04:17 +08:00
SiriusXT
daba7c398d Merge branch 'main' into feat/extra-window 2026-01-03 20:04:11 +08:00
SiriusXT
de1ef5b98b chore(test): fix errors caused by layout changes 2026-01-03 19:04:34 +08:00
SiriusXT
1bb206d978 chore(i18n): tabs total 2026-01-03 18:15:45 +08:00
SiriusXT
2fd5ddab86 chore(window): optimize the replacement logic for old window notes 2026-01-03 11:07:48 +08:00
SiriusXT
27dc662636 fix(window): a window with no open notes appears blank. 2026-01-02 18:17:05 +08:00
SiriusXT
52691b5c8c Merge branch 'main' into feat/extra-window 2026-01-02 17:44:39 +08:00
SiriusXT
8087ed5688 Merge branch 'main' into feat/extra-window 2026-01-02 14:58:10 +08:00
SiriusXT
79e2c97882 chore(window): initialize closed time of openNoteContents to 0 2026-01-01 16:09:03 +08:00
SiriusXT
1078107776 chore(window): initialize closed time of openNoteContents to 0 2026-01-01 14:30:01 +08:00
SiriusXT
9c9e123e3d Merge branch 'main' into feat/extra-window 2026-01-01 14:04:45 +08:00
SiriusXT
a8c2947062 Merge branch 'main' into feat/extra-window 2025-12-31 14:23:42 +08:00
SiriusXT
366166a561 fix(window): avoid invalid fallback value for openNoteContexts 2025-12-30 09:44:58 +08:00
SiriusXT
4d2b02eddb Merge branch 'main' into feat/extra-window 2025-12-30 09:03:26 +08:00
SiriusXT
07871853a5 fix(window): cannot save when switching between multiple windows 2025-12-29 19:24:53 +08:00
SiriusXT
254145f0e5 chore(window): handle potential JSON parsing failures 2025-12-29 16:26:50 +08:00
SiriusXT
c28f11336e chore(window_db): fix potential migration error 2025-12-29 16:11:49 +08:00
SiriusXT
2e30683b7b chore(window): avoid reduce error when no candidates 2025-12-29 16:08:29 +08:00
SiriusXT
0af7b8b145 chore(window): use MAX_SAVED_WINDOWS constant 2025-12-29 15:56:04 +08:00
SiriusXT
5d39b84886 fix(window): Fix incorrect noteContents error 2025-12-29 15:28:27 +08:00
SiriusXT
537c4051cc feat(window): add class to extra windows 2025-12-29 15:27:35 +08:00
SiriusXT
d0a22bc517 fix(window): Fix empty array issue during openNoteContents data migration 2025-12-29 15:27:11 +08:00
SiriusXT
19a75acf3f Merge branch 'main' into feat/extra-window 2025-12-29 14:44:25 +08:00
SiriusXT
3f0abce874 feat(window_db): migrate openNoteContexts to structured format with window metadata 2025-12-29 14:43:49 +08:00
SiriusXT
36dd29f919 feat(window): add class to extra windows 2025-12-29 14:37:40 +08:00
SiriusXT
d7838f0b67 feat(window): restore recently closed windows from tray 2025-12-29 14:37:35 +08:00
SiriusXT
3353d4f436 feat(window): record openNoteContents of recently closed windows 2025-12-29 14:33:34 +08:00
SiriusXT
7740154bdc feat(window): add windowId for extra windows 2025-12-29 14:32:53 +08:00
326 changed files with 9481 additions and 13525 deletions

View File

@@ -12,7 +12,7 @@ jobs:
steps:
- name: Check if PRs have conflicts
uses: eps1lon/actions-label-merge-conflict@v3
if: ${{ github.repository == vars.REPO_MAIN }}
if: github.repository == ${{ vars.REPO_MAIN }}
with:
dirtyLabel: "merge-conflicts"
repoToken: "${{ secrets.MERGE_CONFLICT_LABEL_PAT }}"

View File

@@ -67,7 +67,7 @@ jobs:
- name: Deploy
uses: ./.github/actions/deploy-to-cloudflare-pages
if: ${{ github.repository == vars.REPO_MAIN }}
if: github.repository == ${{ vars.REPO_MAIN }}
with:
project_name: "trilium-docs"
comment_body: "📚 Documentation preview is ready"

View File

@@ -26,7 +26,7 @@ permissions:
jobs:
nightly-electron:
if: ${{ github.repository == vars.REPO_MAIN }}
if: github.repository == ${{ vars.REPO_MAIN }}
name: Deploy nightly
strategy:
fail-fast: false
@@ -109,7 +109,7 @@ jobs:
path: apps/desktop/upload
nightly-server:
if: ${{ github.repository == vars.REPO_MAIN }}
if: github.repository == ${{ vars.REPO_MAIN }}
name: Deploy server nightly
strategy:
fail-fast: false

View File

@@ -1,69 +0,0 @@
name: Deploy web clipper extension
on:
push:
branches:
- main
paths:
- "apps/web-clipper/**"
tags:
- "web-clipper-v*"
pull_request:
paths:
- "apps/web-clipper/**"
permissions:
contents: write
discussions: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
name: Build web clipper extension
permissions:
contents: read
deployments: write
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- name: Set up node & dependencies
uses: actions/setup-node@v6
with:
node-version: 24
cache: "pnpm"
- name: Install dependencies
run: pnpm install --filter web-clipper --frozen-lockfile --ignore-scripts
- name: Build the web clipper extension
run: |
pnpm --filter web-clipper zip
pnpm --filter web-clipper zip:firefox
- name: Upload build artifacts
uses: actions/upload-artifact@v6
if: ${{ !startsWith(github.ref, 'refs/tags/web-clipper-v') }}
with:
name: web-clipper-extension
path: apps/web-clipper/.output/*.zip
include-hidden-files: true
if-no-files-found: error
compression-level: 0
- name: Release web clipper extension
uses: softprops/action-gh-release@v2.5.0
if: ${{ startsWith(github.ref, 'refs/tags/web-clipper-v') }}
with:
draft: false
fail_on_unmatched_files: true
files: apps/web-clipper/.output/*.zip
discussion_category_name: Releases
make_latest: false
token: ${{ secrets.RELEASE_PAT }}

View File

@@ -42,8 +42,5 @@
},
"eslint.rules.customizations": [
{ "rule": "*", "severity": "warn" }
],
"cSpell.words": [
"Trilium"
]
}
}

View File

@@ -9,13 +9,13 @@
"keywords": [],
"author": "Elian Doran <contact@eliandoran.me>",
"license": "AGPL-3.0-only",
"packageManager": "pnpm@10.28.2",
"packageManager": "pnpm@10.28.0",
"devDependencies": {
"@redocly/cli": "2.15.1",
"@redocly/cli": "2.14.5",
"archiver": "7.0.1",
"fs-extra": "11.3.3",
"react": "19.2.4",
"react-dom": "19.2.4",
"react": "19.2.3",
"react-dom": "19.2.3",
"typedoc": "0.28.16",
"typedoc-plugin-missing-exports": "4.1.2"
}

View File

@@ -27,14 +27,14 @@
"@mermaid-js/layout-elk": "0.2.0",
"@mind-elixir/node-menu": "5.0.1",
"@popperjs/core": "2.11.8",
"@preact/signals": "2.6.2",
"@preact/signals": "2.5.1",
"@triliumnext/ckeditor5": "workspace:*",
"@triliumnext/codemirror": "workspace:*",
"@triliumnext/commons": "workspace:*",
"@triliumnext/highlightjs": "workspace:*",
"@triliumnext/share-theme": "workspace:*",
"@triliumnext/split.js": "workspace:*",
"@zumer/snapdom": "2.0.2",
"@zumer/snapdom": "2.0.1",
"autocomplete.js": "0.38.1",
"bootstrap": "5.3.8",
"boxicons": "2.1.4",
@@ -43,25 +43,25 @@
"debounce": "3.0.0",
"draggabilly": "3.0.0",
"force-graph": "1.51.0",
"globals": "17.3.0",
"i18next": "25.8.0",
"globals": "17.0.0",
"i18next": "25.7.4",
"i18next-http-backend": "3.0.2",
"jquery": "4.0.0",
"jquery": "3.7.1",
"jquery.fancytree": "2.38.5",
"jsplumb": "2.15.6",
"katex": "0.16.28",
"katex": "0.16.27",
"knockout": "3.5.1",
"leaflet": "1.9.4",
"leaflet-gpx": "2.2.0",
"mark.js": "8.11.1",
"marked": "17.0.1",
"mermaid": "11.12.2",
"mind-elixir": "5.7.1",
"mind-elixir": "5.5.0",
"normalize.css": "8.0.1",
"panzoom": "9.4.3",
"preact": "10.28.3",
"react-i18next": "16.5.4",
"react-window": "2.2.6",
"preact": "10.28.2",
"react-i18next": "16.5.3",
"react-window": "2.2.5",
"reveal.js": "5.2.1",
"svg-pan-zoom": "3.6.2",
"tabulator-tables": "6.3.1",
@@ -78,9 +78,9 @@
"@types/reveal.js": "5.2.2",
"@types/tabulator-tables": "6.3.1",
"copy-webpack-plugin": "13.0.1",
"happy-dom": "20.5.0",
"lightningcss": "1.31.1",
"happy-dom": "20.1.0",
"lightningcss": "1.30.2",
"script-loader": "0.7.2",
"vite-plugin-static-copy": "3.2.0"
"vite-plugin-static-copy": "3.1.4"
}
}

View File

@@ -1,6 +1,6 @@
import type { CKTextEditor } from "@triliumnext/ckeditor5";
import type CodeMirror from "@triliumnext/codemirror";
import { SqlExecuteResponse } from "@triliumnext/commons";
import { SqlExecuteResults } from "@triliumnext/commons";
import type { NativeImage, TouchBar } from "electron";
import { ColumnComponent } from "tabulator-tables";
@@ -410,7 +410,7 @@ type EventMappings = {
addNewLabel: CommandData;
addNewRelation: CommandData;
sqlQueryResults: CommandData & {
response: SqlExecuteResponse;
results: SqlExecuteResults;
};
readOnlyTemporarilyDisabled: {
noteContext: NoteContext;
@@ -542,6 +542,7 @@ export type FilteredCommandNames<T extends CommandData> = keyof Pick<CommandMapp
export class AppContext extends Component {
isMainWindow: boolean;
windowId: string;
components: Component[];
beforeUnloadListeners: (WeakRef<BeforeUploadListener> | (() => boolean))[];
tabManager!: TabManager;
@@ -550,10 +551,11 @@ export class AppContext extends Component {
lastSearchString?: string;
constructor(isMainWindow: boolean) {
constructor(isMainWindow: boolean, windowId: string) {
super();
this.isMainWindow = isMainWindow;
this.windowId = windowId;
// non-widget/layout components needed for the application
this.components = [];
this.beforeUnloadListeners = [];
@@ -683,8 +685,7 @@ export class AppContext extends Component {
this.beforeUnloadListeners = this.beforeUnloadListeners.filter(l => l !== listener);
}
}
const appContext = new AppContext(window.glob.isMainWindow);
const appContext = new AppContext(window.glob.isMainWindow, window.glob.windowId);
// we should save all outstanding changes before the page/app is closed
$(window).on("beforeunload", () => {

View File

@@ -1,17 +1,16 @@
import { CreateChildrenResponse, SqlExecuteResponse } from "@triliumnext/commons";
import bundleService from "../services/bundle.js";
import utils from "../services/utils.js";
import dateNoteService from "../services/date_notes.js";
import froca from "../services/froca.js";
import { t } from "../services/i18n.js";
import linkService from "../services/link.js";
import protectedSessionHolder from "../services/protected_session_holder.js";
import server from "../services/server.js";
import toastService from "../services/toast.js";
import utils from "../services/utils.js";
import ws from "../services/ws.js";
import appContext, { type NoteCommandData } from "./app_context.js";
import Component from "./component.js";
import toastService from "../services/toast.js";
import ws from "../services/ws.js";
import bundleService from "../services/bundle.js";
import froca from "../services/froca.js";
import linkService from "../services/link.js";
import { t } from "../services/i18n.js";
import { CreateChildrenResponse, SqlExecuteResponse } from "@triliumnext/commons";
export default class Entrypoints extends Component {
constructor() {
@@ -143,14 +142,15 @@ export default class Entrypoints extends Component {
}
async openInWindowCommand({ notePath, hoistedNoteId, viewScope }: NoteCommandData) {
const extraWindowId = utils.randomString(4);
const extraWindowHash = linkService.calculateHash({ notePath, hoistedNoteId, viewScope });
if (utils.isElectron()) {
const { ipcRenderer } = utils.dynamicRequire("electron");
ipcRenderer.send("create-extra-window", { extraWindowHash });
ipcRenderer.send("create-extra-window", { extraWindowId, extraWindowHash });
} else {
const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}?extraWindow=1${extraWindowHash}`;
const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}?extraWindow=${extraWindowId}${extraWindowHash}`;
window.open(url, "", "width=1000,height=800");
}
@@ -188,8 +188,13 @@ export default class Entrypoints extends Component {
} else if (note.mime.endsWith("env=backend")) {
await server.post(`script/run/${note.noteId}`);
} else if (note.mime === "text/x-sqlite;schema=trilium") {
const response = await server.post<SqlExecuteResponse>(`sql/execute/${note.noteId}`);
await appContext.triggerEvent("sqlQueryResults", { ntxId, response });
const resp = await server.post<SqlExecuteResponse>(`sql/execute/${note.noteId}`);
if (!resp.success) {
toastService.showError(t("entrypoints.sql-error", { message: resp.error }));
}
await appContext.triggerEvent("sqlQueryResults", { ntxId: ntxId, results: resp.results });
}
toastService.showMessage(t("entrypoints.note-executed"));

View File

@@ -11,6 +11,8 @@ import linkService from "../services/link.js";
import type { EventData } from "./app_context.js";
import type FNote from "../entities/fnote.js";
const MAX_SAVED_WINDOWS = 10;
interface TabState {
contexts: NoteContext[];
position: number;
@@ -25,6 +27,13 @@ interface NoteContextState {
viewScope: Record<string, any>;
}
interface WindowState {
windowId: string;
createdAt: number;
closedAt: number;
contexts: NoteContextState[];
}
export default class TabManager extends Component {
public children: NoteContext[];
public mutex: Mutex;
@@ -41,9 +50,6 @@ export default class TabManager extends Component {
this.recentlyClosedTabs = [];
this.tabsUpdate = new SpacedUpdate(async () => {
if (!appContext.isMainWindow) {
return;
}
if (options.is("databaseReadonly")) {
return;
}
@@ -52,9 +58,21 @@ export default class TabManager extends Component {
.map((nc) => nc.getPojoState())
.filter((t) => !!t);
await server.put("options", {
openNoteContexts: JSON.stringify(openNoteContexts)
});
// Update the current windows openNoteContexts in options
const savedWindows = options.getJson("openNoteContexts") || [];
const win = savedWindows.find((w: WindowState) => w.windowId === appContext.windowId);
if (win) {
win.contexts = openNoteContexts;
} else {
savedWindows.push({
windowId: appContext.windowId,
createdAt: Date.now(),
closedAt: 0,
contexts: openNoteContexts
} as WindowState);
}
await options.save("openNoteContexts", JSON.stringify(savedWindows));
});
appContext.addBeforeUnloadListener(this);
@@ -69,8 +87,13 @@ export default class TabManager extends Component {
}
async loadTabs() {
// Get the current windows openNoteContexts
const savedWindows = options.getJson("openNoteContexts") || [];
const currentWin = savedWindows.find(w => w.windowId === appContext.windowId);
const openNoteContexts = currentWin ? currentWin.contexts : undefined;
try {
const noteContextsToOpen = (appContext.isMainWindow && options.getJson("openNoteContexts")) || [];
const noteContextsToOpen = openNoteContexts || [];
// preload all notes at once
await froca.getNotes([...noteContextsToOpen.flatMap((tab: NoteContextState) =>
@@ -119,6 +142,51 @@ export default class TabManager extends Component {
}
});
// Save window contents
if (currentWin as WindowState) {
currentWin.createdAt = Date.now();
currentWin.closedAt = 0;
currentWin.contexts = filteredNoteContexts;
} else {
if (savedWindows?.length >= MAX_SAVED_WINDOWS) {
// Filter out the oldest entry
// 1) Never remove the "main" window
// 2) Prefer removing the oldest closed window (closedAt !== 0)
// 3) If no closed window exists, remove the window with the oldest created window
let oldestClosedIndex = -1;
let oldestClosedTime = Infinity;
let oldestCreatedIndex = -1;
let oldestCreatedTime = Infinity;
savedWindows.forEach((w: WindowState, i: number) => {
if (w.windowId === "main") return;
if (w.closedAt !== 0) {
if (w.closedAt < oldestClosedTime) {
oldestClosedTime = w.closedAt;
oldestClosedIndex = i;
}
} else {
if (w.createdAt < oldestCreatedTime) {
oldestCreatedTime = w.createdAt;
oldestCreatedIndex = i;
}
}
});
const indexToRemove = oldestClosedIndex !== -1 ? oldestClosedIndex : oldestCreatedIndex;
if (indexToRemove !== -1) {
savedWindows.splice(indexToRemove, 1);
}
}
savedWindows.push({
windowId: appContext.windowId,
createdAt: Date.now(),
closedAt: 0,
contexts: filteredNoteContexts
} as WindowState);
}
await options.save("openNoteContexts", JSON.stringify(savedWindows));
// if there's a notePath in the URL, make sure it's open and active
// (useful, for e.g., opening clipped notes from clipper or opening link in an extra window)
if (parsedFromUrl.notePath) {

View File

@@ -99,22 +99,15 @@ function initFullScreenDetection(currentWindow: Electron.BrowserWindow) {
}
function initTransparencyEffects(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
const material = style.getPropertyValue("--background-material").trim();
if (window.glob.platform === "win32") {
const material = style.getPropertyValue("--background-material");
// TriliumNextTODO: find a nicer way to make TypeScript happy unfortunately TS did not like Array.includes here
const bgMaterialOptions = ["auto", "none", "mica", "acrylic", "tabbed"] as const;
const foundBgMaterialOption = bgMaterialOptions.find((bgMaterialOption) => material === bgMaterialOption);
if (foundBgMaterialOption) {
currentWindow.setBackgroundMaterial(foundBgMaterialOption);
}
}
if (window.glob.platform === "darwin") {
const bgMaterialOptions = [ "popover", "tooltip", "titlebar", "selection", "menu", "sidebar", "header", "sheet", "window", "hud", "fullscreen-ui", "content", "under-window", "under-page" ] as const;
const foundBgMaterialOption = bgMaterialOptions.find((bgMaterialOption) => material === bgMaterialOption);
if (foundBgMaterialOption) {
currentWindow.setVibrancy(foundBgMaterialOption);
}
}
}
/**

View File

@@ -1,4 +1,4 @@
import { getNoteIcon } from "@triliumnext/commons";
import { MIME_TYPES_DICT } from "@triliumnext/commons";
import cssClassManager from "../services/css_class_manager.js";
import type { Froca } from "../services/froca-interface.js";
@@ -13,6 +13,25 @@ import type { AttributeType, default as FAttribute } from "./fattribute.js";
const LABEL = "label";
const RELATION = "relation";
export const NOTE_TYPE_ICONS = {
file: "bx bx-file",
image: "bx bx-image",
code: "bx bx-code",
render: "bx bx-extension",
search: "bx bx-file-find",
relationMap: "bx bxs-network-chart",
book: "bx bx-book",
noteMap: "bx bxs-network-chart",
mermaid: "bx bx-selection",
canvas: "bx bx-pen",
webView: "bx bx-globe-alt",
launcher: "bx bx-link",
doc: "bx bxs-file-doc",
contentWidget: "bx bxs-widget",
mindMap: "bx bx-sitemap",
aiChat: "bx bx-bot"
};
/**
* There are many different Note types, some of which are entirely opaque to the
* end user. Those types should be used only for checking against, they are
@@ -563,18 +582,32 @@ export default class FNote {
}
getIcon() {
return `tn-icon ${this.#getIconInternal()}`;
}
#getIconInternal() {
const iconClassLabels = this.getLabels("iconClass");
const workspaceIconClass = this.getWorkspaceIconClass();
const icon = getNoteIcon({
noteId: this.noteId,
type: this.type,
mime: this.mime,
iconClass: iconClassLabels.length > 0 ? iconClassLabels[0].value : undefined,
workspaceIconClass,
isFolder: this.isFolder.bind(this)
});
return `tn-icon ${icon}`;
if (iconClassLabels && iconClassLabels.length > 0) {
return iconClassLabels[0].value;
} else if (workspaceIconClass) {
return workspaceIconClass;
} else if (this.noteId === "root") {
return "bx bx-home-alt-2";
}
if (this.noteId === "_share") {
return "bx bx-share-alt";
} else if (this.type === "text") {
if (this.isFolder()) {
return "bx bx-folder";
}
return "bx bx-note";
} else if (this.type === "code") {
const correspondingMimeType = MIME_TYPES_DICT.find(m => m.mime === this.mime);
return correspondingMimeType?.icon ?? NOTE_TYPE_ICONS.code;
}
return NOTE_TYPE_ICONS[this.type];
}
getColorClass() {

View File

@@ -13,14 +13,13 @@
<body id="trilium-app">
<noscript>Trilium requires JavaScript to be enabled.</noscript>
<div id="context-menu-cover"></div>
<div class="dropdown-menu dropdown-menu-sm" id="context-menu-container" style="display: none"></div>
<!-- Required to match the PWA's top bar color with the theme -->
<!-- This works even when the user directly changes --root-background in CSS -->
<div id="background-color-tracker" style="position: absolute; visibility: hidden; color: var(--root-background); transition: color 1ms;"></div>
<script src="./src/index.ts" type="module"></script>
<script src="./index.ts" type="module"></script>
<!-- Required for correct loading of scripts in Electron -->
<script>

View File

@@ -16,21 +16,10 @@ async function initJQuery() {
const $ = (await import("jquery")).default;
window.$ = $;
window.jQuery = $;
// Polyfill removed jQuery methods for autocomplete.js compatibility
($ as any).isArray = Array.isArray;
($ as any).isFunction = function(obj: any) { return typeof obj === 'function'; };
($ as any).isPlainObject = function(obj: any) {
if (obj == null || typeof obj !== 'object') { return false; }
const proto = Object.getPrototypeOf(obj);
if (proto === null) { return true; }
const Ctor = Object.prototype.hasOwnProperty.call(proto, 'constructor') && proto.constructor;
return typeof Ctor === 'function' && Ctor === Object;
};
}
async function setupGlob() {
const response = await fetch(`./bootstrap${window.location.search}`);
const response = await fetch(`/bootstrap${window.location.search}`);
const json = await response.json();
window.global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */
@@ -50,25 +39,22 @@ async function loadBootstrapCss() {
}
function loadStylesheets() {
const { device, assetPath, themeCssUrl, themeUseNextAsBase } = window.glob;
const { assetPath, themeCssUrl, themeUseNextAsBase } = window.glob;
const cssToLoad: string[] = [];
if (device !== "print") {
cssToLoad.push(`${assetPath}/stylesheets/ckeditor-theme.css`);
cssToLoad.push(`api/fonts`);
cssToLoad.push(`${assetPath}/stylesheets/theme-light.css`);
if (themeCssUrl) {
cssToLoad.push(themeCssUrl);
}
if (themeUseNextAsBase === "next") {
cssToLoad.push(`${assetPath}/stylesheets/theme-next.css`);
} else if (themeUseNextAsBase === "next-dark") {
cssToLoad.push(`${assetPath}/stylesheets/theme-next-dark.css`);
} else if (themeUseNextAsBase === "next-light") {
cssToLoad.push(`${assetPath}/stylesheets/theme-next-light.css`);
}
cssToLoad.push(`${assetPath}/stylesheets/style.css`);
cssToLoad.push(`${assetPath}/stylesheets/ckeditor-theme.css`);
cssToLoad.push(`api/fonts`);
cssToLoad.push(`${assetPath}/stylesheets/theme-light.css`);
if (themeCssUrl) {
cssToLoad.push(themeCssUrl);
}
if (themeUseNextAsBase === "next") {
cssToLoad.push(`${assetPath}/stylesheets/theme-next.css`);
} else if (themeUseNextAsBase === "next-dark") {
cssToLoad.push(`${assetPath}/stylesheets/theme-next-dark.css`);
} else if (themeUseNextAsBase === "next-light") {
cssToLoad.push(`${assetPath}/stylesheets/theme-next-light.css`);
}
cssToLoad.push(`${assetPath}/stylesheets/style.css`);
for (const href of cssToLoad) {
const linkEl = document.createElement("link");
@@ -85,7 +71,7 @@ function loadIcons() {
}
function setBodyAttributes() {
const { device, headingStyle, layoutOrientation, platform, isElectron, hasNativeTitleBar, hasBackgroundEffects, currentLocale } = window.glob;
const { device, headingStyle, layoutOrientation, platform, isElectron, hasNativeTitleBar, hasBackgroundEffects, currentLocale, isMainWindow } = window.glob;
const classesToSet = [
device,
`heading-style-${headingStyle}`,
@@ -93,7 +79,8 @@ function setBodyAttributes() {
`platform-${platform}`,
isElectron && "electron",
hasNativeTitleBar && "native-titlebar",
hasBackgroundEffects && "background-effects"
hasBackgroundEffects && "background-effects",
!isMainWindow && 'extra-window'
].filter(Boolean) as string[];
for (const classToSet of classesToSet) {
@@ -105,17 +92,10 @@ function setBodyAttributes() {
}
async function loadScripts() {
switch (glob.device) {
case "mobile":
await import("./mobile.js");
break;
case "print":
await import("./print.js");
break;
case "desktop":
default:
await import("./desktop.js");
break;
if (glob.device === "mobile") {
await import("./mobile.js");
} else {
await import("./desktop.js");
}
}

View File

@@ -46,6 +46,8 @@ import ScrollPadding from "../widgets/scroll_padding.js";
import SearchResult from "../widgets/search_result.jsx";
import SharedInfo from "../widgets/shared_info.jsx";
import RightPanelContainer from "../widgets/sidebar/RightPanelContainer.jsx";
import SqlResults from "../widgets/sql_result.js";
import SqlTableSchemas from "../widgets/sql_table_schemas.js";
import TabRowWidget from "../widgets/tab_row.js";
import TabHistoryNavigationButtons from "../widgets/TabHistoryNavigationButtons.jsx";
import TitleBarButtons from "../widgets/title_bar_buttons.jsx";
@@ -161,9 +163,11 @@ export default class DesktopLayout {
.child(<SharedInfo />)
)
.optChild(!isNewLayout, <PromotedAttributes />)
.child(<SqlTableSchemas />)
.child(<NoteDetail />)
.child(<NoteList media="screen" />)
.child(<SearchResult />)
.child(<SqlResults />)
.child(<ScrollPadding />)
)
.child(<ApiLog />)

View File

@@ -2,20 +2,18 @@ import type AppContext from "../components/app_context.js";
import GlobalMenuWidget from "../widgets/buttons/global_menu.js";
import CloseZenModeButton from "../widgets/close_zen_button.js";
import NoteList from "../widgets/collections/NoteList.jsx";
import ContentHeader from "../widgets/containers/content_header.js";
import FlexContainer from "../widgets/containers/flex_container.js";
import RootContainer from "../widgets/containers/root_container.js";
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
import FindWidget from "../widgets/find.js";
import FloatingButtons from "../widgets/FloatingButtons.jsx";
import { MOBILE_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
import LauncherContainer from "../widgets/launch_bar/LauncherContainer.jsx";
import InlineTitle from "../widgets/layout/InlineTitle.jsx";
import NoteBadges from "../widgets/layout/NoteBadges.jsx";
import NoteTitleActions from "../widgets/layout/NoteTitleActions.jsx";
import MobileDetailMenu from "../widgets/mobile_widgets/mobile_detail_menu.js";
import ScreenContainer from "../widgets/mobile_widgets/screen_container.js";
import SidebarContainer from "../widgets/mobile_widgets/sidebar_container.js";
import ToggleSidebarButton from "../widgets/mobile_widgets/toggle_sidebar_button.jsx";
import NoteIconWidget from "../widgets/note_icon.jsx";
import NoteTitleWidget from "../widgets/note_title.js";
import NoteTreeWidget from "../widgets/note_tree.js";
import NoteWrapperWidget from "../widgets/note_wrapper.js";
@@ -23,10 +21,13 @@ import NoteDetail from "../widgets/NoteDetail.jsx";
import PromotedAttributes from "../widgets/PromotedAttributes.jsx";
import QuickSearchWidget from "../widgets/quick_search.js";
import { useNoteContext } from "../widgets/react/hooks.jsx";
import ReadOnlyNoteInfoBar from "../widgets/ReadOnlyNoteInfoBar.jsx";
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
import FilePropertiesTab from "../widgets/ribbon/FilePropertiesTab.jsx";
import SearchDefinitionTab from "../widgets/ribbon/SearchDefinitionTab.jsx";
import SearchResult from "../widgets/search_result.jsx";
import SharedInfoWidget from "../widgets/shared_info.js";
import TabRowWidget from "../widgets/tab_row.js";
import MobileEditorToolbar from "../widgets/type_widgets/text/mobile_editor_toolbar.jsx";
import { applyModals } from "./layout_commons.js";
@@ -146,22 +147,23 @@ export default class MobileLayout {
new NoteWrapperWidget()
.child(
new FlexContainer("row")
.class("title-row note-split-title")
.contentSized()
.css("font-size", "larger")
.css("align-items", "center")
.child(<ToggleSidebarButton />)
.child(<NoteIconWidget />)
.child(<NoteTitleWidget />)
.child(<NoteBadges />)
.child(<MobileDetailMenu />)
)
.child(<FloatingButtons items={MOBILE_FLOATING_BUTTONS} />)
.child(<PromotedAttributes />)
.child(
new ScrollingContainer()
.filling()
.contentSized()
.child(<InlineTitle />)
.child(<NoteTitleActions />)
.child(new ContentHeader()
.child(<ReadOnlyNoteInfoBar />)
.child(<SharedInfoWidget />)
)
.child(<NoteDetail />)
.child(<NoteList media="screen" />)
.child(<StandaloneRibbonAdapter component={SearchDefinitionTab} />)
@@ -169,7 +171,6 @@ export default class MobileLayout {
.child(<FilePropertiesWrapper />)
)
.child(<MobileEditorToolbar />)
.child(new FindWidget())
)
)
)
@@ -178,6 +179,7 @@ export default class MobileLayout {
new FlexContainer("column")
.contentSized()
.id("mobile-bottom-bar")
.child(new TabRowWidget().css("height", "40px"))
.child(new FlexContainer("row")
.class("horizontal")
.css("height", "53px")

View File

@@ -1,9 +1,8 @@
import { KeyboardActionNames } from "@triliumnext/commons";
import { h, JSX, render } from "preact";
import keyboardActionService, { getActionSync } from "../services/keyboard_actions.js";
import note_tooltip from "../services/note_tooltip.js";
import utils from "../services/utils.js";
import { h, JSX, render } from "preact";
export interface ContextMenuOptions<T> {
x: number;
@@ -63,17 +62,17 @@ export type ContextMenuEvent = PointerEvent | MouseEvent | JQuery.ContextMenuEve
class ContextMenu {
private $widget: JQuery<HTMLElement>;
private $cover?: JQuery<HTMLElement>;
private $cover: JQuery<HTMLElement>;
private options?: ContextMenuOptions<any>;
private isMobile: boolean;
constructor() {
this.$widget = $("#context-menu-container");
this.$cover = $("#context-menu-cover");
this.$widget.addClass("dropend");
this.isMobile = utils.isMobile();
if (this.isMobile) {
this.$cover = $("#context-menu-cover");
this.$cover.on("click", () => this.hide());
} else {
$(document).on("click", (e) => this.hide());
@@ -92,7 +91,7 @@ class ContextMenu {
}
this.$widget.toggleClass("mobile-bottom-menu", !this.options.forcePositionOnMobile);
this.$cover?.addClass("show");
this.$cover.addClass("show");
$("body").addClass("context-menu-shown");
this.$widget.empty();
@@ -141,14 +140,16 @@ class ContextMenu {
} else {
left = this.options.x - contextMenuWidth + CONTEXT_MENU_OFFSET;
}
} else if (contextMenuWidth && this.options.x + contextMenuWidth - CONTEXT_MENU_OFFSET > clientWidth - CONTEXT_MENU_PADDING) {
// Overflow: right
left = clientWidth - contextMenuWidth - CONTEXT_MENU_PADDING;
} else if (this.options.x - CONTEXT_MENU_OFFSET < CONTEXT_MENU_PADDING) {
// Overflow: left
left = CONTEXT_MENU_PADDING;
} else {
left = this.options.x - CONTEXT_MENU_OFFSET;
if (contextMenuWidth && this.options.x + contextMenuWidth - CONTEXT_MENU_OFFSET > clientWidth - CONTEXT_MENU_PADDING) {
// Overflow: right
left = clientWidth - contextMenuWidth - CONTEXT_MENU_PADDING;
} else if (this.options.x - CONTEXT_MENU_OFFSET < CONTEXT_MENU_PADDING) {
// Overflow: left
left = CONTEXT_MENU_PADDING;
} else {
left = this.options.x - CONTEXT_MENU_OFFSET;
}
}
this.$widget
@@ -248,7 +249,7 @@ class ContextMenu {
if ("uiIcon" in item || "checked" in item) {
const icon = (item.checked ? "bx bx-check" : item.uiIcon);
if (icon) {
$icon.addClass([icon, "tn-icon"]);
$icon.addClass(icon);
} else {
$icon.append("&nbsp;");
}
@@ -260,7 +261,7 @@ class ContextMenu {
.append(item.title);
if ("badges" in item && item.badges) {
for (const badge of item.badges) {
for (let badge of item.badges) {
const badgeElement = $(`<span class="badge">`).text(badge.title);
if (badge.className) {
@@ -351,7 +352,7 @@ class ContextMenu {
async hide() {
this.options?.onHide?.();
this.$widget.removeClass("show");
this.$cover?.removeClass("show");
this.$cover.removeClass("show");
$("body").removeClass("context-menu-shown");
this.$widget.hide();
}

View File

@@ -29,9 +29,7 @@ async function main() {
const froca = (await import("./services/froca")).default;
const note = await froca.getNote(noteId);
const bodyWrapper = document.createElement("div");
render(<App note={note} noteId={noteId} />, bodyWrapper);
document.body.appendChild(bodyWrapper);
render(<App note={note} noteId={noteId} />, document.body);
}
function App({ note, noteId }: { note: FNote | null | undefined, noteId: string }) {

View File

@@ -8,17 +8,6 @@ async function loadBootstrap() {
}
}
// Polyfill removed jQuery methods for autocomplete.js compatibility
($ as any).isArray = Array.isArray;
($ as any).isFunction = function(obj: any) { return typeof obj === 'function'; };
($ as any).isPlainObject = function(obj: any) {
if (obj == null || typeof obj !== 'object') { return false; }
const proto = Object.getPrototypeOf(obj);
if (proto === null) { return true; }
const Ctor = Object.prototype.hasOwnProperty.call(proto, 'constructor') && proto.constructor;
return typeof Ctor === 'function' && Ctor === Object;
};
(window as any).$ = $;
(window as any).jQuery = $;
await loadBootstrap();

View File

@@ -42,7 +42,7 @@ describe("Set boolean with inheritance", () => {
name: "foo",
value: "",
isInheritable: false
}, undefined);
});
});
it("removes boolean normally without inheritance", async () => {
@@ -91,7 +91,7 @@ describe("Set boolean with inheritance", () => {
name: "foo",
value: "false",
isInheritable: false
}, undefined);
});
});
it("overrides boolean with inherited false", async () => {
@@ -112,7 +112,7 @@ describe("Set boolean with inheritance", () => {
name: "foo",
value: "",
isInheritable: false
}, undefined);
});
});
it("deletes override boolean with inherited false with already existing value", async () => {
@@ -134,6 +134,6 @@ describe("Set boolean with inheritance", () => {
name: "foo",
value: "",
isInheritable: false
}, undefined);
});
});
});

View File

@@ -14,13 +14,13 @@ async function addLabel(noteId: string, name: string, value: string = "", isInhe
});
}
export async function setLabel(noteId: string, name: string, value: string = "", isInheritable = false, componentId?: string) {
export async function setLabel(noteId: string, name: string, value: string = "", isInheritable = false) {
await server.put(`notes/${noteId}/set-attribute`, {
type: "label",
name,
value,
isInheritable,
}, componentId);
isInheritable
});
}
export async function setRelation(noteId: string, name: string, value: string = "", isInheritable = false) {
@@ -117,15 +117,15 @@ function removeOwnedRelationByName(note: FNote, relationName: string) {
* @param name the name of the attribute to set.
* @param value the value of the attribute to set.
*/
export async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined, componentId?: string) {
export async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) {
if (value !== null && value !== undefined) {
// Create or update the attribute.
await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value }, componentId);
await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value });
} else {
// Remove the attribute if it exists on the server but we don't define a value for it.
const attributeId = note.getAttribute(type, name)?.attributeId;
if (attributeId) {
await server.remove(`notes/${note.noteId}/attributes/${attributeId}`, componentId);
await server.remove(`notes/${note.noteId}/attributes/${attributeId}`);
}
}
}

View File

@@ -103,7 +103,7 @@ async function moveToParentNote(branchIdsToMove: string[], newParentBranchId: st
* @param moveToParent whether to automatically go to the parent note path after a succesful delete. Usually makes sense if deleting the active note(s).
* @returns promise that returns false if the operation was cancelled or there was nothing to delete, true if the operation succeeded.
*/
async function deleteNotes(branchIdsToDelete: string[], forceDeleteAllClones = false, moveToParent = true, componentId?: string) {
async function deleteNotes(branchIdsToDelete: string[], forceDeleteAllClones = false, moveToParent = true) {
branchIdsToDelete = filterRootNote(branchIdsToDelete);
if (branchIdsToDelete.length === 0) {
@@ -139,9 +139,9 @@ async function deleteNotes(branchIdsToDelete: string[], forceDeleteAllClones = f
const branch = froca.getBranch(branchIdToDelete);
if (deleteAllClones && branch) {
await server.remove(`notes/${branch.noteId}${query}`, componentId);
await server.remove(`notes/${branch.noteId}${query}`);
} else {
await server.remove(`branches/${branchIdToDelete}${query}`, componentId);
await server.remove(`branches/${branchIdToDelete}${query}`);
}
}

View File

@@ -49,7 +49,7 @@ function createClassForColor(colorString: string | null) {
return clsx("use-note-color", className, colorsWithHue.has(className) && "with-hue");
}
export function parseColor(color: string) {
function parseColor(color: string) {
try {
return Color(color.toLowerCase());
} catch (ex) {
@@ -77,7 +77,7 @@ function adjustColorLightness(color: ColorInstance, lightThemeMaxLightness: numb
}
/** Returns the hue of the specified color, or undefined if the color is grayscale. */
export function getHue(color: ColorInstance) {
function getHue(color: ColorInstance) {
const hslColor = color.hsl();
if (hslColor.saturationl() > 0) {
return hslColor.hue();

View File

@@ -1,6 +1,5 @@
import { t } from "./i18n";
import options from "./options";
import { isMobile } from "./utils";
export interface ExperimentalFeature {
id: string;
@@ -22,7 +21,7 @@ let enabledFeatures: Set<ExperimentalFeatureId> | null = null;
export function isExperimentalFeatureEnabled(featureId: ExperimentalFeatureId): boolean {
if (featureId === "new-layout") {
return (isMobile() || options.is("newLayout"));
return options.is("newLayout");
}
return getEnabledFeatures().has(featureId);
@@ -30,7 +29,7 @@ export function isExperimentalFeatureEnabled(featureId: ExperimentalFeatureId):
export function getEnabledExperimentalFeatureIds() {
const values = [ ...getEnabledFeatures().values() ];
if (isMobile() || options.is("newLayout")) {
if (options.is("newLayout")) {
values.push("new-layout");
}
return values;

View File

@@ -27,10 +27,6 @@ async function processEntityChanges(entityChanges: EntityChange[]) {
loadResults.addRevision(ec.entityId, ec.noteId, ec.componentId);
} else if (ec.entityName === "options") {
const attributeEntity = ec.entity as FAttributeRow;
if (attributeEntity.name === "openNoteContexts") {
continue; // only noise
}
options.set(attributeEntity.name as OptionNames, attributeEntity.value);
loadResults.addOption(attributeEntity.name as OptionNames);
} else if (ec.entityName === "attachments") {

View File

@@ -1,6 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import shortcuts, { isIMEComposing, keyMatches, matchesShortcut } from "./shortcuts.js";
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import shortcuts, { keyMatches, matchesShortcut, isIMEComposing } from "./shortcuts.js";
// Mock utils module
vi.mock("./utils.js", () => ({
@@ -62,10 +61,9 @@ describe("shortcuts", () => {
});
describe("keyMatches", () => {
const createKeyboardEvent = (key: string, code?: string, extraProps: Partial<KeyboardEvent> = {}) => ({
const createKeyboardEvent = (key: string, code?: string) => ({
key,
code: code || `Key${key.toUpperCase()}`,
...extraProps
code: code || `Key${key.toUpperCase()}`
} as KeyboardEvent);
it("should match regular letter keys using key code", () => {
@@ -103,23 +101,17 @@ describe("shortcuts", () => {
consoleSpy.mockRestore();
});
it("should match azerty keys", () => {
const event = createKeyboardEvent("A", "KeyQ");
expect(keyMatches(event, "a")).toBe(true);
expect(keyMatches(event, "q")).toBe(false);
});
it("should match letter keys using code when key is a special character (macOS Alt behavior)", () => {
// On macOS, pressing Option/Alt + A produces 'å' as the key, but code is still 'KeyA'
const macOSAltAEvent = createKeyboardEvent("å", "KeyA", { altKey: true });
const macOSAltAEvent = createKeyboardEvent("å", "KeyA");
expect(keyMatches(macOSAltAEvent, "a")).toBe(true);
// Option + H produces '˙'
const macOSAltHEvent = createKeyboardEvent("˙", "KeyH", { altKey: true });
const macOSAltHEvent = createKeyboardEvent("˙", "KeyH");
expect(keyMatches(macOSAltHEvent, "h")).toBe(true);
// Option + S produces 'ß'
const macOSAltSEvent = createKeyboardEvent("ß", "KeyS", { altKey: true });
const macOSAltSEvent = createKeyboardEvent("ß", "KeyS");
expect(keyMatches(macOSAltSEvent, "s")).toBe(true);
});
});
@@ -223,15 +215,6 @@ describe("shortcuts", () => {
consoleSpy.mockRestore();
});
it("matches azerty", () => {
const event = createKeyboardEvent({
key: "a",
code: "KeyQ",
ctrlKey: true
});
expect(matchesShortcut(event, "Ctrl+A")).toBe(true);
});
it("should match Alt+letter shortcuts on macOS where key is a special character", () => {
// On macOS, pressing Option/Alt + A produces 'å' but code remains 'KeyA'
const macOSAltAEvent = createKeyboardEvent({

View File

@@ -215,12 +215,9 @@ export function keyMatches(e: KeyboardEvent, key: string): boolean {
// For letter keys, use the physical key code for consistency
// On macOS, Option/Alt key produces special characters, so we must use e.code
if (key.length === 1 && key >= 'a' && key <= 'z') {
if (e.altKey) {
// e.code is like "KeyA", "KeyB", etc.
const expectedCode = `Key${key.toUpperCase()}`;
return e.code === expectedCode || e.key.toLowerCase() === key.toLowerCase();
}
return e.key.toLowerCase() === key.toLowerCase();
// e.code is like "KeyA", "KeyB", etc.
const expectedCode = `Key${key.toUpperCase()}`;
return e.code === expectedCode || e.key.toLowerCase() === key.toLowerCase();
}
// For regular keys, check both key and code as fallback

View File

@@ -1,11 +1,10 @@
import { MimeType } from "@triliumnext/commons";
import { type AutoHighlightResult, ensureMimeTypes, highlight, highlightAuto, type HighlightResult, loadTheme, type Theme,Themes } from "@triliumnext/highlightjs";
import { copyText, copyTextWithToast } from "./clipboard_ext.js";
import { t } from "./i18n.js";
import { ensureMimeTypes, highlight, highlightAuto, loadTheme, Themes, type AutoHighlightResult, type HighlightResult, type Theme } from "@triliumnext/highlightjs";
import mime_types from "./mime_types.js";
import options from "./options.js";
import { t } from "./i18n.js";
import { copyText, copyTextWithToast } from "./clipboard_ext.js";
import { isShare } from "./utils.js";
import { MimeType } from "@triliumnext/commons";
let highlightingLoaded = false;
@@ -77,15 +76,13 @@ export async function applySingleBlockSyntaxHighlight($codeBlock: JQuery<HTMLEle
}
export async function ensureMimeTypesForHighlighting(mimeTypeHint?: string) {
if (!mimeTypeHint && highlightingLoaded) {
if (highlightingLoaded) {
return;
}
// Load theme.
if (!highlightingLoaded) {
const currentThemeName = String(options.get("codeBlockTheme"));
await loadHighlightingTheme(currentThemeName);
}
const currentThemeName = String(options.get("codeBlockTheme"));
await loadHighlightingTheme(currentThemeName);
// Load mime types.
let mimeTypes: MimeType[];
@@ -97,7 +94,7 @@ export async function ensureMimeTypesForHighlighting(mimeTypeHint?: string) {
enabled: true,
mime: mimeTypeHint.replace("-", "/")
}
];
]
} else {
mimeTypes = mime_types.getMimeTypes();
}
@@ -127,9 +124,9 @@ export function isSyntaxHighlightEnabled() {
if (!isShare) {
const theme = options.get("codeBlockTheme");
return !!theme && theme !== "none";
} else {
return true;
}
return true;
}
/**

View File

@@ -224,6 +224,10 @@ body.mobile .modal .modal-dialog {
width: 100%;
}
body.mobile .modal .modal-content {
border-radius: var(--bs-modal-border-radius) var(--bs-modal-border-radius) 0 0;
}
.component {
contain: size;
}
@@ -454,7 +458,7 @@ body.desktop .tabulator-popup-container,
visibility: hidden;
}
.dropdown-menu:not(#context-menu-container) .dropdown-item,
body.desktop .dropdown-menu:not(#context-menu-container) .dropdown-item,
body.desktop .dropdown-menu .dropdown-toggle,
body #context-menu-container .dropdown-item > span,
body.mobile .dropdown .dropdown-submenu > span {
@@ -462,15 +466,6 @@ body.mobile .dropdown .dropdown-submenu > span {
align-items: center;
}
body.mobile .dropdown .dropdown-submenu {
flex-wrap: wrap;
& > span {
flex-grow: 1;
}
}
.dropdown-item span.keyboard-shortcut,
.dropdown-item *:not(.keyboard-shortcut) > kbd {
flex-grow: 1;
@@ -1260,7 +1255,7 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
inset-inline-start: 0;
inset-inline-end: 0;
bottom: 0;
z-index: 2500;
z-index: 1000;
background: rgba(0, 0, 0, 0.1);
}
@@ -1539,8 +1534,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
@media (max-width: 991px) {
body.mobile #launcher-pane .dropdown.global-menu > .dropdown-menu.show,
body.mobile #launcher-container .dropdown > .dropdown-menu.show,
body.mobile .dropdown-menu.mobile-bottom-menu.show {
body.mobile #launcher-container .dropdown > .dropdown-menu.show {
--dropdown-bottom: calc(var(--mobile-bottom-offset) + var(--launcher-pane-size));
position: fixed !important;
bottom: var(--dropdown-bottom) !important;
@@ -1552,10 +1546,6 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
max-height: calc(var(--tn-modal-max-height) - var(--dropdown-bottom));
}
body.mobile .modal-dialog .dropdown-menu.mobile-bottom-menu.show {
--dropdown-bottom: 0;
}
#mobile-sidebar-container {
position: fixed;
top: 0;
@@ -1624,7 +1614,6 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
body.mobile .modal-content {
overflow-y: auto;
border-radius: var(--bs-modal-border-radius) var(--bs-modal-border-radius) 0 0;
}
body.mobile .modal-footer {
@@ -1680,15 +1669,6 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
#detail-container {
background: var(--main-background-color);
}
.modal-dialog {
margin: var(--bs-modal-margin);
max-width: 80%;
}
.modal-content {
height: 100%;
}
}
@media (max-width: 991px) {
@@ -2637,14 +2617,14 @@ iframe.print-iframe {
}
}
/* #root-widget.virtual-keyboard-opened .note-split:not(:focus-within) {
#root-widget.virtual-keyboard-opened .note-split:not(:focus-within) {
max-height: 80px;
opacity: 0.4;
} */
}
}
}
.title-row {
body.desktop .title-row {
height: 50px;
min-height: 50px;
align-items: center;

View File

@@ -14,13 +14,13 @@
--row-moving-background-color: var(--accented-background-color);
--row-text-color: var(--main-text-color);
--row-delimiter-color: var(--more-accented-background-color);
--cell-horiz-padding-size: 8px;
--cell-vert-padding-size: 8px;
--cell-editable-hover-outline-color: var(--main-border-color);
--cell-read-only-text-color: var(--muted-text-color);
--cell-editing-border-color: var(--main-border-color);
--cell-editing-border-width: 2px;
--cell-editing-background-color: var(--ck-color-selector-focused-cell-background);
@@ -40,42 +40,10 @@
border-bottom: var(--col-header-bottom-border);
background: var(--col-header-background-color);
color: var(--col-header-text-color);
font-weight: normal;
}
.tabulator-col.tabulator-range-highlight {
background: inherit;
color: inherit;
font-weight: bold;
}
.tabulator-col-content {
padding: 0 !important;
.tabulator-col-title-holder {
padding: 8px 4px;
}
&:has(.tabulator-header-filter) {
.tabulator-col-title-holder {
padding: 4px;
padding-bottom: 0;
}
}
.tabulator-header-filter {
background: var(--main-background-color);
padding: 2px 1px;
input {
background: var(--main-background-color);
color: var(--main-text-color);
border: 1px solid var(--button-border-color);
border-radius: 3px;
outline: none;
padding: 2px;
}
}
}
.tabulator .tabulator-col-content {
padding: 8px 4px !important;
}
@media (hover: hover) and (pointer: fine) {
@@ -112,6 +80,7 @@
.tabulator-tableholder {
padding-top: 10px;
height: unset !important; /* Don't extend on the full height */
}
/* Rows */
@@ -130,14 +99,6 @@
border-top: none;
border-bottom: 1px solid var(--row-delimiter-color);
color: var(--row-text-color);
&:last-of-type {
border-bottom: none;
}
&.tabulator-range-highlight > .tabulator-cell.tabulator-frozen {
font-weight: bold;
}
}
.tabulator-row.tabulator-row-odd {
@@ -159,14 +120,11 @@
margin-inline-end: var(--cell-editing-border-width);
}
.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left,
.tabulator-row .tabulator-cell {
border-inline-end-color: transparent;
}
.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left {
border-inline-end-color: var(--main-border-color);
}
.tabulator-row .tabulator-cell:not(.tabulator-editable) {
color: var(--cell-read-only-text-color);
}
@@ -216,6 +174,10 @@
margin: 0;
}
.tabulator .tabulator-footer {
color: var(--main-text-color);
}
/* Context menus */
.tabulator-popup-container {
@@ -230,27 +192,8 @@
}
/* Footer */
:root .tabulator .tabulator-footer {
background: transparent;
color: var(--main-text-color);
border-top: 1px solid var(--main-border-color);
border-top: unset;
padding: 10px 0;
.tabulator-page {
background: var(--button-background-color);
color: var(--button-text-color);
border: 1px solid var(--button-border-color);
border-radius: var(--button-border-radius);
&:hover {
border-color: var(--hover-item-border-color);
color: var(--button-text-color);
}
}
select {
background: var(--button-background-color);
color: var(--input-text-color);
border: 1px solid var(--button-border-color);
}
}
}

View File

@@ -134,7 +134,6 @@
--left-pane-collapsed-border-color: #0009;
--left-pane-background-color: #1f1f1f;
--left-pane-text-color: #aaaaaa;
--left-pane-icon-color: #c5c5c5;
--left-pane-item-hover-background: #ffffff0d;
--left-pane-item-selected-background: #ffffff25;
--left-pane-item-selected-color: #dfdfdf;

View File

@@ -127,7 +127,6 @@
--left-pane-collapsed-border-color: #0000000d;
--left-pane-background-color: #f2f2f2;
--left-pane-text-color: #383838;
--left-pane-icon-color: currentColor;
--left-pane-item-hover-background: rgba(0, 0, 0, 0.032);
--left-pane-item-selected-background: white;
--left-pane-item-selected-color: black;

View File

@@ -47,14 +47,9 @@
}
/* The toolbar show / hide button for the current text block */
:root .ck.ck-block-toolbar-button {
--ck-color-block-toolbar-button: var(--muted-text-color);
.ck.ck-block-toolbar-button {
--ck-color-button-on-background: transparent;
--ck-color-button-on-color: var(--ck-editor-toolbar-button-on-color);
translate: -40% 0;
min-width: 0;
padding: 0;
z-index: 1600;
--ck-color-button-on-color: currentColor;
}
:root .ck.ck-toolbar .ck-button:not(.ck-disabled):active,
@@ -522,10 +517,6 @@ button.ck.ck-button:is(.ck-button-action, .ck-button-save, .ck-button-cancel).ck
* EDITOR'S CONTENT
*/
.note-detail-editable-text-editor > .ck-placeholder {
opacity: .5;
}
/*
* Code Blocks
*/

View File

@@ -156,10 +156,6 @@
--preferred-max-content-width: var(--options-card-max-width);
}
.note-split.options .collection-properties {
visibility: hidden;
}
/* Create a gap at the top of the option pages */
.note-detail-content-widget-content.options>*:first-child {
margin-top: var(--options-first-item-top-margin, 1em);

View File

@@ -40,30 +40,13 @@ body.mobile {
/* #region Mica */
/* Quirk: --background-material is read before "theme-supports-background-effects" class
* is applied. Apply the matterial even if the theme doesn't support it. */
body.background-effects.platform-win32 {
&.layout-vertical {
--background-material: mica;
}
&.layout-horizontal {
--background-material: tabbed;
}
/* Quirk: --background-material is read before "theme-supports-background-effects" class
* is applied. Apply the matterial even if the theme doesn't support it. */
--background-material: tabbed;
}
body.background-effects.platform-darwin {
/** Reference: https://developer.apple.com/documentation/appkit/nsvisualeffectview?preferredLanguage=objc **/
&.layout-vertical {
--background-material: under-window;
}
&.layout-horizontal {
--background-material: hud;
}
}
body.background-effects.theme-supports-background-effects {
body.background-effects.theme-supports-background-effects.platform-win32 {
--launcher-pane-horiz-border-color: var(--launcher-pane-horiz-border-color-bgfx);
--launcher-pane-horiz-background-color: var(--launcher-pane-horiz-background-color-bgfx);
--launcher-pane-vert-background-color: var(--launcher-pane-vert-background-color-bgfx);
@@ -73,29 +56,33 @@ body.background-effects.theme-supports-background-effects {
--root-background: transparent;
}
body.background-effects.theme-supports-background-effects.layout-vertical {
body.background-effects.platform-win32.layout-vertical {
--background-material: mica;
}
body.background-effects.theme-supports-background-effects.platform-win32.layout-vertical {
--left-pane-background-color: var(--window-background-color-bgfx);
--center-pane-background-color-bgfx: var(--center-pane-vert-layout-background-color-bgfx);
--right-pane-background-color: var(--right-pane-background-color-bgfx);
}
body.background-effects.theme-supports-background-effects.layout-horizontal {
body.background-effects.theme-supports-background-effects.platform-win32.layout-horizontal {
--center-pane-background-color-bgfx: var(--center-pane-horiz-layout-background-color-bgfx);
--gutter-color: var(--left-pane-background-color);
}
body.background-effects.theme-supports-background-effects,
body.background-effects.theme-supports-background-effects #root-widget {
body.background-effects.theme-supports-background-effects.platform-win32,
body.background-effects.theme-supports-background-effects.platform-win32 #root-widget {
background: var(--window-background-color-bgfx) !important;
}
body.background-effects.theme-supports-background-effects.layout-horizontal #horizontal-main-container,
body.background-effects.theme-supports-background-effects.layout-vertical #vertical-main-container {
body.background-effects.theme-supports-background-effects.platform-win32.layout-horizontal #horizontal-main-container,
body.background-effects.theme-supports-background-effects.platform-win32.layout-vertical #vertical-main-container {
background-color: var(--root-background);
}
/* Note split with background effects */
body.background-effects.theme-supports-background-effects #center-pane .note-split.bgfx {
body.background-effects.theme-supports-background-effects.platform-win32 #center-pane .note-split.bgfx {
--note-split-background-color: var(--center-pane-background-color-bgfx);
}
@@ -769,9 +756,9 @@ body.mobile .fancytree-node > span {
#left-pane .fancytree-custom-icon {
margin-top: 0; /* Use this to align the icon with the tree view item's caption */
color: var(--custom-color, var(--left-pane-icon-color));
}
#left-pane span.fancytree-active .fancytree-title {
font-weight: normal;
}
@@ -1067,7 +1054,7 @@ body.layout-horizontal .tab-row-widget-container {
overflow: hidden;
}
body.desktop:not(.background-effects) #root-widget.horizontal-layout {
body.desktop:not(.background-effects.platform-win32) #root-widget.horizontal-layout {
background-color: var(--root-background) !important;
}
@@ -1272,7 +1259,7 @@ body.layout-horizontal #rest-pane > .classic-toolbar-widget {
#center-pane .note-split {
padding-top: 2px;
background-color: var(--note-split-background-color, var(--main-background-color));
transition: border-color 150ms ease-out;
transition: border-color 250ms ease-in;
border: 2px solid transparent;
}
@@ -1322,7 +1309,7 @@ body.mobile .note-title {
margin-inline-start: 0;
}
body.desktop .title-row {
.title-row {
/* Aligns the "Create new split" button with the note menu button (the three dots button) */
padding-inline-end: 3px;
}

View File

@@ -13,7 +13,8 @@ function injectGlobals() {
uncheckedWindow.$ = $;
uncheckedWindow.WebSocket = () => {};
uncheckedWindow.glob = {
isMainWindow: true
isMainWindow: true,
windowId: "main"
};
}

View File

@@ -29,9 +29,7 @@
"widget-render-error": {
"title": "فشل عرض عنصر واجهة مستخدم React مخصص"
},
"widget-missing-parent": "لا تحتوي الأداة المخصصة على خاصية إلزامية '{{property}}'.\n\nإذا كان من المفترض تشغيل هذا البرنامج النصي بدون عنصر واجهة مستخدم، فاستخدم '#run=frontendStartup' بدلاً من ذلك.",
"open-script-note": "فتح ملاحظة برمجية",
"scripting-error": "خطأ في النص البرمجي المخصص: {{title}}"
"widget-missing-parent": "لا تحتوي الأداة المخصصة على خاصية إلزامية '{{property}}'.\n\nإذا كان من المفترض تشغيل هذا البرنامج النصي بدون عنصر واجهة مستخدم، فاستخدم '#run=frontendStartup' بدلاً من ذلك."
},
"add_link": {
"add_link": "أضافة رابط",
@@ -39,9 +37,7 @@
"search_note": "البحث عن الملاحظة بالاسم",
"link_title": "عنوان الرابط",
"button_add_link": "اضافة رابط",
"help_on_links": "مساعدة حول الارتباطات التشعبية",
"link_title_mirrors": "عنوان الرابط يعكس العنوان الحالي للملاحظة",
"link_title_arbitrary": "يمكن تغيير عنوان الرابط حسب الرغبة"
"help_on_links": "مساعدة حول الارتباطات التشعبية"
},
"branch_prefix": {
"edit_branch_prefix": "تعديل بادئة الفرع",

View File

@@ -1639,11 +1639,7 @@
"configure_launchbar": "配置启动栏"
},
"sql_result": {
"no_rows": "此查询没有返回任何数据",
"not_executed": "查询尚未执行。",
"failed": "SQL 查询执行失败",
"execute_now": "立即执行",
"statement_result": "执行结果"
"no_rows": "此查询没有返回任何数据"
},
"sql_table_schemas": {
"tables": "表"
@@ -1782,8 +1778,8 @@
"desktop-application": "桌面应用程序",
"native-title-bar": "原生标题栏",
"native-title-bar-description": "对于 Windows 和 macOS关闭原生标题栏可使应用程序看起来更紧凑。在 Linux 上,保留原生标题栏可以更好地与系统集成。",
"background-effects": "启用背景效果",
"background-effects-description": "为应用窗口添加模糊且时尚的背景,营造出深度感和现代外观。「原生标题栏」必須被禁用。",
"background-effects": "启用背景效果(仅适用于 Windows 11",
"background-effects-description": "Mica 效果为应用窗口添加模糊且时尚的背景,营造出深度感和现代外观。「原生标题栏」必須被禁用。",
"restart-app-button": "重启应用程序以查看更改",
"zoom-factor": "缩放系数"
},
@@ -1802,8 +1798,7 @@
"geo-map": {
"create-child-note-title": "创建一个新的子笔记并将其添加到地图中",
"create-child-note-instruction": "单击地图以在该位置创建新笔记,或按 Escape 以取消。",
"unable-to-load-map": "无法加载地图。",
"create-child-note-text": "添加标记"
"unable-to-load-map": "无法加载地图。"
},
"geo-map-context": {
"open-location": "打开位置",
@@ -2118,7 +2113,7 @@
},
"call_to_action": {
"background_effects_title": "背景效果现已推出稳定版本",
"background_effects_message": "在 Windows 和 macOS 设备上,背景效果现在已稳定。背景效果通过模糊背后的背景,为使用者界面增添一抹色彩。",
"background_effects_message": "在 Windows 装置上,背景效果现在已完全稳定。背景效果通过模糊背后的背景,为使用者界面增添一抹色彩。此技术也用于其他应用程序,例如 Windows 资源管理器。",
"background_effects_button": "启用背景效果",
"next_theme_title": "试用新 Trilium 主题",
"next_theme_message": "当前使用旧版主题,要试用新主题吗?",
@@ -2254,12 +2249,5 @@
"pages_alt": "第{{pageNumber}}页",
"pages_loading": "加载中...",
"layers_other": "{{count}} 层"
},
"platform_indicator": {
"available_on": "在 {{platform}} 上可用"
},
"mobile_tab_switcher": {
"title_other": "{{count}} 选项卡",
"more_options": "更多选项"
}
}

View File

@@ -28,9 +28,9 @@
},
"open-script-note": "Script-Notiz öffnen",
"widget-render-error": {
"title": "Benutzerdefiniertes React-Widget konnte nicht dargestellt werden"
"title": "Eine externe React Integration konnte nicht dargestellt werden"
},
"widget-missing-parent": "Benutzerdefiniertes Widget hat die erforderliche '{{property}}'-Eigenschaft nicht korrekt definiert.\n\nFalls dieses Skript ohne UI-Element ausgeführt werden soll, benutze stattdessen '#run=frontendStartup'.",
"widget-missing-parent": "Der externen Integration fehlt die erforderliche Eigenschaft '{{property}}'\n\nFalls dieses Skript ohne UI-Element ausgeführt werden soll, benutze stattdessen '#run=frontendStartup'.",
"scripting-error": "Benutzerdefinierter Skriptfehler: {{title}}"
},
"add_link": {
@@ -129,7 +129,7 @@
"scrollToActiveNote": "Scrolle zur aktiven Notiz",
"jumpToParentNote": "Zur übergeordneten Notiz springen",
"collapseWholeTree": "Reduziere den gesamten Notizbaum",
"collapseSubTree": "Zweig einklappen",
"collapseSubTree": "Teilbaum einklappen",
"tabShortcuts": "Tab-Tastenkürzel",
"newTabNoteLink": "auf den Notizlink öffnet die Notiz in einem neuen Tab",
"onlyInDesktop": "Nur im Desktop (Electron Build)",
@@ -230,7 +230,7 @@
"move_to": {
"dialog_title": "Notizen verschieben nach ...",
"notes_to_move": "Notizen zum Verschieben",
"target_parent_note": "Übergeordnete Notiz bestimmen",
"target_parent_note": "Ziel-Elternnotiz",
"search_placeholder": "Suche nach einer Notiz anhand ihres Namens",
"move_button": "Zur ausgewählten Notiz wechseln",
"error_no_path": "Kein Weg, auf den man sich bewegen kann.",
@@ -333,8 +333,8 @@
"target_note_title": "Eine Beziehung ist eine benannte Verbindung zwischen Quellnotiz und Zielnotiz.",
"target_note": "Zielnotiz",
"promoted_title": "Das heraufgestufte Attribut wird deutlich in der Notiz angezeigt.",
"promoted": "Hervorgehoben",
"promoted_alias_title": "Der Name, der in der Benutzeroberfläche für hervorgehobene Attribute angezeigt werden soll.",
"promoted": "Gefördert",
"promoted_alias_title": "Der Name, der in der Benutzeroberfläche für heraufgestufte Attribute angezeigt werden soll.",
"promoted_alias": "Alias",
"multiplicity_title": "Multiplizität definiert, wie viele Attribute mit demselben Namen erstellt werden können maximal 1 oder mehr als 1.",
"multiplicity": "Vielzahl",
@@ -367,7 +367,7 @@
"disable_versioning": "deaktiviert die automatische Versionierung. Nützlich z.B. große, aber unwichtige Notizen z.B. große JS-Bibliotheken, die für die Skripterstellung verwendet werden",
"calendar_root": "Markiert eine Notiz, die als Basis für Tagesnotizen verwendet werden soll. Nur einer sollte als solcher gekennzeichnet sein.",
"archived": "Notizen mit dieser Bezeichnung werden standardmäßig nicht in den Suchergebnissen angezeigt (auch nicht in den Dialogen „Springen zu“, „Link hinzufügen“ usw.).",
"exclude_from_export": "Notizen (mit ihrem Unterbaum) werden nicht im Notizexport inkludiert",
"exclude_from_export": "Notizen (mit ihrem Unterbaum) werden nicht in den Notizexport einbezogen",
"run": "Definiert, bei welchen Ereignissen das Skript ausgeführt werden soll. Mögliche Werte sind:\n<ul>\n<li>frontendStartup - wenn das Trilium-Frontend startet (oder aktualisiert wird), außer auf mobilen Geräten.</li>\n<li>mobileStartup - wenn das Trilium-Frontend auf einem mobilen Gerät startet (oder aktualisiert wird).</li>\n<li>backendStartup - wenn das Trilium-Backend startet</li>\n<li>hourly - einmal pro Stunde ausführen. Du kannst das zusätzliche Label <code>runAtHour</code> verwenden, um die genaue Stunde festzulegen.</li>\n<li>daily - einmal pro Tag ausführen</li>\n</ul>",
"run_on_instance": "Definiere, auf welcher Trilium-Instanz dies ausgeführt werden soll. Standardmäßig alle Instanzen.",
"run_at_hour": "Zu welcher Stunde soll das laufen? Sollte zusammen mit <code>#runu003dhourly</code> verwendet werden. Kann für mehr Läufe im Laufe des Tages mehrfach definiert werden.",
@@ -376,7 +376,7 @@
"sort_direction": "ASC (Standard) oder DESC",
"sort_folders_first": "Ordner (Notizen mit Unternotizen) sollten oben sortiert werden",
"top": "Behalte die angegebene Notiz oben in der übergeordneten Notiz (gilt nur für sortierte übergeordnete Notizen)",
"hide_promoted_attributes": "Hervorgehobene Attribute für diese Notiz ausblenden",
"hide_promoted_attributes": "Heraufgestufte Attribute für diese Notiz ausblenden",
"read_only": "Der Editor befindet sich im schreibgeschützten Modus. Funktioniert nur für Text- und Codenotizen.",
"auto_read_only_disabled": "Text-/Codenotizen können automatisch in den Lesemodus versetzt werden, wenn sie zu groß sind. Du kannst dieses Verhalten für jede einzelne Notiz deaktivieren, indem du diese Beschriftung zur Notiz hinzufügst",
"app_css": "markiert CSS-Notizen, die in die Trilium-Anwendung geladen werden und somit zur Änderung des Aussehens von Trilium verwendet werden können.",
@@ -416,13 +416,13 @@
"toc": "<code>#toc</code> oder <code>#tocu003dshow</code> erzwingen die Anzeige des Inhaltsverzeichnisses, <code>#tocu003dhide</code> erzwingt das Ausblenden. Wenn die Bezeichnung nicht vorhanden ist, wird die globale Einstellung beachtet",
"color": "Definiert die Farbe der Notiz im Notizbaum, in Links usw. Verwende einen beliebigen gültigen CSS-Farbwert wie „rot“ oder #a13d5f",
"keyboard_shortcut": "Definiert eine Tastenkombination, die sofort zu dieser Notiz springt. Beispiel: „Strg+Alt+E“. Erfordert ein Neuladen des Frontends, damit die Änderung wirksam wird.",
"keep_current_hoisting": "Das Öffnen dieses Links ändert das Hochziehen nicht, selbst wenn die Notiz im aktuell hochgezogenen Zweig nicht angezeigt werden kann.",
"keep_current_hoisting": "Das Öffnen dieses Links ändert das Hochziehen nicht, selbst wenn die Notiz im aktuell hochgezogenen Unterbaum nicht angezeigt werden kann.",
"execute_button": "Titel der Schaltfläche, welche die aktuelle Codenotiz ausführt",
"execute_description": "Längere Beschreibung der aktuellen Codenotiz, die zusammen mit der Schaltfläche „Ausführen“ angezeigt wird",
"exclude_from_note_map": "Notizen mit dieser Bezeichnung werden in der Notizenkarte ausgeblendet",
"new_notes_on_top": "Neue Notizen werden oben in der übergeordneten Notiz erstellt, nicht unten.",
"hide_highlight_widget": "Widget „Markierungsliste“ ausblenden",
"run_on_note_creation": "Wird ausgeführt, wenn eine Notiz im Backend erstellt wird. Verwende diese Beziehung, wenn du das Skript für alle Notizen ausführen möchtest, die unter einem bestimmten Zweig erstellt wurden. Erstelle es in diesem Fall auf der Stammnotiz und mache es vererbbar. Eine neue Notiz, die innerhalb des Zweigs (beliebige Tiefe) erstellt wird, löst das Skript aus.",
"hide_highlight_widget": "Widget „Hervorhebungsliste“ ausblenden",
"run_on_note_creation": "Wird ausgeführt, wenn eine Notiz im Backend erstellt wird. Verwende diese Beziehung, wenn du das Skript für alle Notizen ausführen möchtest, die unter einer bestimmten Unternotiz erstellt wurden. Erstelle es in diesem Fall auf der Unternotiz-Stammnotiz und mache es vererbbar. Eine neue Notiz, die innerhalb der Unternotiz (beliebige Tiefe) erstellt wird, löst das Skript aus.",
"run_on_child_note_creation": "Wird ausgeführt, wenn eine neue Notiz unter der Notiz erstellt wird, in der diese Beziehung definiert ist",
"run_on_note_title_change": "Wird ausgeführt, wenn der Notiztitel geändert wird (einschließlich der Notizerstellung)",
"run_on_note_content_change": "Wird ausgeführt, wenn der Inhalt einer Notiz geändert wird (einschließlich der Erstellung von Notizen).",
@@ -433,8 +433,8 @@
"run_on_branch_deletion": "wird ausgeführt, wenn ein Zweig gelöscht wird. Der Zweig ist eine Verknüpfung zwischen der übergeordneten Notiz und der untergeordneten Notiz und wird z. B. gelöscht. beim Verschieben der Notiz (alter Zweig/Link wird gelöscht).",
"run_on_attribute_creation": "wird ausgeführt, wenn für die Notiz ein neues Attribut erstellt wird, das diese Beziehung definiert",
"run_on_attribute_change": " wird ausgeführt, wenn das Attribut einer Notiz geändert wird, die diese Beziehung definiert. Dies wird auch ausgelöst, wenn das Attribut gelöscht wird",
"relation_template": "Die Attribute der Notiz werden auch ohne eine Hierarchische-Beziehung vererbt. Der Inhalt und der Zweig werden den Instanznotizen hinzugefügt, wenn sie leer sind. Einzelheiten findest du in der Dokumentation.",
"inherit": "Die Attribute einer Notiz werden auch ohne eine Hierarchische-Beziehung vererbt. Ein ähnliches Konzept findest du unter Vorlagenbeziehung. Siehe Attributsvererbung in der Dokumentation.",
"relation_template": "Die Attribute der Notiz werden auch ohne eine Eltern-Kind-Beziehung vererbt. Der Inhalt und der Unterbaum der Notiz werden den Instanznotizen hinzugefügt, wenn sie leer sind. Einzelheiten findest du in der Dokumentation.",
"inherit": "Die Attribute einer Notiz werden auch ohne eine Eltern-Kind-Beziehung vererbt. Ein ähnliches Konzept findest du unter Vorlagenbeziehung. Siehe Attributvererbung in der Dokumentation.",
"render_note": "Notizen vom Typ \"HTML-Notiz rendern\" werden mit einer Code-Notiz (HTML oder Skript) gerendert, und es ist notwendig, über diese Beziehung anzugeben, welche Notiz gerendert werden soll",
"widget_relation": "Das Ziel dieser Beziehung wird ausgeführt und als Widget in der Seitenleiste gerendert",
"share_css": "CSS-Hinweis, der in die Freigabeseite eingefügt wird. Die CSS-Notiz muss sich ebenfalls im gemeinsamen Unterbaum befinden. Erwäge auch die Verwendung von „share_hidden_from_tree“ und „share_omit_default_css“.",
@@ -632,7 +632,7 @@
"show_toc": "Inhaltsverzeichnis anzeigen"
},
"show_highlights_list_widget_button": {
"show_highlights_list": "Markierungsliste anzeigen"
"show_highlights_list": "Hervorhebungen anzeigen"
},
"global_menu": {
"menu": "Menü",
@@ -645,8 +645,8 @@
"zoom_out": "Herauszoomen",
"reset_zoom_level": "Zoomstufe zurücksetzen",
"zoom_in": "Hineinzoomen",
"configure_launchbar": "Konfiguriere die Starterleiste",
"show_shared_notes_subtree": "Zweig „Freigegebene Notizen“ anzeigen",
"configure_launchbar": "Konfiguriere die Launchbar",
"show_shared_notes_subtree": "Unterbaum „Freigegebene Notizen“ anzeigen",
"advanced": "Erweitert",
"open_dev_tools": "Öffne die Entwicklungstools",
"open_sql_console": "Öffne die SQL-Konsole",
@@ -655,7 +655,7 @@
"show_backend_log": "Backend-Protokoll anzeigen",
"reload_hint": "Ein Neuladen kann bei einigen visuellen Störungen Abhilfe schaffen, ohne die gesamte App neu starten zu müssen.",
"reload_frontend": "Frontend neu laden",
"show_hidden_subtree": "Versteckten Zweig anzeigen",
"show_hidden_subtree": "Versteckten Teilbaum anzeigen",
"show_help": "Hilfe anzeigen",
"about": "Über Trilium Notes",
"logout": "Abmelden",
@@ -703,8 +703,8 @@
"export_as_image_png": "PNG (Raster)",
"export_as_image_svg": "SVG (Vektor)",
"note_map": "Notizen Karte",
"view_revisions": "Notizrevisionen...",
"advanced": "Erweitert"
"view_revisions": "Änderungshistorie...",
"advanced": "Fortgeschritten"
},
"onclick_button": {
"no_click_handler": "Das Schaltflächen-Widget „{{componentId}}“ hat keinen definierten Klick-Handler"
@@ -720,7 +720,7 @@
"update_available": "Update verfügbar"
},
"note_launcher": {
"this_launcher_doesnt_define_target_note": "Dieser Starter definiert keine Zielnotiz."
"this_launcher_doesnt_define_target_note": "Dieser Launcher definiert keine Zielnotiz."
},
"code_buttons": {
"execute_button_title": "Skript ausführen",
@@ -763,8 +763,8 @@
"change_note_icon": "Notiz-Icon ändern",
"search": "Suche:",
"reset-default": "Standard wiederherstellen",
"search_placeholder_one": "Suche {{number}} Symbole über {{count}} Pakete",
"search_placeholder_other": "Suche {{number}} Symbole über {{count}} Pakete",
"search_placeholder_one": "Suche {{number}} Icons über {{count}} Pakete",
"search_placeholder_other": "Suche {{number}} Icons über {{count}} Pakete",
"search_placeholder_filtered": "Suche {{number}} Icons in {{name}}",
"filter": "Filter",
"filter-none": "Alle Icons",
@@ -798,7 +798,7 @@
"expand_tooltip": "Erweitert die direkten Unterelemente dieser Sammlung (eine Ebene tiefer). Für weitere Optionen auf den Pfeil rechts klicken.",
"expand_first_level": "Direkte Unterelemente erweitern",
"expand_nth_level": "{{depth}} Ebenen erweitern",
"hide_child_notes": "Unternotizen im Baum ausblenden"
"hide_child_notes": "Unterknoten im Baum ausblenden"
},
"edited_notes": {
"no_edited_notes_found": "An diesem Tag wurden noch keine Notizen bearbeitet...",
@@ -842,7 +842,7 @@
"note_size": "Notengröße",
"note_size_info": "Die Notizgröße bietet eine grobe Schätzung des Speicherbedarfs für diese Notiz. Es berücksichtigt den Inhalt der Notiz und den Inhalt ihrer Notizrevisionen.",
"calculate": "berechnen",
"subtree_size": "(Zweiggröße: {{size}} in {{count}} Notizen)",
"subtree_size": "(Teilbaumgröße: {{size}} in {{count}} Notizen)",
"title": "Notizinfo",
"mime": "MIME Typ",
"show_similar_notes": "Zeige ähnliche Notizen"
@@ -871,7 +871,7 @@
"owned_attributes": "Eigene Attribute"
},
"promoted_attributes": {
"promoted_attributes": "Hervorgehobene Attribute",
"promoted_attributes": "Übergebene Attribute",
"url_placeholder": "http://website...",
"open_external_link": "Externen Link öffnen",
"unknown_label_type": "Unbekannter Labeltyp „{{type}}“",
@@ -1115,7 +1115,7 @@
"vacuum_database": {
"title": "Datenbank aufräumen",
"description": "Dadurch wird die Datenbank neu erstellt, was normalerweise zu einer kleineren Datenbankdatei führt. Es werden keine Daten tatsächlich geändert.",
"button_text": "Datenbank aufräumen",
"button_text": "Vakuumdatenbank",
"vacuuming_database": "Datenbank wird geleert...",
"database_vacuumed": "Die Datenbank wurde geleert"
},
@@ -1156,7 +1156,7 @@
},
"ribbon": {
"widgets": "Multifunktionsleisten-Widgets",
"promoted_attributes_message": "Die „Hervorgehobene Attribute“-Leiste wird automatisch geöffnet, wenn in der Notiz hervorgehobene Attribute vorhanden sind",
"promoted_attributes_message": "Die Multifunktionsleisten-Registerkarte „Heraufgestufte Attribute“ wird automatisch geöffnet, wenn in der Notiz heraufgestufte Attribute vorhanden sind",
"edited_notes_message": "Die Multifunktionsleisten-Registerkarte „Bearbeitete Notizen“ wird bei Tagesnotizen automatisch geöffnet"
},
"theme": {
@@ -1169,7 +1169,7 @@
"layout": "Layout",
"layout-vertical-title": "Vertikal",
"layout-horizontal-title": "Horizontal",
"layout-vertical-description": "Startleiste ist auf der linken Seite (Standard)",
"layout-vertical-description": "Startleiste ist auf der linken Seite (standard)",
"layout-horizontal-description": "Startleiste ist unter der Tableiste. Die Tableiste wird dadurch auf die ganze Breite erweitert.",
"auto_theme": "Alt (Folge dem Farbschema des Systems)",
"light_theme": "Alt (Hell)",
@@ -1177,7 +1177,7 @@
},
"zoom_factor": {
"title": "Zoomfaktor (nur Desktop-Build)",
"description": "Das Zoomen kann auch mit den Tastenkombinationen Strg+- und Strg+= gesteuert werden."
"description": "Das Zoomen kann auch mit den Tastenkombinationen STRG+- und STRG+u003d gesteuert werden."
},
"code_auto_read_only_size": {
"title": "Automatische schreibgeschützte Größe",
@@ -1266,16 +1266,16 @@
"markdown": "Markdown-Stil"
},
"highlights_list": {
"title": "Markierungsliste",
"description": "Du kannst die im rechten Bereich angezeigte Markierungsliste anpassen:",
"title": "Highlights-Liste",
"description": "Du kannst die im rechten Bereich angezeigte Highlights-Liste anpassen:",
"bold": "Fettgedruckter Text",
"italic": "Kursiver Text",
"underline": "Unterstrichener Text",
"color": "Farbiger Text",
"bg_color": "Text mit Hintergrundfarbe",
"visibility_title": "Sichtbarkeit der Markierungsliste",
"visibility_description": "Du kannst das Markierungs-Widget pro Notiz ausblenden, indem du die Beschriftung #hideHighlightWidget hinzufügst.",
"shortcut_info": "Du kannst eine Tastenkombination zum schnellen Umschalten des rechten Bereichs (einschließlich Markierungen) in den Optionen -> Tastenkombinationen konfigurieren (Name „toggleRightPane“)."
"visibility_title": "Sichtbarkeit der Highlights-Liste",
"visibility_description": "Du kannst das Hervorhebungs-Widget pro Notiz ausblenden, indem du die Beschriftung #hideHighlightWidget hinzufügst.",
"shortcut_info": "Du kannst eine Tastenkombination zum schnellen Umschalten des rechten Bereichs (einschließlich Hervorhebungen) in den Optionen -> Tastenkombinationen konfigurieren (Name „toggleRightPane“)."
},
"table_of_contents": {
"title": "Inhaltsverzeichnis",
@@ -1445,19 +1445,19 @@
"insert-note-after": "Notiz dahinter einfügen",
"insert-child-note": "Unternotiz einfügen",
"delete": "Löschen",
"search-in-subtree": "Im Zweig suchen",
"search-in-subtree": "Im Notizbaum suchen",
"hoist-note": "Notiz-Fokus setzen",
"unhoist-note": "Notiz-Fokus aufheben",
"edit-branch-prefix": "Zweig-Präfix bearbeiten",
"advanced": "Erweitert",
"expand-subtree": "Zweig aufklappen",
"collapse-subtree": "Zweig einklappen",
"expand-subtree": "Unterzweig aufklappen",
"collapse-subtree": "Notizbaum einklappen",
"sort-by": "Sortieren nach...",
"recent-changes-in-subtree": "Kürzliche Änderungen im Zweig",
"recent-changes-in-subtree": "Kürzliche Änderungen im Notizbaum",
"convert-to-attachment": "Als Anhang konvertieren",
"copy-note-path-to-clipboard": "Notiz-Pfad in die Zwischenablage kopieren",
"protect-subtree": "Zweig schützen",
"unprotect-subtree": "Zweig-Schutz aufheben",
"protect-subtree": "Notizbaum schützen",
"unprotect-subtree": "Notizenbaum-Schutz aufheben",
"copy-clone": "Kopieren / Klonen",
"clone-to": "Klonen nach...",
"cut": "Ausschneiden",
@@ -1474,12 +1474,12 @@
"archive": "Archiviere",
"unarchive": "Entarchivieren",
"open-in-a-new-window": "In neuem Fenster öffnen",
"hide-subtree": "Zweig ausblenden",
"show-subtree": "Zweig anzeigen"
"hide-subtree": "Teilbaum ausblenden",
"show-subtree": "Teilbaum anzeigen"
},
"shared_info": {
"shared_publicly": "Diese Notiz ist öffentlich freigegeben über {{- link}}.",
"shared_locally": "Diese Notiz ist lokal freigegeben über {{- link}}.",
"shared_publicly": "Diese Notiz ist öffentlich geteilt auf {{- link}}.",
"shared_locally": "Diese Notiz ist lokal geteilt auf {{- link}}.",
"help_link": "Für Hilfe besuche <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki</a>."
},
"note_types": {
@@ -1490,12 +1490,12 @@
"note-map": "Notizkarte",
"render-note": "Render Notiz",
"mermaid-diagram": "Mermaid Diagramm",
"canvas": "Leinwand",
"canvas": "Canvas",
"web-view": "Webansicht",
"mind-map": "Mind Map",
"file": "Datei",
"image": "Bild",
"launcher": "Starter",
"launcher": "Launcher",
"doc": "Dokument",
"widget": "Widget",
"confirm-change": "Es is nicht empfehlenswert den Notiz-Typ zu ändern, wenn der Inhalt der Notiz nicht leer ist. Möchtest du dennoch fortfahren?",
@@ -1514,10 +1514,10 @@
"toggle-off-hint": "Notiz ist geschützt, klicken, um den Schutz aufzuheben"
},
"shared_switch": {
"shared": "Freigegeben",
"toggle-on-title": "Notiz freigeben",
"shared": "Teilen",
"toggle-on-title": "Notiz teilen",
"toggle-off-title": "Notiz-Freigabe aufheben",
"shared-branch": "Diese Notiz existiert nur als freigegebene Notiz, das Aufheben der Freigabe würde sie löschen. Möchtest du fortfahren und die Notiz damit löschen?",
"shared-branch": "Diese Notiz existiert nur als geteilte Notiz, das Aufheben der Freigabe würde sie löschen. Möchtest du fortfahren und die Notiz damit löschen?",
"inherited": "Die Notiz kann hier nicht von der Freigabe entfernt werden, da sie über Vererbung von einer übergeordneten Notiz geteilt wird."
},
"template_switch": {
@@ -1535,13 +1535,13 @@
"replace_all": "Alle Ersetzen"
},
"highlights_list_2": {
"title": "Markierungsliste",
"title": "Hervorhebungs-Liste",
"options": "Optionen",
"title_with_count_one": "{{count}} Markierung",
"title_with_count_other": "{{count}} Markierungen",
"modal_title": "Markierungsliste konfigurieren",
"menu_configure": "Markierungsliste konfigurieren…",
"no_highlights": "Keine Markierungen gefunden."
"title_with_count_one": "{{count}} Highlight",
"title_with_count_other": "{{count}} Highlights",
"modal_title": "Highlight Liste konfigurieren",
"menu_configure": "Highlight Liste konfigurieren…",
"no_highlights": "Keine Highlights gefunden."
},
"quick-search": {
"placeholder": "Schnellsuche",
@@ -1566,15 +1566,15 @@
"unhoist": "Fokus verlassen",
"toggle-sidebar": "Seitenleiste ein-/ausblenden",
"dropping-not-allowed": "Ablegen von Notizen an dieser Stelle ist nicht zulässig.",
"clone-indicator-tooltip": "Diese Notiz hat {{- count}} übergeordnete Knoten: {{- parents}}",
"clone-indicator-tooltip-single": "Diese Notiz ist geklont (1 weitere Quelle: {{- parent}})",
"shared-indicator-tooltip": "Diese Notiz ist öffentlich freigegeben",
"shared-indicator-tooltip-with-url": "Diese Notiz ist öffentlich freigegeben unter: {{- url}}",
"subtree-hidden-tooltip_one": "{{count}} untergeordnete Notiz, die im Baum ausgeblendet ist",
"subtree-hidden-tooltip_other": "{{count}} untergeordnete Notizen, die im Baum ausgeblendet sind",
"clone-indicator-tooltip": "Diese Notiz hat {{- count}} Elterknoten: {{- parents}}",
"clone-indicator-tooltip-single": "Diese Notiz ist geklont (1 weiterer Elternknoten: {{- parent}})",
"shared-indicator-tooltip": "Diese Notiz ist öffentlich einsehbar",
"shared-indicator-tooltip-with-url": "Diese Notiz ist unter {{- url}} öffentlich einsehbar",
"subtree-hidden-tooltip_one": "{{count}} Unterknoten, der im Baum ausgeblendet ist",
"subtree-hidden-tooltip_other": "{{count}} Unterknoten, die im Baum ausgeblendet sind",
"subtree-hidden-moved-title": "Zu {{title}} hinzugefügt",
"subtree-hidden-moved-description-collection": "Diese Sammlung blendet ihre Unternotizen im Baum aus.",
"subtree-hidden-moved-description-other": "Untergeordnete Notizen sind im Baum für diese Notiz ausgeblendet."
"subtree-hidden-moved-description-collection": "Diese Sammlung blendet ihre Unternotizem im Baum aus.",
"subtree-hidden-moved-description-other": "Diese Sammlung blendet ihre Unterknoten im Baum aus."
},
"title_bar_buttons": {
"window-on-top": "Dieses Fenster immer oben halten"
@@ -1586,8 +1586,8 @@
"print_report_title": "Druckreport",
"print_report_collection_details_button": "Details anzeigen",
"print_report_collection_details_ignored_notes": "Ignorierte Notizen",
"print_report_collection_content_one": "{{count}} Notiz in der Sammlung konnte nicht gedruckt werden, weil sie nicht unterstützt oder geschützt ist.",
"print_report_collection_content_other": "{{count}} Notizen in der Sammlung konnten nicht gedruckt werden, weil sie nicht unterstützt oder geschützt sind."
"print_report_collection_content_one": "{{count}} Notiz in der Sammlung konnte nicht gedruckt werden, weil sie nicht unterstützt ist oder geschützt ist.",
"print_report_collection_content_other": "{{count}} Notizen in der Sammlung konnten nicht gedruckt werden, weil sie nicht unterstützt sind oder geschützt sind."
},
"note_title": {
"placeholder": "Titel der Notiz hier eingeben…",
@@ -1595,7 +1595,7 @@
"last_modified": "Bearbeitet am <Value />",
"note_type_switcher_label": "Ändere von {{type}} zu:",
"note_type_switcher_others": "Andere Notizart",
"note_type_switcher_templates": "Vorlage",
"note_type_switcher_templates": "Template",
"note_type_switcher_collection": "Sammlung",
"edited_notes": "Notizen, bearbeitet an diesem Tag",
"promoted_attributes": "Hervorgehobene Attribute"
@@ -1605,14 +1605,10 @@
"search_not_executed": "Die Suche wurde noch nicht ausgeführt. Klicke oben auf „Suchen“, um die Ergebnisse anzuzeigen."
},
"spacer": {
"configure_launchbar": "Starterleiste konfigurieren"
"configure_launchbar": "Startleiste konfigurieren"
},
"sql_result": {
"no_rows": "Es wurden keine Zeilen für diese Abfrage zurückgegeben",
"not_executed": "Die Abfrage wurde noch nicht ausgeführt.",
"failed": "SQL-Abfrage ist fehlgeschlagen",
"execute_now": "Jetzt ausführen",
"statement_result": "Abfrageergebnis"
"no_rows": "Es wurden keine Zeilen für diese Abfrage zurückgegeben"
},
"sql_table_schemas": {
"tables": "Tabellen"
@@ -1683,16 +1679,16 @@
"confirm_unhoisting": "Die angeforderte Notiz {{requestedNote}} befindet sich außerhalb des hoisted Bereichs der Notiz {{hoistedNote}}. Du musst sie unhoisten, um auf die Notiz zuzugreifen. Möchtest du mit dem Unhoisting fortfahren?"
},
"launcher_context_menu": {
"reset_launcher_confirm": "Möchtest du „{{title}}“ wirklich zurücksetzen? Alle Daten / Einstellungen in dieser Notiz (und ihren Unternotizen) gehen verloren und der Starter wird an seinen ursprünglichen Standort zurückgesetzt.",
"add-note-launcher": "Notiz-Starter hinzufügen",
"add-script-launcher": "Skript-Starter hinzufügen",
"reset_launcher_confirm": "Möchtest du „{{title}}“ wirklich zurücksetzen? Alle Daten / Einstellungen in dieser Notiz (und ihren Unternotizen) gehen verloren und der Launcher wird an seinen ursprünglichen Standort zurückgesetzt.",
"add-note-launcher": "Launcher für Notiz hinzufügen",
"add-script-launcher": "Launcher für Skript hinzufügen",
"add-custom-widget": "Benutzerdefiniertes Widget hinzufügen",
"add-spacer": "Abstandhalter hinzufügen",
"add-spacer": "Spacer hinzufügen",
"delete": "Löschen <kbd data-command=\"deleteNotes\"></kbd>",
"reset": "Zurücksetzen",
"move-to-visible-launchers": "Zu sichtbaren Startern verschieben",
"move-to-available-launchers": "Zu verfügbaren Startern verschieben",
"duplicate-launcher": "Starter duplizieren <kbd data-command=\"duplicateSubtree\">"
"move-to-visible-launchers": "Zu sichtbaren Launchern verschieben",
"move-to-available-launchers": "Zu verfügbaren Launchern verschieben",
"duplicate-launcher": "Launcher duplizieren <kbd data-command=\"duplicateSubtree\">"
},
"highlighting": {
"description": "Steuert die Syntaxhervorhebung für Codeblöcke in Textnotizen, Code-Notizen sind nicht betroffen.",
@@ -1701,7 +1697,7 @@
},
"code_block": {
"word_wrapping": "Wortumbruch",
"theme_none": "Keine Syntaxhervorhebung",
"theme_none": "Keine Syntax-Hervorhebung",
"theme_group_light": "Helle Themen",
"theme_group_dark": "Dunkle Themen",
"copy_title": "Kopiere in Zwischenablage"
@@ -1751,8 +1747,8 @@
"desktop-application": "Desktop Anwendung",
"native-title-bar": "Native Anwendungsleiste",
"native-title-bar-description": "In Windows und macOS, sorgt das Deaktivieren der nativen Anwendungsleiste für ein kompakteres Aussehen. Unter Linux, sorgt das Aktivieren der nativen Anwendungsleiste für eine bessere Integration mit anderen Teilen des Systems.",
"background-effects": "Hintergrundeffekte aktivieren",
"background-effects-description": "Fügt einen unscharfen, stylischen Hintergrund in das Anwendungsfenstern ein. Dies erzeugt Tiefe und ein modernes Auftreten. \"Native Titelleiste\" muss deaktiviert sein.",
"background-effects": "Hintergrundeffekte aktivieren (nur Windows 11)",
"background-effects-description": "Der Mica Effekt fügt einen unscharfen, stylischen Hintergrund in Anwendungsfenstern ein. Dieser erzeugt Tiefe und ein modernes Auftreten. \"Native Titelleiste\" muss deaktiviert sein.",
"restart-app-button": "Anwendung neustarten um Änderungen anzuwenden",
"zoom-factor": "Zoomfaktor"
},
@@ -1771,8 +1767,7 @@
"geo-map": {
"create-child-note-title": "Neue Unternotiz anlegen und zur Karte hinzufügen",
"create-child-note-instruction": "Auf die Karte klicken, um eine neue Notiz an der Stelle zu erstellen oder Escape drücken um abzubrechen.",
"unable-to-load-map": "Karte konnte nicht geladen werden.",
"create-child-note-text": "Marker hinzufügen"
"unable-to-load-map": "Karte konnte nicht geladen werden."
},
"geo-map-context": {
"open-location": "Ort öffnen",
@@ -2001,7 +1996,7 @@
"check_share_root": "Status des Freigabe-Roots prüfen",
"share_root_found": "Freigabe-Root-Notiz '{{noteTitle}}' ist bereit",
"share_root_not_found": "Keine Notiz mit #shareRoot Label gefunden",
"share_root_not_shared": "Notiz '{{noteTitle}}' hat das #shareRoot Label, wurde jedoch noch nicht freigegeben"
"share_root_not_shared": "Notiz '{{noteTitle}}' hat das #shareRoot Label, wurde jedoch noch nicht geteilt"
},
"tasks": {
"due": {
@@ -2119,8 +2114,8 @@
"show_attachments_description": "Notizanhänge anzeigen",
"search_notes_title": "Suche Notiz",
"search_notes_description": "Öffne erweiterte Suche",
"search_subtree_title": "Im Zweig suchen",
"search_subtree_description": "Im aktuellen Zweig suchen",
"search_subtree_title": "Im Unterzweig suchen",
"search_subtree_description": "Im aktuellen Unterzweig suchen",
"search_history_title": "Zeige Suchhistorie",
"search_history_description": "Zeige vorherige Suchen",
"configure_launch_bar_title": "Startleiste anpassen",
@@ -2134,7 +2129,7 @@
"next_theme_message": "Es wird aktuell das alte Design verwendet. Möchten Sie das neue Design ausprobieren?",
"next_theme_button": "Teste das neue Design",
"background_effects_title": "Hintergrundeffekte sind jetzt zuverlässig nutzbar",
"background_effects_message": "Auf Windows- und macOS-Geräten sind die Hintergrundeffekte nun stabil. Die Hintergrundeffekte verleihen der Benutzeroberfläche einen Farbakzent, indem der Hintergrund dahinter weichgezeichnet wird.",
"background_effects_message": "Auf Windows-Geräten sind die Hintergrundeffekte nun vollständig stabil. Die Hintergrundeffekte verleihen der Benutzeroberfläche einen Farbakzent, indem der Hintergrund dahinter weichgezeichnet wird. Diese Technik wird auch in anderen Anwendungen wie dem Windows-Explorer eingesetzt.",
"background_effects_button": "Aktiviere Hintergrundeffekte",
"dismiss": "Ablehnen",
"new_layout_title": "Neues Layout",
@@ -2189,9 +2184,9 @@
"new_layout_description": "Probiere das neue Layout für eine modernere Darstellung und verbesserte Benutzbarkeit aus. Kann sich in Zukunft stark ändern."
},
"server": {
"unknown_http_error_title": "Kommunikationsfehler mit dem Server",
"unknown_http_error_title": "Bei der Kommunikation mit dem Server ist ein Fehler aufgetreten",
"unknown_http_error_content": "Statuscode: {{statusCode}}\nURL: {{method}} {{url}}\nNachricht: {{message}}",
"traefik_blocks_requests": "Der Traefik Reverse-Proxy hat eine Änderung erfahren, welches die Kommunikation mit dem Server beeinflusst."
"traefik_blocks_requests": "Der Traefik Reverse-Proxy hat ein fatales Update bekommen, welche die Kommunikation mit dem Server stört."
},
"tab_history_navigation_buttons": {
"go-back": "Zur vorherigen Notiz zurück kehren",
@@ -2206,30 +2201,30 @@
"empty_hide_archived_notes": "Archivierte Notizen ausblenden"
},
"breadcrumb_badges": {
"read_only_explicit": "Schreibgeschützt",
"read_only_explicit_description": "Diese Notiz wurde händisch schreibgeschützt.\nKlicke hier um sie temporär zu bearbeiten.",
"read_only_auto": "Automatisch schreibgeschützt",
"read_only_auto_description": "Diese Notiz wurde automatisch aus Leistungsgründen als schreibgeschützt markiert. Dieses automatische Limit kann in den Einstellungen angepasst werden.\n\nKlicke hier, um sie temporär zu bearbeiten.",
"read_only_explicit": "Nicht Änderbar",
"read_only_explicit_description": "Diese Notiz wurde händisch als nicht änderbar markiert.\nKlicke hier um sie temporär zu bearbeiten.",
"read_only_auto": "Automatisch nicht änderbar",
"read_only_auto_description": "Diese Notiz wurde automatisch aus Leistungsgründen als nicht änderbar markiert. Dieses automatische Limit kann in den Einstellungen angepasst werden.\n\nKlicke hier, um sie temporär zu bearbeiten.",
"read_only_temporarily_disabled": "Temporär bearbeitbar",
"read_only_temporarily_disabled_description": "Diese Notiz ist aktuell bearbeitbar, ist aber normalerweise schreibgeschützt. Sobald du zu einer anderen Notiz navigierst wird diese wieder schreibgeschützt.\n\nKlicke hier, um die Notiz wieder schreibgeschützt zu machen.",
"shared_publicly": "Öffentlich freigegeben",
"shared_locally": "Lokal freigegeben",
"read_only_temporarily_disabled_description": "Diese Notiz ist aktuell bearbeitbar, ist aber normalerweise nicht änderbar. Sobald du zu einer anderen Notiz navigierst, kehrt diese Notiz in ihren Normalzustand zurück.\n\nKlicke hier, um die Notiz wieder nicht änderbar zu machen.",
"shared_publicly": "Öffentlich geteilt",
"shared_locally": "Lokal geteilt",
"shared_copy_to_clipboard": "Link in die Zwischenablage kopieren",
"shared_open_in_browser": "Link im Browser öffnen",
"shared_unshare": "Freigabe aufheben",
"shared_open_in_browser": "Link öffnen",
"shared_unshare": "Teilen aufheben",
"clipped_note": "Internetschnellverweis",
"clipped_note_description": "Diese Notiz wurde von {{url}} übernommen.\n\nKlicke hier, um zur Quelle zu gehen.",
"clipped_note_description": "Diese Notiz wurde von {{url}} übernommen.\n\nKlicke hier, um zum Ursprung zu gehen.",
"execute_script": "Skript ausführen",
"execute_script_description": "Diese Notiz ist eine Skriptnotiz. Klicke hier, um das Skript auszuführen.",
"execute_sql": "SQL ausführen",
"execute_sql_description": "Diese Notiz ist eine SQL-Notiz. Klicke hier, um die SQL-Abfrage auszuführen.",
"save_status_saved": "Gespeichert",
"save_status_saving": "Speichere...",
"save_status_saving": "Speichern...",
"save_status_unsaved": "Nicht gespeichert",
"save_status_error": "Speichern fehlgeschlagen",
"save_status_saving_tooltip": "Änderungen werden gespeichert.",
"save_status_unsaved_tooltip": "Es gibt ungespeicherte Änderungen, welche gleich automatisch gespeichert werden.",
"save_status_error_tooltip": "Beim speichern der Notiz ist ein Fehler aufgetreten. Wenn möglich, versuche die Notiz woandershin zu kopieren und die Anwendung neu zu laden."
"save_status_error_tooltip": "Beim speichern der Notiz ist ein Fehler aufgetreten. Wenn möglich, versuche die Notiz woandershin zu kopieren und die Applikation neu zu laden."
},
"status_bar": {
"language_title": "Inhaltssprache ändern",
@@ -2242,22 +2237,22 @@
"attachments_other": "{{count}} Anhänge",
"attachments_title_one": "Anhang in einem neuen Tab öffnen",
"attachments_title_other": "Anhänge in einem neuen Tab öffnen",
"attributes_one": "{{count}} Attribut",
"attributes_other": "{{count}} Attribute",
"attributes_title": "Eigene und geerbte Attribute",
"attributes_one": "{{count}} Eigenschaft",
"attributes_other": "{{count}} Eigenschaften",
"attributes_title": "Eigene und gererbte Eigenschaften",
"note_paths_one": "{{count}} Pfad",
"note_paths_other": "{{count}} Pfade",
"note_paths_title": "Notizpfade",
"code_note_switcher": "Sprachmodus ändern"
},
"attributes_panel": {
"title": "Notizattribute"
"title": "Notizeigenschaften"
},
"right_pane": {
"empty_message": "Für diese Notiz gibt es nichts anzuzeigen",
"empty_button": "Leiste ausblenden",
"toggle": "Rechte Leiste umschalten",
"custom_widget_go_to_source": "Zum Quellcode"
"empty_button": "Anzeige ausblenden",
"toggle": "Rechte Anzeige umschalten",
"custom_widget_go_to_source": "Zum Ursprungscode"
},
"pdf": {
"attachments_one": "{{count}} Anhang",
@@ -2267,14 +2262,6 @@
"pages_one": "{{count}} Seite",
"pages_other": "{{count}} Seiten",
"pages_alt": "Seite {{pageNumber}}",
"pages_loading": "Lädt..."
},
"platform_indicator": {
"available_on": "Verfügbar auf {{platform}}"
},
"mobile_tab_switcher": {
"title_one": "{{count}} Tab",
"title_other": "{{count}} Tabs",
"more_options": "Weitere Optionen"
"pages_loading": "Laden..."
}
}

View File

@@ -745,7 +745,7 @@
"button_title": "Export diagram as SVG"
},
"relation_map_buttons": {
"create_child_note_title": "Create child note and add it to map",
"create_child_note_title": "Create new child note and add it into this relation map",
"reset_pan_zoom_title": "Reset pan & zoom to initial coordinates and magnification",
"zoom_in_title": "Zoom In",
"zoom_out_title": "Zoom Out"
@@ -760,8 +760,7 @@
"delete_this_note": "Delete this note",
"note_revisions": "Note revisions",
"error_cannot_get_branch_id": "Cannot get branchId for notePath '{{notePath}}'",
"error_unrecognized_command": "Unrecognized command {{command}}",
"backlinks": "Backlinks"
"error_unrecognized_command": "Unrecognized command {{command}}"
},
"note_icon": {
"change_note_icon": "Change note icon",
@@ -1816,11 +1815,7 @@
"configure_launchbar": "Configure Launchbar"
},
"sql_result": {
"not_executed": "The query has not been executed yet.",
"no_rows": "No rows have been returned for this query",
"failed": "SQL query execution has failed",
"statement_result": "Statement result",
"execute_now": "Execute now"
"no_rows": "No rows have been returned for this query"
},
"sql_table_schemas": {
"tables": "Tables"
@@ -1959,8 +1954,8 @@
"desktop-application": "Desktop Application",
"native-title-bar": "Native title bar",
"native-title-bar-description": "For Windows and macOS, keeping the native title bar off makes the application look more compact. On Linux, keeping the native title bar on integrates better with the rest of the system.",
"background-effects": "Enable background effects",
"background-effects-description": "Adds a blurred, stylish background to app windows, creating depth and a modern look. \"Native title bar\" must be disabled.",
"background-effects": "Enable background effects (Windows 11 only)",
"background-effects-description": "The Mica effect adds a blurred, stylish background to app windows, creating depth and a modern look. \"Native title bar\" must be disabled.",
"restart-app-button": "Restart the application to view the changes",
"zoom-factor": "Zoom factor"
},
@@ -1978,7 +1973,6 @@
},
"geo-map": {
"create-child-note-title": "Create a new child note and add it to the map",
"create-child-note-text": "Add marker",
"create-child-note-instruction": "Click on the map to create a new note at that location or press Escape to dismiss.",
"unable-to-load-map": "Unable to load map."
},
@@ -2154,7 +2148,7 @@
"next_theme_message": "You are currently using the legacy theme, would you like to try the new theme?",
"next_theme_button": "Try the new theme",
"background_effects_title": "Background effects are now stable",
"background_effects_message": "On Windows and macOS devices, background effects are now stable. The background effects adds a touch of color to the user interface by blurring the background behind it.",
"background_effects_message": "On Windows devices, background effects are now fully stable. The background effects adds a touch of color to the user interface by blurring the background behind it. This technique is also used in other applications such as Windows Explorer.",
"background_effects_button": "Enable background effects",
"new_layout_title": "New layout",
"new_layout_message": "Weve introduced a modernized layout for Trilium. The ribbon has been removed and seamlessly integrated into the main interface, with a new status bar and expandable sections (such as promoted attributes) taking over key functions.\n\nThe new layout is enabled by default, and can be temporarily disabled via Options → Appearance.",
@@ -2269,13 +2263,5 @@
"pages_other": "{{count}} pages",
"pages_alt": "Page {{pageNumber}}",
"pages_loading": "Loading..."
},
"platform_indicator": {
"available_on": "Available on {{platform}}"
},
"mobile_tab_switcher": {
"title_one": "{{count}} tab",
"title_other": "{{count}} tabs",
"more_options": "More options"
}
}

View File

@@ -28,10 +28,7 @@
},
"widget-render-error": {
"title": "Hubo un fallo al renderizar un widget personalizado de React"
},
"widget-missing-parent": "El widget personalizado no tiene definida la propiedad obligatoria '{{property}}'.\n\nSi este script está pensado para ejecutarse sin un elemento de interfaz de usuario, utilice '#run=frontendStartup' en su lugar.",
"open-script-note": "Abrir script",
"scripting-error": "Error en script personalizado: {{title}}"
}
},
"add_link": {
"add_link": "Agregar enlace",
@@ -214,8 +211,7 @@
"info": {
"modalTitle": "Mensaje informativo",
"closeButton": "Cerrar",
"okButton": "Aceptar",
"copy_to_clipboard": "Copiar al portapapeles"
"okButton": "Aceptar"
},
"jump_to_note": {
"search_button": "Buscar en texto completo",
@@ -701,13 +697,7 @@
"convert_into_attachment_successful": "La nota '{{title}}' ha sido convertida a un archivo adjunto.",
"convert_into_attachment_prompt": "¿Está seguro que desea convertir la nota '{{title}}' en un archivo adjunto de la nota padre?",
"print_pdf": "Exportar como PDF...",
"open_note_on_server": "Abrir nota en servidor",
"view_revisions": "Revisiones de la nota...",
"advanced": "Avanzado",
"export_as_image": "Exportar como imagen",
"export_as_image_png": "PNG (ráster)",
"export_as_image_svg": "SVG (vectorial)",
"note_map": "Mapa de la nota"
"open_note_on_server": "Abrir nota en servidor"
},
"onclick_button": {
"no_click_handler": "El widget de botón '{{componentId}}' no tiene un controlador de clics definido"
@@ -769,13 +759,7 @@
"reset-default": "Restablecer a icono por defecto",
"search_placeholder_one": "Buscar {{number}} icono a través de {{count}} paquetes",
"search_placeholder_many": "Buscar {{number}} iconos a través de {{count}} paquetes",
"search_placeholder_other": "Buscar {{number}} iconos a través de {{count}} paquetes",
"search_placeholder_filtered": "Buscar {{number}} iconos en {{name}}",
"filter": "Filtro",
"filter-none": "Todos los iconos",
"filter-default": "Iconos predeterminados",
"icon_tooltip": "{{name}}\nPaquete de iconos: {{iconPack}}",
"no_results": "No se encontraron iconos."
"search_placeholder_other": "Buscar {{number}} iconos a través de {{count}} paquetes"
},
"basic_properties": {
"note_type": "Tipo de nota",
@@ -799,11 +783,10 @@
"board": "Tablero",
"include_archived_notes": "Mostrar notas archivadas",
"presentation": "Presentación",
"expand_tooltip": "Expande las subnotas inmediatas de esta colección (un nivel). Para más opciones, pulsa la flecha a la derecha.",
"expand_tooltip": "Expande las notas hijas inmediatas de esta colección (un nivel). Para más opciones, pulsa la flecha a la derecha.",
"expand_first_level": "Expandir hijos inmediatos",
"expand_nth_level": "Expandir {{depth}} niveles",
"expand_all_levels": "Expandir todos los niveles",
"hide_child_notes": "Ocultar subnotas en el árbol"
"expand_all_levels": "Expandir todos los niveles"
},
"edited_notes": {
"no_edited_notes_found": "Aún no hay notas editadas en este día...",
@@ -836,8 +819,7 @@
},
"inherited_attribute_list": {
"title": "Atributos heredados",
"no_inherited_attributes": "Sin atributos heredados.",
"none": "ninguno"
"no_inherited_attributes": "Sin atributos heredados."
},
"note_info_widget": {
"note_id": "ID de nota",
@@ -848,9 +830,7 @@
"note_size_info": "El tamaño de la nota proporciona una estimación aproximada de los requisitos de almacenamiento para esta nota. Toma en cuenta el contenido de la nota y el contenido de sus revisiones de nota.",
"calculate": "calcular",
"subtree_size": "(tamaño del subárbol: {{size}} en {{count}} notas)",
"title": "Información de nota",
"mime": "Tipo MIME",
"show_similar_notes": "Mostrar notas similares"
"title": "Información de nota"
},
"note_map": {
"open_full": "Ampliar al máximo",
@@ -913,8 +893,7 @@
"search_parameters": "Parámetros de búsqueda",
"unknown_search_option": "Opción de búsqueda desconocida {{searchOptionName}}",
"search_note_saved": "La nota de búsqueda se ha guardado en {{- notePathTitle}}",
"actions_executed": "Las acciones han sido ejecutadas.",
"view_options": "Ver opciones:"
"actions_executed": "Las acciones han sido ejecutadas."
},
"similar_notes": {
"title": "Notas similares",
@@ -1017,13 +996,7 @@
},
"editable_text": {
"placeholder": "Escribe aquí el contenido de tu nota...",
"auto-detect-language": "Detectado automáticamente",
"editor_crashed_title": "El editor de texto ha dejado de responder",
"editor_crashed_content": "Su contenido ha sido recuperado con éxito, pero puede que algunos de sus cambios más recientes no se hayan guardado.",
"editor_crashed_details_button": "Ver más detalles...",
"editor_crashed_details_intro": "Si experimenta este error varias veces, considere informarlo en GitHub adjuntando la siguiente información.",
"editor_crashed_details_title": "Información técnica",
"keeps-crashing": "El componente de edición sigue fallando. Por favor, intente reiniciar Trilium. Si el problema persiste, considere crear un informe de fallos."
"auto-detect-language": "Detectado automáticamente"
},
"empty": {
"open_note_instruction": "Abra una nota escribiendo el título de la nota en la entrada a continuación o elija una nota en el árbol.",
@@ -1339,8 +1312,8 @@
"code_mime_types": {
"title": "Tipos MIME disponibles en el menú desplegable",
"tooltip_syntax_highlighting": "Resaltado de sintaxis",
"tooltip_code_block_syntax": "Bloques de código en Notas de texto",
"tooltip_code_note_syntax": "Notas de código"
"tooltip_code_block_syntax": "Bloques de Código en notas de Texto",
"tooltip_code_note_syntax": "Notas de Código"
},
"vim_key_bindings": {
"use_vim_keybindings_in_code_notes": "Combinaciones de teclas Vim",
@@ -1417,16 +1390,16 @@
"markdown": "Estilo Markdown"
},
"highlights_list": {
"title": "Lista de puntos destacados",
"description": "Puede personalizar la lista de puntos destacados que se muestra en el panel derecho:",
"title": "Lista de aspectos destacados",
"description": "Puede personalizar la lista de aspectos destacados que se muestra en el panel derecho:",
"bold": "Texto en negrita",
"italic": "Texto en cursiva",
"underline": "Texto subrayado",
"color": "Texto con color",
"bg_color": "Texto con color de fondo",
"visibility_title": "Visibilidad de la lista de puntos destacados",
"visibility_description": "Puede ocultar el widget de puntos destacados por nota agregando la etiqueta #hideHighlightWidget.",
"shortcut_info": "Puede configurar un método abreviado de teclado para alternar rápidamente el panel derecho (incluidos los puntos destacados) en Opciones -> Atajos (nombre 'toggleRightPane')."
"visibility_title": "Visibilidad de la lista de aspectos destacados",
"visibility_description": "Puede ocultar el widget de aspectos destacados por nota agregando una etiqueta #hideHighlightWidget.",
"shortcut_info": "Puede configurar un método abreviado de teclado para alternar rápidamente el panel derecho (incluidos los aspectos destacados) en Opciones -> Atajos (nombre 'toggleRightPane')."
},
"table_of_contents": {
"title": "Tabla de contenido",
@@ -1614,7 +1587,7 @@
},
"bookmark_switch": {
"bookmark": "Marcador",
"bookmark_this_note": "Agregar esta nota a marcadores en el panel lateral izquierdo",
"bookmark_this_note": "Añadir esta nota a marcadores en el panel lateral izquierdo",
"remove_bookmark": "Eliminar marcador"
},
"editability_select": {
@@ -1662,10 +1635,7 @@
"convert-to-attachment-confirm": "¿Está seguro que desea convertir las notas seleccionadas en archivos adjuntos de sus notas padres? Esta operación solo aplica a notas de Imagen, otras notas serán omitidas.",
"open-in-popup": "Edición rápida",
"archive": "Archivar",
"unarchive": "Desarchivar",
"open-in-a-new-window": "Abrir en una nueva ventana",
"hide-subtree": "Ocultar subárbol",
"show-subtree": "Mostrar subárbol"
"unarchive": "Desarchivar"
},
"shared_info": {
"shared_publicly": "Esta nota está compartida públicamente en {{- link}}.",
@@ -1726,13 +1696,7 @@
},
"highlights_list_2": {
"title": "Lista de destacados",
"options": "Opciones",
"title_with_count_one": "{{count}} punto destacado",
"title_with_count_many": "{{count}} puntos destacados",
"title_with_count_other": "{{count}} puntos destacados",
"modal_title": "Configurar la lista de puntos destacados",
"menu_configure": "Configurar la lista de puntos destacados...",
"no_highlights": "Ningún punto destacado encontrado."
"options": "Opciones"
},
"quick-search": {
"placeholder": "Búsqueda rápida",
@@ -1755,18 +1719,7 @@
"refresh-saved-search-results": "Refrescar resultados de búsqueda guardados",
"create-child-note": "Crear subnota",
"unhoist": "Desanclar",
"toggle-sidebar": "Alternar barra lateral",
"dropping-not-allowed": "No está permitido soltar notas en esta ubicación.",
"clone-indicator-tooltip": "Esta nota tiene {{- count}} padres: {{- parents}}",
"clone-indicator-tooltip-single": "Esta nota está clonada (1 padre adicional: {{- parent}})",
"shared-indicator-tooltip": "Esta nota está compartida públicamente",
"shared-indicator-tooltip-with-url": "Esta nota está compartida públicamente en: {{- url}}",
"subtree-hidden-tooltip_one": "{{count}} subnota que está oculta del árbol",
"subtree-hidden-tooltip_many": "{{count}} subnotas que están ocultas del árbol",
"subtree-hidden-tooltip_other": "{{count}} subnotas que están ocultas del árbol",
"subtree-hidden-moved-title": "Agregado a {{title}}",
"subtree-hidden-moved-description-collection": "Esta colección oculta sus subnotas en el árbol.",
"subtree-hidden-moved-description-other": "Las subnotas están ocultas en el árbol para esta nota."
"toggle-sidebar": "Alternar barra lateral"
},
"title_bar_buttons": {
"window-on-top": "Mantener esta ventana en la parte superior"
@@ -1777,21 +1730,10 @@
"printing_pdf": "Exportando a PDF en curso..",
"print_report_collection_content_one": "{{count}} nota en la colección no se puede imprimir porque no son compatibles o está protegida.",
"print_report_collection_content_many": "{{count}} notas en la colección no se pueden imprimir porque no son compatibles o están protegidas.",
"print_report_collection_content_other": "{{count}} notas en la colección no se pueden imprimir porque no son compatibles o están protegidas.",
"print_report_title": "Imprimir informe",
"print_report_collection_details_button": "Ver detalles",
"print_report_collection_details_ignored_notes": "Notas ignoradas"
"print_report_collection_content_other": "{{count}} notas en la colección no se pueden imprimir porque no son compatibles o están protegidas."
},
"note_title": {
"placeholder": "escriba el título de la nota aquí...",
"created_on": "Creado en <Value />",
"last_modified": "Modificado en <Value />",
"note_type_switcher_label": "Cambiar de {{type}} a:",
"note_type_switcher_others": "Otro tipo de nota",
"note_type_switcher_templates": "Plantilla",
"note_type_switcher_collection": "Colección",
"edited_notes": "Notas editadas en este día",
"promoted_attributes": "Atributos promovidos"
"placeholder": "escriba el título de la nota aquí..."
},
"search_result": {
"no_notes_found": "No se han encontrado notas para los parámetros de búsqueda dados.",
@@ -1801,11 +1743,7 @@
"configure_launchbar": "Configurar barra de lanzamiento"
},
"sql_result": {
"no_rows": "No se han devuelto filas para esta consulta",
"not_executed": "La consulta aún no ha sido ejecutada.",
"failed": "La ejecución de la consulta SQL ha fallado",
"statement_result": "Resultado de declaración",
"execute_now": "Ejecutar ahora"
"no_rows": "No se han devuelto filas para esta consulta"
},
"sql_table_schemas": {
"tables": "Tablas"
@@ -1824,8 +1762,7 @@
},
"toc": {
"table_of_contents": "Tabla de contenido",
"options": "Opciones",
"no_headings": "Sin encabezados."
"options": "Opciones"
},
"watched_file_update_status": {
"file_last_modified": "Archivo <code class=\"file-path\"></code> ha sido modificado por última vez en<span class=\"file-last-modified\"></span>.",
@@ -1937,15 +1874,14 @@
"open_note_in_new_tab": "Abrir nota en una pestaña nueva",
"open_note_in_new_split": "Abrir nota en una nueva división",
"open_note_in_new_window": "Abrir nota en una nueva ventana",
"open_note_in_popup": "Edición rápida",
"open_note_in_other_split": "Abrir nota en la otra división"
"open_note_in_popup": "Edición rápida"
},
"electron_integration": {
"desktop-application": "Aplicación de escritorio",
"native-title-bar": "Barra de título nativa",
"native-title-bar-description": "Para Windows y macOS, quitar la barra de título nativa hace que la aplicación se vea más compacta. En Linux, mantener la barra de título nativa hace que se integre mejor con el resto del sistema.",
"background-effects": "Habilitar efectos de fondo",
"background-effects-description": "Agrega un fondo borroso y elegante a las ventanas de la aplicación, creando profundidad y un aspecto moderno. \"Título nativo de la barra\" debe deshabilitarse.",
"background-effects": "Habilitar efectos de fondo (sólo en Windows 11)",
"background-effects-description": "El efecto Mica agrega un fondo borroso y elegante a las ventanas de la aplicación, creando profundidad y un aspecto moderno. \"Título nativo de la barra\" debe deshabilitarse.",
"restart-app-button": "Reiniciar la aplicación para ver los cambios",
"zoom-factor": "Factor de zoom"
},
@@ -1964,8 +1900,7 @@
"geo-map": {
"create-child-note-title": "Crear una nueva subnota y agregarla al mapa",
"create-child-note-instruction": "Dé clic en el mapa para crear una nueva nota en esa ubicación o presione Escape para cancelar.",
"unable-to-load-map": "No se puede cargar el mapa.",
"create-child-note-text": "Agregar marcador"
"unable-to-load-map": "No se puede cargar el mapa."
},
"geo-map-context": {
"open-location": "Abrir ubicación",
@@ -2008,11 +1943,10 @@
},
"note_language": {
"not_set": "Idioma no establecido",
"configure-languages": "Configurar idiomas...",
"help-on-languages": "Ayuda en idiomas de contenido..."
"configure-languages": "Configurar idiomas..."
},
"content_language": {
"title": "Idiomas de contenido",
"title": "Contenido de idiomas",
"description": "Seleccione uno o más idiomas que deben aparecer en la selección del idioma en la sección Propiedades Básicas de una nota de texto de solo lectura o editable. Esto permitirá características tales como corrección de ortografía o soporte de derecha a izquierda."
},
"switch_layout_button": {
@@ -2027,8 +1961,7 @@
"button_title": "Exportar diagrama como PNG"
},
"svg": {
"export_to_png": "El diagrama no pudo ser exportado a PNG.",
"export_to_svg": "El diagrama no pudo ser exportado a SVG."
"export_to_png": "El diagrama no pudo ser exportado a PNG."
},
"code_theme": {
"title": "Apariencia",
@@ -2131,12 +2064,9 @@
"next_theme_message": "Estás usando actualmente el tema heredado. ¿Te gustaría probar el nuevo tema?",
"next_theme_button": "Prueba el nuevo tema",
"background_effects_title": "Los efectos de fondo son ahora estables",
"background_effects_message": "En los dispositivos Windows y macOS, los efectos de fondo ya son estables. Los efectos de fondo añaden un toque de color a la interfaz de usuario difuminando el fondo que hay detrás.",
"background_effects_message": "En los dispositivos Windows, los efectos de fondo ya son totalmente estables. Los efectos de fondo añaden un toque de color a la interfaz de usuario difuminando el fondo que hay detrás. Esta técnica también se utiliza en otras aplicaciones como el Explorador de Windows.",
"background_effects_button": "Activar efectos de fondo",
"dismiss": "Desestimar",
"new_layout_title": "Nuevo diseño",
"new_layout_message": "Hemos introducido un diseño modernizado para Trilium. La cinta se ha eliminado y se ha integrado perfectamente en la interfaz principal, con una nueva barra de estado y secciones ampliables (como los atributos promovidos) que tienen funciones clave.\n\nEl nuevo diseño está habilitado por defecto, y puede ser deshabilitado temporalmente a través de Opciones → Apariencia.",
"new_layout_button": "Más información"
"dismiss": "Desestimar"
},
"ui-performance": {
"title": "Rendimiento",
@@ -2151,10 +2081,7 @@
},
"settings_appearance": {
"related_code_blocks": "Esquema de colores para bloques de código en notas de texto",
"related_code_notes": "Esquema de colores para notas de código",
"ui": "Interfaz de usuario",
"ui_old_layout": "Antiguo diseño",
"ui_new_layout": "Nuevo diseño"
"related_code_notes": "Esquema de colores para notas de código"
},
"units": {
"percentage": "%"
@@ -2202,12 +2129,7 @@
"attributes_other": "{{count}} atributos",
"note_paths_one": "{{count}} ruta",
"note_paths_many": "{{count}} rutas",
"note_paths_other": "{{count}} rutas",
"language_title": "Cambiar el idioma del contenido",
"note_info_title": "Ver información de la nota (p. e., fechas, tamaño de la nota)",
"attributes_title": "Atributos propios y atributos heredados",
"note_paths_title": "Rutas de nota",
"code_note_switcher": "Cambiar modo de idioma"
"note_paths_other": "{{count}} rutas"
},
"pdf": {
"attachments_one": "{{count}} adjunto",
@@ -2218,78 +2140,6 @@
"layers_other": "{{count}} capas",
"pages_one": "{{count}} página",
"pages_many": "{{count}} páginas",
"pages_other": "{{count}} páginas",
"pages_alt": "Página {{pageNumber}}",
"pages_loading": "Cargando..."
},
"experimental_features": {
"title": "Opciones experimentales",
"disclaimer": "Estas opciones son experimentales y pueden causar inestabilidad. Úselas con precaución.",
"new_layout_name": "Nuevo diseño",
"new_layout_description": "Pruebe el nuevo diseño para tener un aspecto más moderno y usabilidad mejorada. Sujeto a grandes cambios en las próximas versiones."
},
"popup-editor": {
"maximize": "Cambiar a editor completo"
},
"server": {
"unknown_http_error_title": "Error de comunicación con el servidor",
"unknown_http_error_content": "Código de estado: {{statusCode}}\nURL: {{method}} {{url}}\nMensaje: {{message}}",
"traefik_blocks_requests": "Si está usando el proxy inverso Traefik, este introdujo un cambio que afecta la comunicación con el servidor."
},
"tab_history_navigation_buttons": {
"go-back": "Volver a la nota anterior",
"go-forward": "Avanzar a la siguiente nota"
},
"breadcrumb": {
"hoisted_badge": "Anclada",
"hoisted_badge_title": "Desanclar",
"workspace_badge": "Espacio de trabajo",
"scroll_to_top_title": "Saltar al inicio de la nota",
"create_new_note": "Crear nueva subnota",
"empty_hide_archived_notes": "Ocultar notas archivadas"
},
"breadcrumb_badges": {
"read_only_explicit": "Sólo lectura",
"read_only_explicit_description": "Esta nota se ha fijado manualmente como sólo lectura.\nHaga clic para editarla temporalmente.",
"read_only_auto": "Sólo lectura automática",
"read_only_auto_description": "Esta nota se fijó automáticamente con el modo de sólo lectura por razones de rendimiento. Este límite automático es ajustable desde los ajustes.\n\nHaga clic para editarla temporalmente.",
"read_only_temporarily_disabled": "Temporalmente editable",
"read_only_temporarily_disabled_description": "Esta nota actualmente es editable, pero normalmente es de sólo lectura. La nota volverá a ser de sólo lectura tan pronto como navegue a otra nota.\n\nHaga clic para volver a habilitar el modo de sólo lectura.",
"shared_publicly": "Compartida públicamente",
"shared_locally": "Compartida localmente",
"shared_copy_to_clipboard": "Copiar enlace al portapapeles",
"shared_open_in_browser": "Abrir enlace en el navegador",
"shared_unshare": "Eliminar compartido",
"clipped_note_description": "Esta nota fue tomada originalmente de {{url}}.\n\nHaga clic para navegar a la página web de origen.",
"execute_script": "Ejecutar script",
"execute_script_description": "Esta nota es una nota de script. Haga clic para ejecutar el script.",
"execute_sql": "Ejecutar SQL",
"execute_sql_description": "Esta nota es una nota SQL. Haga clic para ejecutar la consulta SQL.",
"save_status_saved": "Guardado",
"save_status_saving": "Guardando...",
"save_status_unsaved": "Sin guardar",
"save_status_error": "Fallo al guardar",
"save_status_saving_tooltip": "Los cambios están siendo guardados.",
"save_status_unsaved_tooltip": "Hay cambios sin guardar. Se guardarán automáticamente en un momento.",
"save_status_error_tooltip": "Se produjo un error al guardar la nota. Si es posible, trate de copiar el contenido de la nota en otro lugar y recargar la aplicación.",
"clipped_note": "Clip web"
},
"attributes_panel": {
"title": "Atributos de nota"
},
"right_pane": {
"empty_message": "Nada que mostrar para esta nota",
"empty_button": "Ocultar el panel",
"toggle": "Alternar panel derecho",
"custom_widget_go_to_source": "Ir al código fuente"
},
"platform_indicator": {
"available_on": "Disponible en {{platform}}"
},
"mobile_tab_switcher": {
"title_one": "{{count}} pestaña",
"title_many": "{{count}} pestañas",
"title_other": "{{count}} pestañas",
"more_options": "Más opciones"
"pages_other": "{{count}} páginas"
}
}

View File

@@ -1,5 +0,0 @@
{
"global_menu": {
"about": "Maidir le Trilium Notes"
}
}

View File

@@ -11,8 +11,8 @@
},
"toast": {
"critical-error": {
"title": "Eror kritikal",
"message": "Telah terjadi eror kritikal yang mencegah aplikasi klien untuk memulai:\n\n{{message}}\n\nHal ini kemungkinan besar disebabkan oleh skrip yang gagal secara tidak terduga. Coba jalankan aplikasi dalam mode aman dan atasi masalahnya."
"title": "Kesalahan kritis",
"message": "Telah terjadi kesalahan kritis yang mencegah aplikasi klien untuk memulai:\n\n{{message}}\n\nHal ini kemungkinan besar disebabkan oleh skrip yang gagal secara tidak terduga. Coba jalankan aplikasi dalam mode aman dan atasi masalahnya."
},
"widget-error": {
"title": "Gagal menginisialisasi widget",
@@ -21,65 +21,12 @@
},
"bundle-error": {
"title": "Gagal memuat skrip kustom",
"message": "Skrip tidak dapat dijalankan:\n\n{{message}}"
},
"widget-list-error": {
"title": "Gagal mendapatkan daftar widget dari server"
},
"open-script-note": "Buka skrip catatan",
"widget-render-error": {
"title": "Gagal render widget React custom"
},
"widget-missing-parent": "Widget custom '{{property}}' tidak terdefinisi.\n\nJika skrip ini bermaksud untuk bisa dijalankan tanpa elemen UI, gunakanlah '#run=frontendStartup'.",
"scripting-error": "Skrip custom eror : {{title}}"
"message": "Skrip dari catatan dengan ID \"{{id}}\", berjudul \"{{title}}\" tidak dapat dijalankan karena:\n\n{{message}}"
}
},
"add_link": {
"add_link": "Tambah tautan",
"help_on_links": "Bantuan pada tautan",
"note": "Catatan",
"search_note": "cari catatan berdasarkan nama",
"link_title_mirrors": "judul tautan mencerminkan judul catatan saat ini",
"link_title_arbitrary": "judul tautan dapat diubah secara bebas",
"link_title": "Judul tautan",
"button_add_link": "Tambah tautan"
},
"branch_prefix": {
"edit_branch_prefix_multiple": "Edit prefiks cabang untuk {{count}} cabang",
"help_on_tree_prefix": "Bantuan pada prefiks pohon catatan",
"prefix": "Prefiks: ",
"save": "Simpan",
"branch_prefix_saved": "Prefiks cabang telah disimpan.",
"branch_prefix_saved_multiple": "Prefix cabang telah disimpan pada {{count}} cabang.",
"affected_branches": "Cabang terdampak ({{count}}):"
},
"bulk_actions": {
"bulk_actions": "Aksi borongan",
"affected_notes": "Catatan terdampak",
"include_descendants": "Sertakan anakan dari catatan yang dipilih",
"available_actions": "Pilihan aksi",
"chosen_actions": "Aksi terpilih",
"execute_bulk_actions": "Eksekusi aksi borongan",
"bulk_actions_executed": "Aksi borongan telah di eksekusi dengan sukses.",
"none_yet": "Belum ada... tambahkan aksi dengan memilih salah satu dari aksi di atas.",
"labels": "Label-label"
},
"confirm": {
"cancel": "Batal",
"ok": "Oke",
"are_you_sure_remove_note": "Apakah anda yakin mau membuang catatan \"{{title}}\" dari peta relasi? ",
"if_you_dont_check": "Jika Anda tidak mencentang ini, catatan hanya akan dihapus dari peta relasi.",
"also_delete_note": "Hapus juga catatannya"
},
"delete_notes": {
"delete_notes_preview": "Hapus pratinjau catatan",
"close": "Tutup",
"delete_all_clones_description": "Hapus seluruh duplikat (bisa dikembalikan di menu revisi)",
"erase_notes_description": "Penghapusan normal hanya menandai catatan sebagai dihapus dan dapat dipulihkan (melalui dialog versi revisi) dalam jangka waktu tertentu. Mencentang opsi ini akan menghapus catatan secara permanen seketika dan catatan tidak akan bisa dipulihkan kembali.",
"erase_notes_warning": "Hapus catatan secara permanen (tidak bisa dikembalikan), termasuk semua duplikat. Aksi akan memaksa aplikasi untuk mengulang kembali.",
"notes_to_be_deleted": "Catatan-catatan berikut akan dihapuskan ({{notesCount}})",
"no_note_to_delete": "Tidak ada Catatan yang akan dihapus (hanya duplikat)."
},
"clone_to": {
"clone_notes_to": "Duplikat catatan ke…"
"note": "Catatan"
}
}

View File

@@ -325,10 +325,7 @@
"apply-bulk-actions": "Applica azioni in blocco",
"converted-to-attachments": "{{count}} note sono state convertite in allegati.",
"convert-to-attachment-confirm": "Sei sicuro di voler convertire le note selezionate in allegati delle note principali? Questa operazione si applica solo alle note immagine, le altre note verranno ignorate.",
"open-in-popup": "Modifica rapida",
"open-in-a-new-window": "Apri in una nuova finestra",
"hide-subtree": "Nascondi sottostruttura",
"show-subtree": "Mostra sottoalbero"
"open-in-popup": "Modifica rapida"
},
"electron_context_menu": {
"cut": "Taglia",
@@ -1381,8 +1378,7 @@
"expand_tooltip": "Espande i figli diretti di questa raccolta (a un livello di profondità). Per ulteriori opzioni, premere la freccia a destra.",
"expand_first_level": "Espandi figli diretti",
"expand_nth_level": "Espandi {{depth}} livelli",
"expand_all_levels": "Espandi tutti i livelli",
"hide_child_notes": "Nascondi note secondarie nell'albero"
"expand_all_levels": "Espandi tutti i livelli"
},
"edited_notes": {
"no_edited_notes_found": "Nessuna nota modificata per questo giorno...",
@@ -1903,13 +1899,7 @@
"clone-indicator-tooltip": "Questa nota ha {{- count}} genitori: {{- parents}}",
"clone-indicator-tooltip-single": "Questa nota è stata clonata (1 genitore aggiuntivo: {{- parent}})",
"shared-indicator-tooltip": "Questa nota è condivisa pubblicamente",
"shared-indicator-tooltip-with-url": "Questa nota è condivisa pubblicamente all'indirizzo: {{- url}}",
"subtree-hidden-tooltip_one": "{{count}} nota secondaria nascosta dall'albero",
"subtree-hidden-tooltip_many": "{{count}} note secondarie nascoste dall'albero",
"subtree-hidden-tooltip_other": "{{count}} note secondarie nascoste dall'albero",
"subtree-hidden-moved-title": "Aggiunto a {{title}}",
"subtree-hidden-moved-description-collection": "Questa raccolta nasconde le sue note secondarie nell'albero.",
"subtree-hidden-moved-description-other": "Le note secondarie sono nascoste nell'albero di questa nota."
"shared-indicator-tooltip-with-url": "Questa nota è condivisa pubblicamente all'indirizzo: {{- url}}"
},
"title_bar_buttons": {
"window-on-top": "Mantieni la finestra in primo piano"
@@ -1944,11 +1934,7 @@
"configure_launchbar": "Configura Launchbar"
},
"sql_result": {
"no_rows": "Nessuna riga è stata restituita per questa query",
"not_executed": "La query non è stata ancora eseguita.",
"failed": "Esecuzione query SQL non riuscita",
"statement_result": "Risultato della dichiarazione",
"execute_now": "Esegui ora"
"no_rows": "Nessuna riga è stata restituita per questa query"
},
"watched_file_update_status": {
"file_last_modified": "Il file <code class=\"file-path\"></code> è stato modificato l'ultima volta il <span class=\"file-last-modified\"></span>.",

View File

@@ -1288,11 +1288,7 @@
"search_not_executed": "検索はまだ実行されていません。上の「検索」ボタンをクリックすると、検索結果が表示されます。"
},
"sql_result": {
"no_rows": "このクエリでは行が返されませんでした",
"not_executed": "クエリはまだ実行されていません。",
"failed": "SQLクエリの実行に失敗しました",
"statement_result": "ステートメント結果",
"execute_now": "今すぐ実行"
"no_rows": "このクエリでは行が返されませんでした"
},
"sql_table_schemas": {
"tables": "テーブル"
@@ -1757,8 +1753,8 @@
"desktop-application": "デスクトップアプリケーション",
"native-title-bar": "ネイティブタイトルバー",
"native-title-bar-description": "WindowsとmacOSでは、ネイティブタイトルバーをオフにしておくと、アプリケーションがよりコンパクトに見えます。Linuxでは、ネイティブタイトルバーを表示したままの方が、他のシステムとの統一性が高まります。",
"background-effects": "背景効果を有効化",
"background-effects-description": "アプリウィンドウにぼかしの効いたスタイリッシュな背景を追加し、奥行きとモダンな外観を演出します。「ネイティブタイトルバー」を無効にする必要があります。",
"background-effects": "背景効果を有効化Windows 11のみ",
"background-effects-description": "Mica効果は、アプリウィンドウにぼかされたスタイリッシュな背景を追加し、奥行きとモダンな外観を演出します。「ネイティブタイトルバー」を無効にする必要があります。",
"restart-app-button": "アプリケーションを再起動して変更を反映",
"zoom-factor": "ズーム倍率"
},
@@ -2008,8 +2004,7 @@
"geo-map": {
"create-child-note-title": "新しい子ノートを作成し、マップに追加する",
"create-child-note-instruction": "地図をクリックしてその場所に新しいートを作成するか、Esc キーを押して閉じます。",
"unable-to-load-map": "マップを読み込めません。",
"create-child-note-text": "マーカーを追加"
"unable-to-load-map": "マップを読み込めません。"
},
"geo-map-context": {
"open-location": "現在位置を表示",
@@ -2045,7 +2040,7 @@
"next_theme_message": "現在、レガシーテーマを使用しています。新しいテーマを試してみませんか?",
"next_theme_button": "新しいテーマを試す",
"background_effects_title": "背景効果が安定しました",
"background_effects_message": "WindowsおよびmacOSデバイスで、背景効果が安定しました。背景効果は、背景をぼかすことでユーザーインターフェースに彩りを添えます。",
"background_effects_message": "Windowsデバイスで、背景効果が完全に安定しました。背景効果は、背景をぼかすことでユーザーインターフェースに彩りを添えます。この技術は、Windowsエクスプローラーなどの他のアプリケーションでも使用されています。",
"background_effects_button": "背景効果を有効にする",
"dismiss": "却下",
"new_layout_title": "新しいレイアウト",
@@ -2254,12 +2249,5 @@
"pages_other": "{{count}} ページ",
"pages_alt": "ページ {{pageNumber}}",
"pages_loading": "読み込み中..."
},
"platform_indicator": {
"available_on": "{{platform}} で利用可能"
},
"mobile_tab_switcher": {
"title_other": "{{count}} タブ",
"more_options": "その他のオプション"
}
}

View File

@@ -68,7 +68,7 @@
"attachment_detail_2": {
"deletion_reason": ", deoarece nu există o legătură către atașament în conținutul notiței. Pentru a preveni ștergerea, trebuie adăugată înapoi o legătură către atașament în conținut sau atașamentul trebuie convertit în notiță.",
"link_copied": "O legătură către atașament a fost copiată în clipboard.",
"role_and_size": "Rol: {{role}}, dimensiune: {{size}}, MIME: {{- mimeType}}",
"role_and_size": "Rol: {{role}}, dimensiune: {{size}}",
"unrecognized_role": "Rol atașament necunoscut: „{{role}}”.",
"will_be_deleted_in": "Acest atașament va fi șters automat în {{time}}",
"will_be_deleted_soon": "Acest atașament va fi șters automat în curând"
@@ -293,8 +293,7 @@
"expand_tooltip": "Expandează subnotițele directe ale acestei colecții (un singur nivel de adâncime). Pentru mai multe opțiuni, apăsați săgeata din dreapta.",
"expand_first_level": "Expandează subnotițele directe",
"expand_nth_level": "Expandează pe {{depth}} nivele",
"expand_all_levels": "Expandează pe toate nivelele",
"hide_child_notes": "Ascunde subnotițele din arbore"
"expand_all_levels": "Expandează pe toate nivelele"
},
"bookmark_switch": {
"bookmark": "Semn de carte",
@@ -570,7 +569,7 @@
"file_size": "Dimensiunea fișierului",
"file_type": "Tipul fișierului",
"note_id": "ID-ul notiței",
"open": "Deschide în exterior",
"open": "Deschide",
"original_file_name": "Denumirea originală a fișierului",
"title": "Fișier",
"upload_failed": "Încărcarea a unei noi revizii ale fișierului a eșuat.",
@@ -796,8 +795,7 @@
},
"inherited_attribute_list": {
"no_inherited_attributes": "Niciun atribut moștenit.",
"title": "Atribute moștenite",
"none": "niciunul"
"title": "Atribute moștenite"
},
"jump_to_note": {
"search_button": "Caută în întregul conținut",
@@ -882,11 +880,7 @@
"convert_into_attachment_prompt": "Doriți convertirea notiței „{{title}}” într-un atașament al notiței părinte?",
"print_pdf": "Exportare ca PDF...",
"open_note_on_server": "Deschide notița pe server",
"view_revisions": "Revizii ale notițelor...",
"export_as_image": "Exportează ca imagine",
"export_as_image_png": "PNG (bitmap)",
"export_as_image_svg": "SVG (vectorial)",
"note_map": "Harta notițelor"
"view_revisions": "Revizii ale notițelor..."
},
"note_erasure_timeout": {
"deleted_notes_erased": "Notițele șterse au fost eliminate permanent.",
@@ -905,9 +899,7 @@
"note_size_info": "Dimensiunea notiței reprezintă o aproximare a cerințelor de stocare ale acestei notițe. Ia în considerare conținutul notiței dar și ale reviziilor sale.",
"subtree_size": "(dimensiunea sub-arborelui: {{size}} în {{count}} notițe)",
"title": "Informații despre notiță",
"type": "Tip",
"mime": "Tip MIME",
"show_similar_notes": "Afișează notițe similare"
"type": "Tip"
},
"note_launcher": {
"this_launcher_doesnt_define_target_note": "Acesată scurtătură nu definește o notiță-destinație."
@@ -1165,8 +1157,7 @@
"search_parameters": "Parametrii de căutare",
"search_script": "script de căutare",
"search_string": "șir de căutat",
"unknown_search_option": "Opțiune de căutare necunoscută „{{searchOptionName}}”",
"view_options": "Opțiuni de afișare:"
"unknown_search_option": "Opțiune de căutare necunoscută „{{searchOptionName}}”"
},
"search_engine": {
"baidu": "Baidu",
@@ -1318,17 +1309,8 @@
},
"bundle-error": {
"title": "Eroare la încărcarea unui script personalizat",
"message": "Scriptul nu a putut fi executat din cauza:\n\n{{message}}"
},
"widget-list-error": {
"title": "Nu s-a putut obține lista de widget-uri de la server"
},
"widget-render-error": {
"title": "Nu s-a putut randa un widget React"
},
"widget-missing-parent": "Widget-ul personalizat nu are definită proprietatea necesară „{{property}}“.\n\nDacă acest script este menit să ruleze fără interfață grafică, folosiți '#run=frontendStartup'.",
"open-script-note": "Deschide notița scriptului",
"scripting-error": "Eroare script personalizat: {{title}}"
"message": "Scriptul din notița cu ID-ul „{{id}}”, întitulată „{{title}}” nu a putut fi executată din cauza:\n\n{{message}}"
}
},
"tray": {
"enable_tray": "Activează system tray-ul (este necesară repornirea aplicației pentru a avea efect)",
@@ -1435,10 +1417,7 @@
"convert-to-attachment-confirm": "Doriți convertirea notițelor selectate în atașamente ale notiței părinte? Această operațiune se aplică doar notițelor de tip imagine, celelalte vor fi ignorate.",
"open-in-popup": "Editare rapidă",
"archive": "Arhivează",
"unarchive": "Dezarhivează",
"open-in-a-new-window": "Deschide în fereastră nouă",
"hide-subtree": "Ascunde subnotițele",
"show-subtree": "Afișează subnotițele"
"unarchive": "Dezarhivează"
},
"shared_info": {
"help_link": "Pentru informații vizitați <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki-ul</a>.",
@@ -1499,27 +1478,12 @@
},
"highlights_list_2": {
"options": "Setări",
"title": "Listă de evidențieri",
"title_with_count_one": "{{count}} evidențiere",
"title_with_count_few": "{{count}} evidențieri",
"title_with_count_other": "{{count}} de evidențieri",
"modal_title": "Configurează lista de evidențieri",
"menu_configure": "Configurează lista de evidențieri...",
"no_highlights": "Nu există nicio evidențiere."
"title": "Listă de evidențieri"
},
"note_icon": {
"change_note_icon": "Schimbă iconița notiței",
"reset-default": "Resetează la iconița implicită",
"search": "Căutare:",
"search_placeholder_one": "Caută printre {{number}} iconițe dintr-un pachet",
"search_placeholder_few": "Caută printre {{number}} iconițe din {{count}} pachete",
"search_placeholder_other": "Caută printre {{number}} iconițe din {{count}} de pachete",
"search_placeholder_filtered": "Căutați printre {{number}} iconițe în {{name}}",
"filter": "Filtrează",
"filter-none": "Toate iconițele",
"filter-default": "Iconițele implicite",
"icon_tooltip": "{{name}}\nPachet iconițe: {{iconPack}}",
"no_results": "Nu s-a găsit nicio iconiță."
"search": "Căutare:"
},
"show_highlights_list_widget_button": {
"show_highlights_list": "Afișează lista de evidențieri"
@@ -1557,17 +1521,7 @@
"refresh-saved-search-results": "Reîmprospătează căutarea salvată",
"unhoist": "Defocalizează notița",
"toggle-sidebar": "Comută bara laterală",
"dropping-not-allowed": "Aici nu este permisă plasarea notițelor.",
"clone-indicator-tooltip": "Această notiță are {{- count}} părinți: {{- parents}}",
"clone-indicator-tooltip-single": "Această notiță este clonată (un singur părinte: {{- parent}})",
"shared-indicator-tooltip": "Această notiță este partajată public",
"shared-indicator-tooltip-with-url": "Această notiță este partajată public la: {{- url}}",
"subtree-hidden-tooltip_one": "{{count}} subnotiță ascunsă din arbore",
"subtree-hidden-tooltip_few": "{{count}} subnotițe ascunse din arbore",
"subtree-hidden-tooltip_other": "{{count}} de subnotițe ascunse din arbore",
"subtree-hidden-moved-title": "Adăugat în {{title}}",
"subtree-hidden-moved-description-collection": "Subnotițele din această colecție sunt ascunse din arbore.",
"subtree-hidden-moved-description-other": "Subnotițele din această notiță sunt ascunse."
"dropping-not-allowed": "Aici nu este permisă plasarea notițelor."
},
"title_bar_buttons": {
"window-on-top": "Menține fereastra mereu vizibilă"
@@ -1575,24 +1529,12 @@
"note_detail": {
"could_not_find_typewidget": "Nu s-a putut găsi widget-ul corespunzător tipului „{{type}}”",
"printing": "Imprimare în curs...",
"printing_pdf": "Exportare ca PDF în curs...",
"print_report_title": "Raport de imprimare",
"print_report_collection_content_one": "{{count}} notiță din colecție nu a putut fi imprimată deoarece nu este suportată sau este protejată.",
"print_report_collection_content_few": "{{count}} notițe din colecție nu au putut fi imprimate deoarece nu sunt suportate sau sunt protejate.",
"print_report_collection_content_other": "{{count}} de notițe din colecție nu au putut fi imprimate deoarece nu sunt suportate sau sunt protejate.",
"print_report_collection_details_button": "Afișează detalii",
"print_report_collection_details_ignored_notes": "Notițe ignorate"
"printing_pdf": "Exportare ca PDF în curs..."
},
"note_title": {
"placeholder": "introduceți titlul notiței aici...",
"created_on": "Creată la <Value />",
"last_modified": "Modificată la <Value />",
"note_type_switcher_label": "Schimbă din {{type}} la:",
"note_type_switcher_others": "Mai multe tipuri de notițe",
"note_type_switcher_templates": "Șablon",
"note_type_switcher_collection": "Colecție",
"edited_notes": "Notițe editate în această zi",
"promoted_attributes": "Atribute promovate"
"last_modified": "Modificată la <Value />"
},
"revisions_snapshot_limit": {
"erase_excess_revision_snapshots": "Șterge acum reviziile excesive",
@@ -1613,11 +1555,7 @@
"configure_launchbar": "Configurează bara de lansare"
},
"sql_result": {
"no_rows": "Nu s-a găsit niciun rând pentru această interogare",
"not_executed": "Această interogare nu a fost executată încă.",
"failed": "Interogarea SQL a eșuat",
"statement_result": "Rezultatul comenzii SQL",
"execute_now": "Execută acum"
"no_rows": "Nu s-a găsit niciun rând pentru această interogare"
},
"sql_table_schemas": {
"tables": "Tabele"
@@ -1639,8 +1577,7 @@
},
"toc": {
"options": "Setări",
"table_of_contents": "Cuprins",
"no_headings": "Niciun titlu."
"table_of_contents": "Cuprins"
},
"watched_file_update_status": {
"file_last_modified": "Fișierul <code class=\"file-path\"></code> a fost ultima oară modificat la data de <span class=\"file-last-modified\"></span>.",
@@ -1761,8 +1698,8 @@
"show-recent-notes": "Afișează notițele recente"
},
"electron_integration": {
"background-effects": "Activează efectele de fundal",
"background-effects-description": "Adaugă un fundal estompat și elegant ferestrelor aplicațiilor, creând profunzime și un aspect modern. Opțiunea „Bară de titlu nativă” trebuie să fie dezactivată.",
"background-effects": "Activează efectele de fundal (doar pentru Windows 11)",
"background-effects-description": "Efectul Mica adaugă un fundal estompat și elegant ferestrelor aplicațiilor, creând profunzime și un aspect modern. Opțiunea „Bară de titlu nativă” trebuie să fie dezactivată.",
"desktop-application": "Aplicația desktop",
"native-title-bar": "Bară de titlu nativă",
"native-title-bar-description": "Pentru Windows și macOS, dezactivarea bării de titlu native face aplicația să pară mai compactă. Pe Linux, păstrarea bării integrează mai bine aplicația cu restul sistemului de operare.",
@@ -1781,8 +1718,7 @@
"geo-map": {
"create-child-note-title": "Crează o notiță nouă și adaug-o pe hartă",
"unable-to-load-map": "Nu s-a putut încărca harta.",
"create-child-note-instruction": "Click pe hartă pentru a crea o nouă notiță la acea poziție sau apăsați Escape pentru a anula.",
"create-child-note-text": "Adaugă marcaj"
"create-child-note-instruction": "Click pe hartă pentru a crea o nouă notiță la acea poziție sau apăsați Escape pentru a anula."
},
"duration": {
"days": "zile",
@@ -2076,7 +2012,7 @@
"book_properties_config": {
"hide-weekends": "Ascunde weekend-urile",
"display-week-numbers": "Afișează numărul săptămânii",
"map-style": "Stil hartă",
"map-style": "Stil hartă:",
"max-nesting-depth": "Nivel maxim de imbricare:",
"raster": "Raster",
"vector_light": "Vectorial (culoare deschisă)",
@@ -2128,15 +2064,12 @@
},
"call_to_action": {
"background_effects_title": "Efectele de fundal sunt acum stabile",
"background_effects_message": "Pe dispozitive cu Windows și macOS, efectele de fundal sunt stabile. Acestea adaugă un strop de culoare interfeței grafice prin estomparea fundalului din spatele ferestrei.",
"background_effects_message": "Pe dispozitive cu Windows, efectele de fundal sunt complet stabile. Acestea adaugă un strop de culoare interfeței grafice prin estomparea fundalului din spatele ferestrei. Această tehnică este folosită și în alte aplicații precum Windows Explorer.",
"background_effects_button": "Activează efectele de fundal",
"next_theme_title": "Încercați noua temă Trilium",
"next_theme_message": "Utilizați tema clasică, doriți să încercați noua temă?",
"next_theme_button": "Testează noua temă",
"dismiss": "Treci peste",
"new_layout_title": "Aspect nou",
"new_layout_message": "Am introdus un aspect modernizat pentru Trilium. Panglică a fost integrată în restul interfeței, cu o bară de stare nouă și secțiuni expandabile (precum atributele promovate) ce preiau funcționalitatea de bază.\n\nNoul aspect este activat în mod implicit, și se poate dezactiva momentan din Opțiuni → Aspect.",
"new_layout_button": "Mai multe informații"
"dismiss": "Treci peste"
},
"ui-performance": {
"title": "Setări de performanță",
@@ -2151,10 +2084,7 @@
},
"settings_appearance": {
"related_code_blocks": "Tema de culori pentru blocuri de cod în notițe de tip text",
"related_code_notes": "Tema de culori pentru notițele de tip cod",
"ui": "Interfață grafică",
"ui_old_layout": "Aspect vechi",
"ui_new_layout": "Aspect nou"
"related_code_notes": "Tema de culori pentru notițele de tip cod"
},
"units": {
"percentage": "%"
@@ -2210,86 +2140,6 @@
"read_only_temporarily_disabled": "Editabilă temporar",
"read_only_temporarily_disabled_description": "Această notiță se poate modifica, deși în mod normal ea este doar în citire. Notița va reveni la modul doar în citire imediat ce navigați către altă notiță.\n\nClick pentru a re-activa modul doar în citire.",
"shared_publicly": "Partajată public",
"shared_locally": "Partajată local",
"shared_copy_to_clipboard": "Copiază legătură în clipboard",
"shared_open_in_browser": "Deschide legătura în browser",
"shared_unshare": "Înlătură partajarea",
"clipped_note": "Decupare web",
"clipped_note_description": "Această notiță a fost preluată de la {{url}}.\n\nClic pentru a naviga la pagina web sursă.",
"execute_script": "Rulează script",
"execute_script_description": "Această notiță este un script. Clic pentru a executa scriptul.",
"execute_sql": "Rulează SQL",
"execute_sql_description": "Această notiță este de tip SQL. Clic pentru a executa interogarea SQL.",
"save_status_saved": "Salvat",
"save_status_saving": "Se salvează...",
"save_status_unsaved": "Nesalvat",
"save_status_error": "Salvarea a eșuat",
"save_status_saving_tooltip": "Modificările sunt în curs de salvare.",
"save_status_unsaved_tooltip": "Există schimbări ce nu au fost încă salvate. Acestea vor fi salvate automat într-un moment.",
"save_status_error_tooltip": "A intervenit o eroare la salvarea notiței. Dacă este posibil, încercați să copiați conținutul notiței într-un alt loc și să reîmprospătați aplicația."
},
"breadcrumb": {
"hoisted_badge": "Focalizat",
"hoisted_badge_title": "Defocalizează",
"workspace_badge": "Spațiu de lucru",
"scroll_to_top_title": "Sari la începutul notiței",
"create_new_note": "Crează subnotiță",
"empty_hide_archived_notes": "Ascunde notițele arhivate"
},
"status_bar": {
"language_title": "Schimbă limba conținutului",
"note_info_title": "Afișează informații despre notiță precum data modificării și dimensiunea",
"backlinks_one": "{{count}} legătură de retur",
"backlinks_few": "{{count}} legături de retur",
"backlinks_other": "{{count}} de legături de retur",
"backlinks_title_one": "Afișează legătura de retur",
"backlinks_title_few": "Afișează legăturile de retur",
"backlinks_title_other": "Afișează legăturile de retur",
"attachments_one": "{{count}} atașament",
"attachments_few": "{{count}} atașamente",
"attachments_other": "{{count}} de atașamente",
"attachments_title_one": "Deschide atașamentul într-un tab nou",
"attachments_title_few": "Deschide atașamentele într-un tab nou",
"attachments_title_other": "Deschide atașamentele într-un tab nou",
"attributes_one": "{{count}} atribut",
"attributes_few": "{{count}} atribute",
"attributes_other": "{{count}} de atribute",
"attributes_title": "Atribute proprii și moștenite",
"note_paths_one": "O cale",
"note_paths_few": "{{count}} căi",
"note_paths_other": "{{count}} de căi",
"note_paths_title": "Căi ale notiței",
"code_note_switcher": "Schimbă limbajul"
},
"attributes_panel": {
"title": "Atributele notiței"
},
"right_pane": {
"empty_message": "Nimic de afișat pentru această notiță",
"empty_button": "Ascunde panoul",
"toggle": "Comută panoul din dreapta",
"custom_widget_go_to_source": "Mergi la codul sursă"
},
"pdf": {
"attachments_one": "{{count}} atașament",
"attachments_few": "{{count}} atașamente",
"attachments_other": "{{count}} de atașamente",
"layers_one": "{{count}} strat",
"layers_few": "{{count}} straturi",
"layers_other": "{{count}} de straturi",
"pages_one": "{{count}} pagină",
"pages_few": "{{count}} pagini",
"pages_other": "{{count}} de pagini",
"pages_alt": "Pagina {{pageNumber}}",
"pages_loading": "Încărcare..."
},
"platform_indicator": {
"available_on": "Disponibil pe {{platform}}"
},
"mobile_tab_switcher": {
"title_one": "{{count}} tab",
"title_few": "{{count}} taburi",
"title_other": "{{count}} de taburi",
"more_options": "Mai multe opțiuni"
"shared_locally": "Partajată local"
}
}

View File

@@ -1566,7 +1566,6 @@
"shared-indicator-tooltip": "此筆記已公開分享",
"shared-indicator-tooltip-with-url": "此筆記已公開分享至:{{- url}}",
"subtree-hidden-tooltip_one": "從樹中隱藏的 {{count}} 篇子筆記",
"subtree-hidden-tooltip_other": "",
"subtree-hidden-moved-title": "已新增至 {{title}}",
"subtree-hidden-moved-description-collection": "此集合隱藏其樹中的子筆記。",
"subtree-hidden-moved-description-other": "子筆記隱藏於此筆記的樹中。"
@@ -1603,11 +1602,7 @@
"configure_launchbar": "設定啟動欄"
},
"sql_result": {
"no_rows": "此次查詢沒有返回任何數據",
"not_executed": "查詢尚未執行。",
"failed": "SQL 查詢執行失敗",
"statement_result": "查詢結果",
"execute_now": "立即執行"
"no_rows": "此次查詢沒有返回任何數據"
},
"sql_table_schemas": {
"tables": "表"

View File

@@ -14,10 +14,7 @@
"edit_branch_prefix": "Редагувати префікс гілки",
"help_on_tree_prefix": "Довідка щодо префіксу дерева",
"prefix": "Префікс: ",
"branch_prefix_saved": "Префікс гілки збережено.",
"edit_branch_prefix_multiple": "Редагувати префікс гілки для {{count}} гілок",
"branch_prefix_saved_multiple": "Префікс гілки збережено для {{count}} гілок.",
"affected_branches": "Уражені гілки ({{count}}):"
"branch_prefix_saved": "Префікс гілки збережено."
},
"about": {
"app_version": "Версія програми:",
@@ -73,17 +70,8 @@
},
"bundle-error": {
"title": "Не вдалося завантажити користувацький скрипт",
"message": "Скрипт не вдалося виконати через:\n\n{{message}}"
},
"widget-list-error": {
"title": "Не вдалося отримати список віджетів з сервера"
},
"widget-render-error": {
"title": "Не вдалося відобразити користувацький віджет"
},
"widget-missing-parent": "Для власного віджета не визначено {{property}} обов'язкову властивість\n\nЯкщо цей скрипт призначений для запуску без елемента інтерфейсу користувача, використовуйте замість нього '#run=frontendStartup'.",
"open-script-note": "Відкрити нотатку сценарію",
"scripting-error": "Помилка користувацького скрипта: {{title}}"
"message": "Скрипт з нотатки ID \"{{id}}\" з заголовком \"{{title}}\" не вдалося виконати через:\n\n{{message}}"
}
},
"bulk_actions": {
"bulk_actions": "Масові дії",
@@ -211,8 +199,7 @@
"export_status": "Статус експорту",
"export_in_progress": "Триває експорт: {{progressCount}}",
"export_finished_successfully": "Експорт успішно завершено.",
"format_pdf": "PDF для друку або спільного використання.",
"share-format": "HTML для веб-публікацій використовує ту саму тему, що й для спільних нотаток, але може бути опублікований як статичний веб-сайт."
"format_pdf": "PDF для друку або спільного використання."
},
"help": {
"title": "Шпаргалка",
@@ -266,8 +253,7 @@
"showSQLConsole": "показати консоль SQL",
"other": "Інше",
"quickSearch": "фокус на швидкому введенні пошуку",
"inPageSearch": "пошук на сторінці",
"editShortcuts": "Редагувати комбінації клавіш"
"inPageSearch": "пошук на сторінці"
},
"import": {
"importIntoNote": "Імпортувати в нотатку",
@@ -863,10 +849,7 @@
"note_icon": {
"change_note_icon": "Змінити значок нотатки",
"search": "Пошук:",
"reset-default": "Скинути значок до стандартного значення",
"search_placeholder_one": "Пошук {{number}} значка у {{count}} пакеті",
"search_placeholder_few": "Пошук {{number}} значків у {{count}} пакетах",
"search_placeholder_many": "Пошук {{number}} значків у {{count}} пакетах"
"reset-default": "Скинути значок до стандартного значення"
},
"basic_properties": {
"note_type": "Тип нотатки",
@@ -901,7 +884,7 @@
"file_type": "Тип файлу",
"file_size": "Розмір файлу",
"download": "Завантажити",
"open": "Відкрити зовні",
"open": "Відкрити",
"upload_new_revision": "Завантажити нову версію",
"upload_success": "Завантажено нову версію файлу.",
"upload_failed": "Не вдалося завантажити нову версію файлу.",
@@ -1606,19 +1589,13 @@
"refresh-saved-search-results": "Оновити збережені результати пошуку",
"create-child-note": "Створити дочірню нотатку",
"unhoist": "Відкріпити",
"toggle-sidebar": "Перемикання бічної панелі",
"subtree-hidden-tooltip_one": "{{count}} дочірня нотатка, прихована від дерев",
"subtree-hidden-tooltip_few": "{{count}} дочірніх нотатки, прихованих від дерев",
"subtree-hidden-tooltip_many": "{{count}} дочірніх нотаток, прихованих від дерев"
"toggle-sidebar": "Перемикання бічної панелі"
},
"title_bar_buttons": {
"window-on-top": "Тримати вікно зверху"
},
"note_detail": {
"could_not_find_typewidget": "Не вдалося знайти typeWidget для типу '{{type}}'",
"print_report_collection_content_one": "{{count}} нотатку з колекції не вдалося роздрукувати, тому що вони не підтримуються або захищені.",
"print_report_collection_content_few": "{{count}} нотатки з колекції не вдалося роздрукувати, тому що вони не підтримуються або захищені.",
"print_report_collection_content_many": "{{count}} нотаток з колекції не вдалося роздрукувати, тому що вони не підтримуються або захищені."
"could_not_find_typewidget": "Не вдалося знайти typeWidget для типу '{{type}}'"
},
"note_title": {
"placeholder": "введіть тут заголовок нотатки..."
@@ -1766,7 +1743,7 @@
"unknown_widget": "Невідомий віджет для \"{{id}}\"."
},
"note_language": {
"not_set": "Мову не встановлено",
"not_set": "Не встановлено",
"configure-languages": "Налаштувати мови..."
},
"content_language": {
@@ -1833,7 +1810,7 @@
"book_properties_config": {
"hide-weekends": "Приховати вихідні",
"display-week-numbers": "Відображення номерів тижнів",
"map-style": "Стиль карти",
"map-style": "Стиль карти:",
"max-nesting-depth": "Максимальна глибина вкладення:",
"raster": "Растр",
"vector_light": "Вектор (Світла)",
@@ -1886,7 +1863,7 @@
"will_be_deleted_in": "Це вкладення буде автоматично видалено через {{time}}",
"will_be_deleted_soon": "Це вкладення незабаром буде автоматично видалено",
"deletion_reason": ", оскільки вкладення не має посилання у вмісті нотатки. Щоб запобігти видаленню, додайте посилання на вкладення назад у вміст або перетворіть вкладення на нотатку.",
"role_and_size": "Роль: {{role}}, розмір: {{size}}, формат даних: {{- mimeType}}",
"role_and_size": "Роль: {{role}}, Розмір: {{size}}",
"link_copied": "Посилання на вкладення скопійовано в буфер обміну.",
"unrecognized_role": "Нерозпізнана роль вкладення '{{role}}'."
},
@@ -1937,7 +1914,7 @@
"import-into-note": "Імпортувати в нотатку",
"apply-bulk-actions": "Застосувати масові дії",
"converted-to-attachments": "({{count}}) нотаток перетворено на вкладення.",
"convert-to-attachment-confirm": "Ви впевнені, що хочете конвертувати вибрані нотатки у вкладення до їхніх батьківських нотаток? Ця операція застосовується лише до нотаток із зображеннями, інші нотатки будуть пропущені.",
"convert-to-attachment-confirm": "Ви впевнені, що хочете конвертувати вибрані нотатки у вкладення до їхніх батьківських нотаток?",
"open-in-popup": "Швидке редагування",
"archive": "Архівувати",
"unarchive": "Розархівувати"
@@ -2001,10 +1978,7 @@
},
"highlights_list_2": {
"title": "Список основних моментів",
"options": "Параметри",
"title_with_count_one": "{{count}} виділення",
"title_with_count_few": "{{count}} виділення",
"title_with_count_many": "{{count}} виділень"
"options": "Параметри"
},
"table_context_menu": {
"delete_row": "Видалити рядок"
@@ -2077,36 +2051,5 @@
},
"collections": {
"rendering_error": "Не вдалося показати вміст через помилку."
},
"status_bar": {
"backlinks_one": "{{count}} зворотне посилання",
"backlinks_few": "{{count}} зворотні посилання",
"backlinks_many": "{{count}} зворотних посилань",
"backlinks_title_one": "Переглянути зворотне посилання",
"backlinks_title_few": "Переглянути зворотні посилання",
"backlinks_title_many": "Переглянути зворотніх посилань",
"attachments_one": "{{count}} вкладення",
"attachments_few": "{{count}} вкладення",
"attachments_many": "{{count}} вкладень",
"attachments_title_one": "Переглянути вкладення в новій вкладці",
"attachments_title_few": "Переглянути вкладення в новій вкладці",
"attachments_title_many": "Переглянути вкладень в новій вкладці",
"attributes_one": "{{count}} атрибут",
"attributes_few": "{{count}} атрибути",
"attributes_many": "{{count}} атрибутів",
"note_paths_one": "{{count}} шлях",
"note_paths_few": "{{count}} шляхи",
"note_paths_many": "{{count}} шляхів"
},
"pdf": {
"attachments_one": "{{count}} вкладення",
"attachments_few": "{{count}} вкладення",
"attachments_many": "{{count}} вкладень",
"layers_one": "{{count}} шар",
"layers_few": "{{count}} шари",
"layers_many": "{{count}} шарів",
"pages_one": "{{count}} сторінка",
"pages_few": "{{count}} сторінки",
"pages_many": "{{count}} сторінок"
}
}

View File

@@ -36,6 +36,7 @@ interface CustomGlobals {
isProtectedSessionAvailable: boolean;
isDev: boolean;
isMainWindow: boolean;
windowId: string;
maxEntityChangeIdAtLoad: number;
maxEntityChangeSyncIdAtLoad: number;
assetPath: string;

View File

@@ -1,83 +0,0 @@
.tn-backlinks-widget .backlinks-items {
list-style-type: none;
margin: 0;
padding: 0;
position: static;
width: unset;
> li {
--border-radius: 8px;
max-width: 600px;
padding: 10px 20px;
background: var(--card-background-color);
& + li {
margin-top: 2px;
}
&:first-child {
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
&:last-child {
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
/* Card header */
& > span:first-child {
display: block;
> span {
display: flex;
flex-wrap: wrap;
align-items: center;
/* Note path */
> small {
flex: 100%;
order: -1;
font-size: .65rem;
.note-path {
padding: 0;
}
}
/* Note icon */
> .tn-icon {
color: var(--menu-item-icon-color);
}
/* Note title */
> a {
margin-inline-start: 4px;
color: currentColor;
font-weight: 500;
}
}
}
/* Card content - excerpt */
.backlink-excerpt {
all: unset; /* TODO: Remove after disposing the old style from FloatingButtons.css */
display: block;
margin: 8px 0;
border-radius: 4px;
background: var(--quick-search-result-content-background);
padding: 8px;
font-size: .75rem;
a {
background: transparent;
color: var(--quick-search-result-highlight-color);
text-decoration: underline;
}
p {
margin: 0;
}
}
}
}

View File

@@ -1,5 +1,3 @@
import "./Backlinks.css";
import { BacklinkCountResponse, BacklinksResponse, SaveSqlConsoleResponse } from "@triliumnext/commons";
import { VNode } from "preact";
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
@@ -9,6 +7,7 @@ import Component from "../components/component";
import NoteContext from "../components/note_context";
import FNote from "../entities/fnote";
import attributes from "../services/attributes";
import { isExperimentalFeatureEnabled } from "../services/experimental_features";
import froca from "../services/froca";
import { t } from "../services/i18n";
import { copyImageReferenceToClipboard } from "../services/image";
@@ -56,12 +55,21 @@ export const DESKTOP_FLOATING_BUTTONS: FloatingButtonsList = [
OpenTriliumApiDocsButton,
SaveToNoteButton,
RelationMapButtons,
GeoMapButtons,
CopyImageReferenceButton,
ExportImageButtons,
InAppHelpButton,
Backlinks
];
export const MOBILE_FLOATING_BUTTONS: FloatingButtonsList = [
RefreshBackendLogButton,
EditButton,
RelationMapButtons,
ExportImageButtons,
Backlinks
];
/**
* Floating buttons that should be hidden in popup editor (Quick edit).
*/
@@ -91,10 +99,9 @@ function SwitchSplitOrientationButton({ note, isReadOnly, isDefaultViewMode }: F
/>;
}
function ToggleReadOnlyButton({ note, isDefaultViewMode }: FloatingButtonContext) {
function ToggleReadOnlyButton({ note, viewType, isDefaultViewMode }: FloatingButtonContext) {
const [ isReadOnly, setReadOnly ] = useNoteLabelBoolean(note, "readOnly");
const isSavedSqlite = note.isTriliumSqlite() && !note.isHiddenCompletely();
const isEnabled = ([ "mermaid", "mindMap", "canvas" ].includes(note.type) || isSavedSqlite)
const isEnabled = ([ "mermaid", "mindMap", "canvas" ].includes(note.type) || viewType === "geoMap")
&& note.isContentAvailable() && isDefaultViewMode;
return isEnabled && <FloatingButton
@@ -236,6 +243,17 @@ function RelationMapButtons({ note, isDefaultViewMode, triggerEvent }: FloatingB
);
}
function GeoMapButtons({ triggerEvent, viewType, isReadOnly }: FloatingButtonContext) {
const isEnabled = viewType === "geoMap" && !isReadOnly;
return isEnabled && (
<FloatingButton
icon="bx bx-plus-circle"
text={t("geo-map.create-child-note-title")}
onClick={() => triggerEvent("geoMapCreateChildNote")}
/>
);
}
function CopyImageReferenceButton({ note, isDefaultViewMode }: FloatingButtonContext) {
const hiddenImageCopyRef = useRef<HTMLDivElement>(null);
const isEnabled = (
@@ -287,7 +305,7 @@ function ExportImageButtons({ note, triggerEvent, isDefaultViewMode }: FloatingB
function InAppHelpButton({ note }: FloatingButtonContext) {
const helpUrl = getHelpUrlForNote(note);
const isEnabled = note.type !== "book" && !!helpUrl;
const isEnabled = !!helpUrl;
return isEnabled && (
<FloatingButton

View File

@@ -265,13 +265,9 @@ function useNoteInfo() {
const [ note, setNote ] = useState<FNote | null | undefined>();
const [ type, setType ] = useState<ExtendedNoteType>();
const [ mime, setMime ] = useState<string>();
const refreshIdRef = useRef(0);
function refresh() {
const refreshId = ++refreshIdRef.current;
getExtendedWidgetType(actualNote, noteContext).then(type => {
if (refreshId !== refreshIdRef.current) return;
setNote(actualNote);
setType(type);
setMime(actualNote?.mime);
@@ -322,8 +318,6 @@ export async function getExtendedWidgetType(note: FNote | null | undefined, note
resultingType = "noteMap";
} else if (type === "text" && (await noteContext?.isReadOnly())) {
resultingType = "readOnlyText";
} else if (note.isTriliumSqlite()) {
resultingType = "sqlConsole";
} else if ((type === "code" || type === "mermaid") && (await noteContext?.isReadOnly())) {
resultingType = "readOnlyCode";
} else if (type === "text") {
@@ -348,8 +342,9 @@ export function checkFullHeight(noteContext: NoteContext | undefined, type: Exte
// https://github.com/zadam/trilium/issues/2522
const isBackendNote = noteContext?.noteId === "_backendLog";
const isSqlNote = noteContext.note?.mime === "text/x-sqlite;schema=trilium";
const isFullHeightNoteType = type && TYPE_MAPPINGS[type].isFullHeight;
return (!noteContext?.hasNoteList() && isFullHeightNoteType)
return (!noteContext?.hasNoteList() && isFullHeightNoteType && !isSqlNote)
|| noteContext?.viewScope?.viewMode === "attachments"
|| isBackendNote;
}
@@ -363,8 +358,8 @@ function showToast(type: "printing" | "exporting_pdf", progress: number = 0) {
});
}
function handlePrintReport(printReport?: PrintReport) {
if (printReport?.type === "collection" && printReport.ignoredNoteIds.length > 0) {
function handlePrintReport(printReport: PrintReport) {
if (printReport.type === "collection" && printReport.ignoredNoteIds.length > 0) {
toast.showPersistent({
id: "print-report",
icon: "bx bx-collection",

View File

@@ -5,7 +5,6 @@ import clsx from "clsx";
import { ComponentChild, HTMLInputTypeAttribute, InputHTMLAttributes, MouseEventHandler, TargetedEvent, TargetedInputEvent } from "preact";
import { Dispatch, StateUpdater, useEffect, useRef, useState } from "preact/hooks";
import NoteContext from "../components/note_context";
import FAttribute from "../entities/fattribute";
import FNote from "../entities/fnote";
import { Attribute } from "../services/attribute_parser";
@@ -41,8 +40,8 @@ type OnChangeEventData = TargetedEvent<HTMLInputElement, Event> | InputEvent | J
type OnChangeListener = (e: OnChangeEventData) => Promise<void>;
export default function PromotedAttributes() {
const { note, componentId, noteContext } = useNoteContext();
const [ cells, setCells ] = usePromotedAttributeData(note, componentId, noteContext);
const { note, componentId } = useNoteContext();
const [ cells, setCells ] = usePromotedAttributeData(note, componentId);
return <PromotedAttributesContent note={note} componentId={componentId} cells={cells} setCells={setCells} />;
}
@@ -75,12 +74,12 @@ export function PromotedAttributesContent({ note, componentId, cells, setCells }
*
* The cells are returned as a state since they can also be altered internally if needed, for example to add a new empty cell.
*/
export function usePromotedAttributeData(note: FNote | null | undefined, componentId: string, noteContext: NoteContext | undefined): [ Cell[] | undefined, Dispatch<StateUpdater<Cell[] | undefined>> ] {
export function usePromotedAttributeData(note: FNote | null | undefined, componentId: string): [ Cell[] | undefined, Dispatch<StateUpdater<Cell[] | undefined>> ] {
const [ viewType ] = useNoteLabel(note, "viewType");
const [ cells, setCells ] = useState<Cell[]>();
function refresh() {
if (!note || viewType === "table" || noteContext?.viewScope?.viewMode !== "default") {
if (!note || viewType === "table") {
setCells([]);
return;
}
@@ -125,7 +124,7 @@ export function usePromotedAttributeData(note: FNote | null | undefined, compone
setCells(cells);
}
useEffect(refresh, [ note, viewType, noteContext ]);
useEffect(refresh, [ note, viewType ]);
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (loadResults.getAttributeRows(componentId).find((attr) => attributes.isAffecting(attr, note))) {
refresh();
@@ -218,7 +217,6 @@ function LabelInput({ inputId, ...props }: CellProps & { inputId: string }) {
id={inputId}
type={LABEL_MAPPINGS[definition.labelType ?? "text"]}
value={valueAttr.value}
checked={definition.labelType === "boolean" ? valueAttr.value === "true" : undefined}
placeholder={t("promoted_attributes.unset-field-placeholder")}
data-attribute-id={valueAttr.attributeId}
data-attribute-type={valueAttr.type}

View File

@@ -29,6 +29,7 @@ export default function GlobalMenu({ isHorizontalLayout }: { isHorizontalLayout:
const isVerticalLayout = !isHorizontalLayout;
const parentComponent = useContext(ParentComponent);
const { isUpdateAvailable, latestVersion } = useTriliumUpdateStatus();
const isMobileLocal = isMobile();
const logoRef = useRef<SVGSVGElement>(null);
useStaticTooltip(logoRef);
@@ -43,7 +44,8 @@ export default function GlobalMenu({ isHorizontalLayout }: { isHorizontalLayout:
</div>}
</>}
noDropdownListStyle
mobileBackdrop
onShown={isMobileLocal ? () => document.getElementById("context-menu-cover")?.classList.add("show", "global-menu-cover") : undefined}
onHidden={isMobileLocal ? () => document.getElementById("context-menu-cover")?.classList.remove("show", "global-menu-cover") : undefined}
>
<MenuItem command="openNewWindow" icon="bx bx-window-open" text={t("global_menu.open_new_window")} />
@@ -105,7 +107,8 @@ function BrowserOnlyOptions() {
function DevelopmentOptions({ dropStart }: { dropStart: boolean }) {
return <>
<FormListHeader text="Development Options"></FormListHeader>
<FormDropdownDivider />
<FormListItem disabled>Development Options</FormListItem>
<FormDropdownSubmenu icon="bx bx-test-tube" title="Experimental features" dropStart={dropStart}>
{experimentalFeatures.map((feature) => (
<ExperimentalFeatureToggle key={feature.id} experimentalFeature={feature as ExperimentalFeature} />

View File

@@ -1,7 +1,7 @@
.note-list-widget {
min-height: 0;
max-width: var(--max-content-width); /* Inherited from .note-split */
overflow: auto;
contain: none !important;
}
@@ -11,6 +11,10 @@ body.prefers-centered-content .note-list-widget:not(.full-height) {
margin-inline: auto;
}
.note-list-widget .note-list {
padding-block: 10px;
}
.note-list-widget.full-height,
.note-list-widget.full-height .note-list-widget-content {
height: 100%;
@@ -21,12 +25,8 @@ body.prefers-centered-content .note-list-widget:not(.full-height) {
}
/* #region Pagination */
.note-list-pager {
font-size: 1rem;
span.current-page {
text-decoration: underline;
font-weight: bold;
}
.note-list-pager span.current-page {
text-decoration: underline;
font-weight: bold;
}
/* #endregion */
/* #endregion */

View File

@@ -1,7 +1,7 @@
import "./NoteList.css";
import { WebSocketMessage } from "@triliumnext/commons";
import { Component, VNode } from "preact";
import { VNode } from "preact";
import { lazy, Suspense } from "preact/compat";
import { useEffect, useRef, useState } from "preact/hooks";
@@ -120,9 +120,7 @@ export function CustomNoteList({ note, viewType, isEnabled: shouldEnable, notePa
}
const ComponentToRender = viewType && props && isEnabled && (
props.media === "print"
? ViewComponents[viewType].print ?? ViewComponents[viewType].normal
: ViewComponents[viewType].normal
props.media === "print" ? ViewComponents[viewType].print : ViewComponents[viewType].normal
);
return (

View File

@@ -2,8 +2,7 @@
position: relative;
height: 100%;
user-select: none;
display: flex;
flex-direction: column;
overflow-x: auto;
--card-font-size: 0.9em;
--card-line-height: 1.2;
@@ -20,10 +19,8 @@ body.mobile .board-view {
height: 100%;
display: flex;
gap: 1em;
padding-inline: 12px;
padding-block: 4px;
padding: 1em;
align-items: flex-start;
overflow-x: auto;
}
.board-view-container .board-column {
@@ -355,4 +352,4 @@ body.mobile .board-view-container .board-column {
font-size: 0.9em;
max-width: 200px;
word-wrap: break-word;
}
}

View File

@@ -1,23 +1,20 @@
import "./index.css";
import { createContext, TargetedKeyboardEvent } from "preact";
import { Dispatch, StateUpdater, useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
import FNote from "../../../entities/fnote";
import { t } from "../../../services/i18n";
import toast from "../../../services/toast";
import CollectionProperties from "../../note_bars/CollectionProperties";
import FormTextArea from "../../react/FormTextArea";
import FormTextBox from "../../react/FormTextBox";
import { ViewModeProps } from "../interface";
import "./index.css";
import { ColumnMap, getBoardData } from "./data";
import { useNoteLabelBoolean, useNoteLabelWithDefault, useTriliumEvent } from "../../react/hooks";
import Icon from "../../react/Icon";
import NoteAutocomplete from "../../react/NoteAutocomplete";
import { onWheelHorizontalScroll } from "../../widget_utils";
import { ViewModeProps } from "../interface";
import { t } from "../../../services/i18n";
import Api from "./api";
import BoardApi from "./api";
import FormTextBox from "../../react/FormTextBox";
import { createContext, TargetedKeyboardEvent } from "preact";
import { onWheelHorizontalScroll } from "../../widget_utils";
import Column from "./column";
import { ColumnMap, getBoardData } from "./data";
import BoardApi from "./api";
import FormTextArea from "../../react/FormTextArea";
import FNote from "../../../entities/fnote";
import NoteAutocomplete from "../../react/NoteAutocomplete";
import toast from "../../../services/toast";
export interface BoardViewData {
columns?: BoardColumnData[];
@@ -148,7 +145,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
const insertBefore = mouseX < columnMiddle;
// Calculate the target position
const targetIndex = insertBefore ? index : index + 1;
let targetIndex = insertBefore ? index : index + 1;
setColumnDropPosition(targetIndex);
}, [draggedColumn]);
@@ -162,14 +159,15 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
}, [draggedColumn, columnDropPosition, handleColumnDrop]);
return (
<div className="board-view">
<CollectionProperties note={parentNote} />
<div
className="board-view"
onWheel={onWheelHorizontalScroll}
>
<BoardViewContext.Provider value={boardViewContext}>
{byColumn && columns && <div
className="board-view-container"
onDragOver={handleColumnDragOver}
onDrop={handleContainerDrop}
onWheel={onWheelHorizontalScroll}
>
{columns.map((column, index) => (
<>
@@ -196,7 +194,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
</div>}
</BoardViewContext.Provider>
</div>
);
)
}
function AddNewColumn({ api, isInRelationMode }: { api: BoardApi, isInRelationMode: boolean }) {
@@ -220,26 +218,26 @@ function AddNewColumn({ api, isInRelationMode }: { api: BoardApi, isInRelationMo
tabIndex={300}
>
{!isCreatingNewColumn
? <>
<Icon icon="bx bx-plus" />{" "}
{t("board_view.add-column")}
</>
: (
<TitleEditor
placeholder={t("board_view.add-column-placeholder")}
save={async (columnName) => {
const created = await api.addNewColumn(columnName);
if (!created) {
toast.showMessage(t("board_view.column-already-exists"), undefined, "bx bx-duplicate");
}
}}
dismiss={() => setIsCreatingNewColumn(false)}
isNewItem
mode={isInRelationMode ? "relation" : "normal"}
/>
)}
? <>
<Icon icon="bx bx-plus" />{" "}
{t("board_view.add-column")}
</>
: (
<TitleEditor
placeholder={t("board_view.add-column-placeholder")}
save={async (columnName) => {
const created = await api.addNewColumn(columnName);
if (!created) {
toast.showMessage(t("board_view.column-already-exists"), undefined, "bx bx-duplicate");
}
}}
dismiss={() => setIsCreatingNewColumn(false)}
isNewItem
mode={isInRelationMode ? "relation" : "normal"}
/>
)}
</div>
);
)
}
export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, isNewItem }: {
@@ -304,26 +302,26 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, is
onBlur={onBlur}
/>
);
}
return (
<NoteAutocomplete
inputRef={inputRef}
noteId={currentValue ?? ""}
opts={{
hideAllButtons: true,
allowCreatingNotes: true
}}
onKeyDown={(e) => {
if (e.key === "Escape") {
} else {
return (
<NoteAutocomplete
inputRef={inputRef}
noteId={currentValue ?? ""}
opts={{
hideAllButtons: true,
allowCreatingNotes: true
}}
onKeyDown={(e) => {
if (e.key === "Escape") {
dismiss();
}
}}
onBlur={() => dismiss()}
noteIdChanged={(newValue) => {
save(newValue);
dismiss();
}
}}
onBlur={() => dismiss()}
noteIdChanged={(newValue) => {
save(newValue);
dismiss();
}}
/>
);
}}
/>
);
}
}

View File

@@ -1,8 +1,8 @@
import { AttributeRow, CreateChildrenResponse } from "@triliumnext/commons";
import { CreateChildrenResponse } from "@triliumnext/commons";
import server from "../../../services/server";
import FNote from "../../../entities/fnote";
import { setAttribute, setLabel } from "../../../services/attributes";
import server from "../../../services/server";
import froca from "../../../services/froca";
interface NewEventOpts {
title: string;
@@ -10,7 +10,6 @@ interface NewEventOpts {
endDate?: string | null;
startTime?: string | null;
endTime?: string | null;
componentId?: string;
}
interface ChangeEventOpts {
@@ -18,48 +17,30 @@ interface ChangeEventOpts {
endDate?: string | null;
startTime?: string | null;
endTime?: string | null;
componentId?: string;
}
export async function newEvent(parentNote: FNote, { title, startDate, endDate, startTime, endTime, componentId }: NewEventOpts) {
const attributes: Omit<AttributeRow, "noteId" | "attributeId">[] = [];
attributes.push({
type: "label",
name: "startDate",
value: startDate
});
if (endDate) {
attributes.push({
type: "label",
name: "endDate",
value: endDate
});
}
if (startTime) {
attributes.push({
type: "label",
name: "startTime",
value: startTime
});
}
if (endTime) {
attributes.push({
type: "label",
name: "endTime",
value: endTime
});
}
export async function newEvent(parentNote: FNote, { title, startDate, endDate, startTime, endTime }: NewEventOpts) {
// Create the note.
await server.post<CreateChildrenResponse>(`notes/${parentNote.noteId}/children?target=into`, {
const { note } = await server.post<CreateChildrenResponse>(`notes/${parentNote.noteId}/children?target=into`, {
title,
content: "",
type: "text",
attributes
}, componentId);
type: "text"
});
// Set the attributes.
setLabel(note.noteId, "startDate", startDate);
if (endDate) {
setLabel(note.noteId, "endDate", endDate);
}
if (startTime) {
setLabel(note.noteId, "startTime", startTime);
}
if (endTime) {
setLabel(note.noteId, "endTime", endTime);
}
}
export async function changeEvent(note: FNote, { startDate, endDate, startTime, endTime, componentId }: ChangeEventOpts) {
export async function changeEvent(note: FNote, { startDate, endDate, startTime, endTime }: ChangeEventOpts) {
// Don't store the end date if it's empty.
if (endDate === startDate) {
endDate = undefined;
@@ -71,12 +52,12 @@ export async function changeEvent(note: FNote, { startDate, endDate, startTime,
let endAttribute = note.getAttributes("label").filter(attr => attr.name == "calendar:endDate").shift()?.value||"endDate";
const noteId = note.noteId;
setLabel(noteId, startAttribute, startDate, false, componentId);
setAttribute(note, "label", endAttribute, endDate, componentId);
setLabel(noteId, startAttribute, startDate);
setAttribute(note, "label", endAttribute, endDate);
startAttribute = note.getAttributes("label").filter(attr => attr.name == "calendar:startTime").shift()?.value||"startTime";
endAttribute = note.getAttributes("label").filter(attr => attr.name == "calendar:endTime").shift()?.value||"endTime";
setAttribute(note, "label", startAttribute, startTime, componentId);
setAttribute(note, "label", endAttribute, endTime, componentId);
setAttribute(note, "label", startAttribute, startTime);
setAttribute(note, "label", endAttribute, endTime);
}

View File

@@ -1,12 +1,12 @@
import NoteColorPicker from "../../../menus/custom-items/NoteColorPicker";
import FNote from "../../../entities/fnote";
import contextMenu, { ContextMenuEvent } from "../../../menus/context_menu";
import { getArchiveMenuItem } from "../../../menus/context_menu_utils";
import NoteColorPicker from "../../../menus/custom-items/NoteColorPicker";
import link_context_menu from "../../../menus/link_context_menu";
import branches from "../../../services/branches";
import { getArchiveMenuItem } from "../../../menus/context_menu_utils";
import { t } from "../../../services/i18n";
export function openCalendarContextMenu(e: ContextMenuEvent, note: FNote, parentNote: FNote, componentId?: string) {
export function openCalendarContextMenu(e: ContextMenuEvent, note: FNote, parentNote: FNote) {
e.preventDefault();
e.stopPropagation();
@@ -30,16 +30,16 @@ export function openCalendarContextMenu(e: ContextMenuEvent, note: FNote, parent
}
if (branchIdToDelete) {
await branches.deleteNotes([ branchIdToDelete ], false, false, componentId);
await branches.deleteNotes([ branchIdToDelete ], false, false);
}
}
},
{ kind: "separator" },
{
kind: "custom",
componentFn: () => NoteColorPicker({note})
componentFn: () => NoteColorPicker({note: note})
}
],
selectMenuItemHandler: ({ command }) => link_context_menu.handleLinkContextMenuItem(command, e, note.noteId),
});
})
}

View File

@@ -1,10 +1,9 @@
import { EventInput, EventSourceFuncArg, EventSourceInput } from "@fullcalendar/core/index.js";
import clsx from "clsx";
import FNote from "../../../entities/fnote";
import froca from "../../../services/froca";
import server from "../../../services/server";
import { formatDateToLocalISO, getCustomisableLabel, getMonthsInDateRange, offsetDate } from "./utils";
import FNote from "../../../entities/fnote";
import server from "../../../services/server";
import clsx from "clsx";
interface Event {
startDate: string,
@@ -106,8 +105,7 @@ export async function buildEvent(note: FNote, { startDate, endDate, startTime, e
endDate = (endTime ? `${endDate}T${endTime}:00` : endDate);
const eventData: EventInput = {
id: note.noteId,
title,
title: title,
start: startDate,
url: `#${note.noteId}?popup`,
noteId: note.noteId,
@@ -150,12 +148,12 @@ async function parseCustomTitle(customTitlettributeName: string | null, note: FN
}
async function buildDisplayedAttributes(note: FNote, calendarDisplayedAttributes: string[]) {
const filteredDisplayedAttributes = note.getAttributes().filter((attr): boolean => calendarDisplayedAttributes.includes(attr.name));
const filteredDisplayedAttributes = note.getAttributes().filter((attr): boolean => calendarDisplayedAttributes.includes(attr.name))
const result: Array<[string, string]> = [];
for (const attribute of filteredDisplayedAttributes) {
if (attribute.type === "label") result.push([attribute.name, attribute.value]);
else result.push([attribute.name, (await attribute.getTargetNote())?.title || ""]);
else result.push([attribute.name, (await attribute.getTargetNote())?.title || ""])
}
return result;

View File

@@ -21,16 +21,7 @@
outline: 0;
height: 100%;
user-select: none;
padding: 0;
@media (max-width: 991px) {
padding: 0;
th {
font-weight: normal;
font-size: 0.9em;
}
}
padding: 10px;
}
.calendar-view a,
@@ -52,7 +43,6 @@
--fc-border-color: var(--main-border-color);
--fc-neutral-bg-color: var(--launcher-pane-background-color);
--fc-list-event-hover-bg-color: var(--left-pane-item-hover-background);
padding: 0 12px;
}
.calendar-container .fc-list-sticky .fc-list-day > * {
@@ -69,43 +59,41 @@
overflow: hidden;
}
.calendar-view .collection-properties {
.center-container {
justify-content: center;
.title {
min-width: 150px;
font-size: 1.2em;
text-align: center;
}
}
@media (max-width: 991px) {
>div {
justify-content: flex-start;
}
.right-container {
flex-grow: 0;
}
.center-container {
.title {
flex-grow: 1;
min-width: 110px;
font-size: 0.95em;
}
}
}
/* #region Header */
.calendar-view .calendar-header {
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 10px;
}
.calendar-view .calendar-header .btn {
min-width: unset !important;
}
.calendar-view .calendar-header > .title {
flex-grow: 1;
font-size: 1.3rem;
font-weight: normal;
}
body.desktop:not(.zen) .calendar-view .calendar-header {
padding-block-start: 4px;
padding-inline-end: 5em;
}
.search-result-widget-content .calendar-view .calendar-header {
padding-inline-end: unset !important;
}
/* #endregion */
/* #region Events */
/*
* week, month, year views
*/
.calendar-container a.fc-event {
.calendar-container a.fc-event {
text-decoration: none;
}
@@ -138,7 +126,7 @@
.calendar-view a.fc-timegrid-event,
.calendar-view a.fc-daygrid-event {
--border-color: transparent;
border: 2px solid;
border-left-width: 4px;
border-color: var(--border-color) var(--border-color) var(--border-color)
@@ -187,7 +175,7 @@
opacity: .75;
}
/*
/*
* List view
*/
@@ -200,4 +188,4 @@
--fc-event-border-color: var(--custom-color);
}
/* #endregion */
/* #endregion */

View File

@@ -5,7 +5,7 @@ import { DateSelectArg, EventChangeArg, EventMountArg, EventSourceFuncArg, Local
import { DateClickArg } from "@fullcalendar/interaction";
import { DISPLAYABLE_LOCALE_IDS } from "@triliumnext/commons";
import { RefObject } from "preact";
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
import appContext from "../../../components/app_context";
import FNote from "../../../entities/fnote";
@@ -14,13 +14,9 @@ import dialog from "../../../services/dialog";
import froca from "../../../services/froca";
import { t } from "../../../services/i18n";
import { isMobile } from "../../../services/utils";
import CollectionProperties from "../../note_bars/CollectionProperties";
import ActionButton from "../../react/ActionButton";
import Button, { ButtonGroup } from "../../react/Button";
import Dropdown from "../../react/Dropdown";
import { FormListItem } from "../../react/FormList";
import { useNoteLabel, useNoteLabelBoolean, useResizeObserver, useSpacedUpdate, useTriliumEvent, useTriliumOption, useTriliumOptionInt } from "../../react/hooks";
import { ParentComponent } from "../../react/react_utils";
import TouchBar, { TouchBarButton, TouchBarLabel, TouchBarSegmentedControl, TouchBarSpacer } from "../../react/TouchBar";
import { ViewModeProps } from "../interface";
import { changeEvent, newEvent } from "./api";
@@ -44,28 +40,24 @@ const CALENDAR_VIEWS = [
{
type: "timeGridWeek",
name: t("calendar.week"),
icon: "bx bx-calendar-week",
previousText: t("calendar.week_previous"),
nextText: t("calendar.week_next")
},
{
type: "dayGridMonth",
name: t("calendar.month"),
icon: "bx bx-calendar",
previousText: t("calendar.month_previous"),
nextText: t("calendar.month_next")
},
{
type: "multiMonthYear",
name: t("calendar.year"),
icon: "bx bx-layer",
previousText: t("calendar.year_previous"),
nextText: t("calendar.year_next")
},
{
type: "listMonth",
name: t("calendar.list"),
icon: "bx bx-list-ol",
previousText: t("calendar.month_previous"),
nextText: t("calendar.month_next")
}
@@ -95,7 +87,6 @@ export const LOCALE_MAPPINGS: Record<DISPLAYABLE_LOCALE_IDS, (() => Promise<{ de
};
export default function CalendarView({ note, noteIds }: ViewModeProps<CalendarViewData>) {
const parentComponent = useContext(ParentComponent);
const containerRef = useRef<HTMLDivElement>(null);
const calendarRef = useRef<FullCalendar>(null);
@@ -114,40 +105,32 @@ export default function CalendarView({ note, noteIds }: ViewModeProps<CalendarVi
const eventBuilder = useMemo(() => {
if (!isCalendarRoot) {
return async () => await buildEvents(noteIds);
}
}
return async (e: EventSourceFuncArg) => await buildEventsForCalendar(note, e);
}, [isCalendarRoot, noteIds]);
const plugins = usePlugins(isEditable, isCalendarRoot);
const locale = useLocale();
const { eventDidMount } = useEventDisplayCustomization(note, parentComponent?.componentId);
const editingProps = useEditing(note, isEditable, isCalendarRoot, parentComponent?.componentId);
const { eventDidMount } = useEventDisplayCustomization(note);
const editingProps = useEditing(note, isEditable, isCalendarRoot);
// React to changes.
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
const api = calendarRef.current;
if (!api) return;
// Subnote attribute change.
if (loadResults.getAttributeRows(parentComponent?.componentId).some((a) => noteIds.includes(a.noteId ?? ""))) {
if (loadResults.getNoteIds().some(noteId => noteIds.includes(noteId)) // note title change.
|| loadResults.getAttributeRows().some((a) => noteIds.includes(a.noteId ?? ""))) // subnote change.
{
// Defer execution after the load results are processed so that the event builder has the updated data to work with.
setTimeout(() => api.refetchEvents(), 0);
return; // early return since we'll refresh the events anyway
}
// Title change.
for (const noteId of loadResults.getNoteIds().filter(noteId => noteIds.includes(noteId))) {
const event = api.getEventById(noteId);
const note = froca.getNoteFromCache(noteId);
if (!event || !note) continue;
event.setProp("title", note.title);
setTimeout(() => {
calendarRef.current?.refetchEvents();
}, 0);
}
});
return (plugins &&
<div className="calendar-view" ref={containerRef} tabIndex={100}>
<CalendarCollectionProperties note={note} calendarRef={calendarRef} />
<CalendarHeader calendarRef={calendarRef} />
<Calendar
events={eventBuilder}
calendarRef={calendarRef}
@@ -176,67 +159,28 @@ export default function CalendarView({ note, noteIds }: ViewModeProps<CalendarVi
);
}
function CalendarCollectionProperties({ note, calendarRef }: {
note: FNote;
calendarRef: RefObject<FullCalendar>;
}) {
function CalendarHeader({ calendarRef }: { calendarRef: RefObject<FullCalendar> }) {
const { title, viewType: currentViewType } = useOnDatesSet(calendarRef);
const currentViewData = CALENDAR_VIEWS.find(v => calendarRef.current && v.type === currentViewType);
const isMobileLocal = isMobile();
return (
<CollectionProperties
note={note}
centerChildren={<>
<ActionButton icon="bx bx-chevron-left" text={currentViewData?.previousText ?? ""} onClick={() => calendarRef.current?.prev()} />
<span className="title">{title}</span>
<ActionButton icon="bx bx-chevron-right" text={currentViewData?.nextText ?? ""} onClick={() => calendarRef.current?.next()} />
<Button text={t("calendar.today")} onClick={() => calendarRef.current?.today()} />
{isMobileLocal && <MobileCalendarViewSwitcher calendarRef={calendarRef} />}
</>}
rightChildren={<>
{!isMobileLocal && <DesktopCalendarViewSwitcher calendarRef={calendarRef} />}
</>}
/>
);
}
function DesktopCalendarViewSwitcher({ calendarRef }: { calendarRef: RefObject<FullCalendar> }) {
const { viewType: currentViewType } = useOnDatesSet(calendarRef);
return (
<>
<div className="calendar-header">
<span className="title">{title}</span>
<ButtonGroup>
{CALENDAR_VIEWS.map(viewData => (
<Button
key={viewData.type}
text={viewData.name}
text={viewData.name.toLocaleLowerCase()}
className={currentViewType === viewData.type ? "active" : ""}
onClick={() => calendarRef.current?.changeView(viewData.type)}
/>
))}
</ButtonGroup>
</>
);
}
function MobileCalendarViewSwitcher({ calendarRef }: { calendarRef: RefObject<FullCalendar> }) {
const { viewType: currentViewType } = useOnDatesSet(calendarRef);
const currentViewTypeData = CALENDAR_VIEWS.find(view => view.type === currentViewType);
return (
<Dropdown
text={currentViewTypeData?.name}
>
{CALENDAR_VIEWS.map(viewData => (
<FormListItem
key={viewData.type}
selected={currentViewType === viewData.type}
icon={viewData.icon}
onClick={() => calendarRef.current?.changeView(viewData.type)}
>{viewData.name}</FormListItem>
))}
</Dropdown>
<Button text={t("calendar.today").toLocaleLowerCase()} onClick={() => calendarRef.current?.today()} />
<ButtonGroup>
<ActionButton icon="bx bx-chevron-left" text={currentViewData?.previousText ?? ""} frame onClick={() => calendarRef.current?.prev()} />
<ActionButton icon="bx bx-chevron-right" text={currentViewData?.nextText ?? ""} frame onClick={() => calendarRef.current?.next()} />
</ButtonGroup>
</div>
);
}
@@ -263,23 +207,22 @@ function usePlugins(isEditable: boolean, isCalendarRoot: boolean) {
}
function useLocale() {
const [ locale ] = useTriliumOption("locale");
const [ formattingLocale ] = useTriliumOption("formattingLocale");
const [ calendarLocale, setCalendarLocale ] = useState<LocaleInput>();
useEffect(() => {
const correspondingLocale = LOCALE_MAPPINGS[formattingLocale] ?? LOCALE_MAPPINGS[locale];
const correspondingLocale = LOCALE_MAPPINGS[formattingLocale];
if (correspondingLocale) {
correspondingLocale().then((locale) => setCalendarLocale(locale.default));
} else {
setCalendarLocale(undefined);
}
}, [formattingLocale, locale]);
});
return calendarLocale;
}
function useEditing(note: FNote, isEditable: boolean, isCalendarRoot: boolean, componentId: string | undefined) {
function useEditing(note: FNote, isEditable: boolean, isCalendarRoot: boolean) {
const onCalendarSelection = useCallback(async (e: DateSelectArg) => {
const { startDate, endDate } = parseStartEndDateFromEvent(e);
if (!startDate) return;
@@ -291,8 +234,8 @@ function useEditing(note: FNote, isEditable: boolean, isCalendarRoot: boolean, c
return;
}
newEvent(note, { title, startDate, endDate, startTime, endTime, componentId });
}, [ note, componentId ]);
newEvent(note, { title, startDate, endDate, startTime, endTime });
}, [ note ]);
const onEventChange = useCallback(async (e: EventChangeArg) => {
const { startDate, endDate } = parseStartEndDateFromEvent(e.event);
@@ -301,8 +244,8 @@ function useEditing(note: FNote, isEditable: boolean, isCalendarRoot: boolean, c
const { startTime, endTime } = parseStartEndTimeFromEvent(e.event);
const note = await froca.getNote(e.event.extendedProps.noteId);
if (!note) return;
changeEvent(note, { startDate, endDate, startTime, endTime, componentId });
}, [ componentId ]);
changeEvent(note, { startDate, endDate, startTime, endTime });
}, []);
// Called upon when clicking the day number in the calendar, opens or creates the day note but only if in a calendar root.
const onDateClick = useCallback(async (e: DateClickArg) => {
@@ -321,7 +264,7 @@ function useEditing(note: FNote, isEditable: boolean, isCalendarRoot: boolean, c
};
}
function useEventDisplayCustomization(parentNote: FNote, componentId: string | undefined) {
function useEventDisplayCustomization(parentNote: FNote) {
const eventDidMount = useCallback((e: EventMountArg) => {
const { iconClass, promotedAttributes } = e.event.extendedProps;
@@ -378,7 +321,7 @@ function useEventDisplayCustomization(parentNote: FNote, componentId: string | u
const note = await froca.getNote(e.event.extendedProps.noteId);
if (!note) return;
openCalendarContextMenu(contextMenuEvent, note, parentNote, componentId);
openCalendarContextMenu(contextMenuEvent, note, parentNote);
}
if (isMobile()) {

View File

@@ -2,13 +2,6 @@
overflow: hidden;
position: relative;
height: 100%;
display: flex;
flex-direction: column;
> .collection-properties {
position: relative;
z-index: 2000;
}
}
.geo-map-container {

View File

@@ -1,29 +1,24 @@
import Map from "./map";
import "./index.css";
import { ViewModeProps } from "../interface";
import { useNoteBlob, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useNoteTreeDrag, useSpacedUpdate, useTriliumEvent } from "../../react/hooks";
import { DEFAULT_MAP_LAYER_NAME } from "./map_layer";
import { divIcon, GPXOptions, LatLng, LeafletMouseEvent } from "leaflet";
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
import Marker, { GpxTrack } from "./marker";
import froca from "../../../services/froca";
import FNote from "../../../entities/fnote";
import markerIcon from "leaflet/dist/images/marker-icon.png";
import markerIconShadow from "leaflet/dist/images/marker-shadow.png";
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
import appContext from "../../../components/app_context";
import FNote from "../../../entities/fnote";
import branches from "../../../services/branches";
import froca from "../../../services/froca";
import { t } from "../../../services/i18n";
import server from "../../../services/server";
import toast from "../../../services/toast";
import CollectionProperties from "../../note_bars/CollectionProperties";
import ActionButton from "../../react/ActionButton";
import { ButtonOrActionButton } from "../../react/Button";
import { useNoteBlob, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useNoteTreeDrag, useSpacedUpdate, useTriliumEvent } from "../../react/hooks";
import { ParentComponent } from "../../react/react_utils";
import TouchBar, { TouchBarButton, TouchBarSlider } from "../../react/TouchBar";
import { ViewModeProps } from "../interface";
import { createNewNote, moveMarker } from "./api";
import openContextMenu, { openMapContextMenu } from "./context_menu";
import Map from "./map";
import { DEFAULT_MAP_LAYER_NAME } from "./map_layer";
import Marker, { GpxTrack } from "./marker";
import toast from "../../../services/toast";
import { t } from "../../../services/i18n";
import server from "../../../services/server";
import branches from "../../../services/branches";
import TouchBar, { TouchBarButton, TouchBarLabel, TouchBarSlider } from "../../react/TouchBar";
import { ParentComponent } from "../../react/react_utils";
const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659];
const DEFAULT_ZOOM = 2;
@@ -55,7 +50,7 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
}
}, 5000);
useEffect(() => { froca.getNotes(noteIds).then(setNotes); }, [ noteIds ]);
useEffect(() => { froca.getNotes(noteIds).then(setNotes) }, [ noteIds ]);
useEffect(() => {
if (!note) return;
@@ -65,7 +60,7 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
// Note creation.
useTriliumEvent("geoMapCreateChildNote", () => {
toast.showPersistent({
toast.showPersistent({
icon: "plus",
id: "geo-new-note",
title: "New note",
@@ -135,19 +130,6 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
return (
<div className={`geo-view ${state === State.NewNote ? "placing-note" : ""}`}>
<CollectionProperties
note={note}
rightChildren={<>
<ToggleReadOnlyButton note={note} />
<ButtonOrActionButton
icon="bx bx-plus"
text={t("geo-map.create-child-note-text")}
title={t("geo-map.create-child-note-title")}
triggerCommand="geoMapCreateChildNote"
disabled={isReadOnly}
/>
</>}
/>
{ coordinates !== undefined && zoom !== undefined && <Map
apiRef={apiRef} containerRef={containerRef}
coordinates={coordinates}
@@ -169,16 +151,6 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
);
}
function ToggleReadOnlyButton({ note }: { note: FNote }) {
const [ isReadOnly, setReadOnly ] = useNoteLabelBoolean(note, "readOnly");
return <ActionButton
text={isReadOnly ? t("toggle_read_only_button.unlock-editing") : t("toggle_read_only_button.lock-editing")}
icon={isReadOnly ? "bx bx-lock-open-alt" : "bx bx-lock-alt"}
onClick={() => setReadOnly(!isReadOnly)}
/>;
}
function NoteWrapper({ note, isReadOnly }: { note: FNote, isReadOnly: boolean }) {
const mime = useNoteProperty(note, "mime");
const [ location ] = useNoteLabel(note, LOCATION_ATTRIBUTE);
@@ -232,7 +204,7 @@ function NoteMarker({ note, editable, latLng }: { note: FNote, editable: boolean
onDragged={editable ? onDragged : undefined}
onClick={!editable ? onClick : undefined}
onContextMenu={onContextMenu}
/>;
/>
}
function NoteGpxTrack({ note }: { note: FNote }) {
@@ -266,7 +238,7 @@ function NoteGpxTrack({ note }: { note: FNote }) {
color: note.getLabelValue("color") ?? "blue"
}
}), [ color, iconClass ]);
return xmlString && <GpxTrack gpxXmlString={xmlString} options={options} />;
return xmlString && <GpxTrack gpxXmlString={xmlString} options={options} />
}
function buildIcon(bxIconClass: string, colorClass?: string, title?: string, noteIdLink?: string, archived?: boolean) {
@@ -320,5 +292,5 @@ function GeoMapTouchBar({ state, map }: { state: State, map: L.Map | null | unde
enabled={state === State.Normal}
/>
</TouchBar>
);
)
}

View File

@@ -7,8 +7,7 @@ import attribute_renderer from "../../../services/attribute_renderer";
import content_renderer from "../../../services/content_renderer";
import { t } from "../../../services/i18n";
import link from "../../../services/link";
import CollectionProperties from "../../note_bars/CollectionProperties";
import { useImperativeSearchHighlighlighting, useNoteLabel, useNoteLabelBoolean, useNoteProperty } from "../../react/hooks";
import { useImperativeSearchHighlighlighting, useNoteLabel, useNoteLabelBoolean } from "../../react/hooks";
import Icon from "../../react/Icon";
import NoteLink from "../../react/NoteLink";
import { ViewModeProps } from "../interface";
@@ -20,18 +19,11 @@ export function ListView({ note, noteIds: unfilteredNoteIds, highlightedTokens }
const noteIds = useFilteredNoteIds(note, unfilteredNoteIds);
const { pageNotes, ...pagination } = usePagination(note, noteIds);
const [ includeArchived ] = useNoteLabelBoolean(note, "includeArchived");
const noteType = useNoteProperty(note, "type");
const hasCollectionProperties = [ "book", "search" ].includes(noteType ?? "");
return (
<div class="note-list list-view">
<CollectionProperties
note={note}
centerChildren={<Pager {...pagination} />}
/>
{ noteIds.length > 0 && <div class="note-list-wrapper">
{!hasCollectionProperties && <Pager {...pagination} />}
<Pager {...pagination} />
<div class="note-list-container use-tn-links">
{pageNotes?.map(childNote => (
@@ -53,18 +45,11 @@ export function GridView({ note, noteIds: unfilteredNoteIds, highlightedTokens }
const noteIds = useFilteredNoteIds(note, unfilteredNoteIds);
const { pageNotes, ...pagination } = usePagination(note, noteIds);
const [ includeArchived ] = useNoteLabelBoolean(note, "includeArchived");
const noteType = useNoteProperty(note, "type");
const hasCollectionProperties = [ "book", "search" ].includes(noteType ?? "");
return (
<div class="note-list grid-view">
<CollectionProperties
note={note}
centerChildren={<Pager {...pagination} />}
/>
<div class="note-list-wrapper">
{!hasCollectionProperties && <Pager {...pagination} />}
<Pager {...pagination} />
<div class="note-list-container use-tn-links">
{pageNotes?.map(childNote => (
@@ -155,7 +140,7 @@ function NoteAttributes({ note }: { note: FNote }) {
return <span className="note-list-attributes" ref={ref} />;
}
export function NoteContent({ note, trim, noChildrenList, highlightedTokens, includeArchivedNotes }: {
function NoteContent({ note, trim, noChildrenList, highlightedTokens, includeArchivedNotes }: {
note: FNote;
trim?: boolean;
noChildrenList?: boolean;

View File

@@ -1,7 +1,11 @@
.presentation-view {
display: flex;
flex-direction: column;
height: 100%;
.presentation-button-bar {
position: absolute;
top: 1em;
right: 1em;
.floating-buttons-children {
top: 0;
}
}
.presentation-container {

View File

@@ -1,21 +1,18 @@
import "./index.css";
import { RefObject } from "preact";
import { ViewModeMedia, ViewModeProps } from "../interface";
import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
import Reveal from "reveal.js";
import slideBaseStylesheet from "reveal.js/dist/reveal.css?raw";
import { openInCurrentNoteContext } from "../../../components/note_context";
import FNote from "../../../entities/fnote";
import { t } from "../../../services/i18n";
import CollectionProperties from "../../note_bars/CollectionProperties";
import ActionButton from "../../react/ActionButton";
import { useNoteLabelWithDefault, useTriliumEvent } from "../../react/hooks";
import ShadowDom from "../../react/ShadowDom";
import { ViewModeMedia, ViewModeProps } from "../interface";
import { buildPresentationModel, PresentationModel, PresentationSlideBaseModel } from "./model";
import slideCustomStylesheet from "./slidejs.css?raw";
import { buildPresentationModel, PresentationModel, PresentationSlideBaseModel } from "./model";
import ShadowDom from "../../react/ShadowDom";
import ActionButton from "../../react/ActionButton";
import "./index.css";
import { RefObject } from "preact";
import { openInCurrentNoteContext } from "../../../components/note_context";
import { useNoteLabelWithDefault, useTriliumEvent } from "../../react/hooks";
import { t } from "../../../services/i18n";
import { DEFAULT_THEME, loadPresentationTheme } from "./themes";
import FNote from "../../../entities/fnote";
export default function PresentationView({ note, noteIds, media, onReady, onProgressChanged }: ViewModeProps<{}>) {
const [ presentation, setPresentation ] = useState<PresentationModel>();
@@ -54,17 +51,14 @@ export default function PresentationView({ note, noteIds, media, onReady, onProg
if (media === "screen") {
return (
<div class="presentation-view">
<CollectionProperties
note={note}
rightChildren={<ButtonOverlay containerRef={containerRef} api={api} />}
/>
<>
<ShadowDom
className="presentation-container"
containerRef={containerRef}
>{content}</ShadowDom>
</div>
);
<ButtonOverlay containerRef={containerRef} api={api} />
</>
)
} else if (media === "print") {
// Printing needs a query parameter that is read by Reveal.js.
const url = new URL(window.location.href);
@@ -114,34 +108,42 @@ function ButtonOverlay({ containerRef, api }: { containerRef: RefObject<HTMLDivE
}, [ api ]);
return (
<>
<ActionButton
icon="bx bx-edit"
text={t("presentation_view.edit-slide")}
onClick={e => {
const currentSlide = api?.getCurrentSlide();
const noteId = getNoteIdFromSlide(currentSlide);
<div className="presentation-button-bar">
<div className="floating-buttons-children">
<ActionButton
className="floating-button"
icon="bx bx-edit"
text={t("presentation_view.edit-slide")}
noIconActionClass
onClick={e => {
const currentSlide = api?.getCurrentSlide();
const noteId = getNoteIdFromSlide(currentSlide);
if (noteId) {
openInCurrentNoteContext(e, noteId);
}
}}
/>
if (noteId) {
openInCurrentNoteContext(e, noteId);
}
}}
/>
<ActionButton
icon="bx bx-grid-horizontal"
text={t("presentation_view.slide-overview")}
active={isOverviewActive}
onClick={() => api?.toggleOverview()}
/>
<ActionButton
className="floating-button"
icon="bx bx-grid-horizontal"
text={t("presentation_view.slide-overview")}
active={isOverviewActive}
noIconActionClass
onClick={() => api?.toggleOverview()}
/>
<ActionButton
icon="bx bx-fullscreen"
text={t("presentation_view.start-presentation")}
onClick={() => containerRef.current?.requestFullscreen()}
/>
</>
);
<ActionButton
className="floating-button"
icon="bx bx-fullscreen"
text={t("presentation_view.start-presentation")}
noIconActionClass
onClick={() => containerRef.current?.requestFullscreen()}
/>
</div>
</div>
)
}
function Presentation({ presentation, setApi } : { presentation: PresentationModel, setApi: (api: Reveal.Api | undefined) => void }) {
@@ -177,7 +179,7 @@ function Presentation({ presentation, setApi } : { presentation: PresentationMod
api.destroy();
setRevealApi(undefined);
setApi(undefined);
};
}
}, []);
useEffect(() => {
@@ -189,19 +191,19 @@ function Presentation({ presentation, setApi } : { presentation: PresentationMod
<div className="slides">
{presentation.slides?.map(slide => {
if (!slide.verticalSlides) {
return <Slide key={slide.noteId} slide={slide} />;
return <Slide key={slide.noteId} slide={slide} />
} else {
return (
<section>
<Slide key={slide.noteId} slide={slide} />
{slide.verticalSlides.map(slide => <Slide key={slide.noteId} slide={slide} /> )}
</section>
);
}
return (
<section>
<Slide key={slide.noteId} slide={slide} />
{slide.verticalSlides.map(slide => <Slide key={slide.noteId} slide={slide} /> )}
</section>
);
})}
</div>
</div>
);
)
}

View File

@@ -3,12 +3,7 @@
position: relative;
height: 100%;
user-select: none;
display: flex;
flex-direction: column;
.tabulator-tableholder {
height: unset !important;
}
padding: 0 5px 0 10px;
}
.table-view-container {
@@ -73,4 +68,4 @@
inset-inline-start: 0;
font-size: 1.5em;
transform: translateY(-50%);
}
}

View File

@@ -1,22 +1,20 @@
import "./index.css";
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
import { DataTreeModule, EditModule, FormatModule, FrozenColumnsModule, InteractionModule, MoveColumnsModule, MoveRowsModule, Options, PersistenceModule, ResizeColumnsModule, RowComponent,SortModule, Tabulator as VanillaTabulator} from 'tabulator-tables';
import { t } from "../../../services/i18n";
import SpacedUpdate from "../../../services/spaced_update";
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
import CollectionProperties from "../../note_bars/CollectionProperties";
import { ButtonOrActionButton } from "../../react/Button";
import { useLegacyWidget } from "../../react/hooks";
import { ParentComponent } from "../../react/react_utils";
import { ViewModeProps } from "../interface";
import useColTableEditing from "./col_editing";
import { useContextMenu } from "./context_menu";
import useData, { TableConfig } from "./data";
import useRowTableEditing from "./row_editing";
import { TableData } from "./rows";
import { useLegacyWidget } from "../../react/hooks";
import Tabulator from "./tabulator";
import { Tabulator as VanillaTabulator, SortModule, FormatModule, InteractionModule, EditModule, ResizeColumnsModule, FrozenColumnsModule, PersistenceModule, MoveColumnsModule, MoveRowsModule, DataTreeModule, Options, RowComponent} from 'tabulator-tables';
import { useContextMenu } from "./context_menu";
import { ParentComponent } from "../../react/react_utils";
import FNote from "../../../entities/fnote";
import { t } from "../../../services/i18n";
import Button from "../../react/Button";
import "./index.css";
import useRowTableEditing from "./row_editing";
import useColTableEditing from "./col_editing";
import AttributeDetailWidget from "../../attribute_widgets/attribute_detail";
import SpacedUpdate from "../../../services/spaced_update";
import useData, { TableConfig } from "./data";
export default function TableView({ note, noteIds, notePath, viewConfig, saveConfig }: ViewModeProps<TableConfig>) {
const tabulatorRef = useRef<VanillaTabulator>(null);
@@ -38,7 +36,7 @@ export default function TableView({ note, noteIds, notePath, viewConfig, saveCon
dataTreeChildIndent: 20,
dataTreeExpandElement: `<button class="tree-expand"><span class="bx bx-chevron-right"></span></button>`,
dataTreeCollapseElement: `<button class="tree-collapse"><span class="bx bx-chevron-down"></span></button>`
};
}
}, [ hasChildren ]);
const rowFormatter = useCallback((row: RowComponent) => {
@@ -48,16 +46,6 @@ export default function TableView({ note, noteIds, notePath, viewConfig, saveCon
return (
<div className="table-view">
<CollectionProperties
note={note}
rightChildren={note.type !== "search" &&
<>
<ButtonOrActionButton triggerCommand="addNewRow" icon="bx bx-plus" text={t("table_view.new-row")} />
<ButtonOrActionButton triggerCommand="addNewTableColumn" icon="bx bx-carousel" text={t("table_view.new-column")} />
</>
}
/>
{rowData !== undefined && persistenceProps && (
<>
<Tabulator
@@ -66,6 +54,7 @@ export default function TableView({ note, noteIds, notePath, viewConfig, saveCon
columns={columnDefs ?? []}
data={rowData}
modules={[ SortModule, FormatModule, InteractionModule, EditModule, ResizeColumnsModule, FrozenColumnsModule, PersistenceModule, MoveColumnsModule, MoveRowsModule, DataTreeModule ]}
footerElement={<TableFooter note={note} />}
events={{
...contextMenuEvents,
...rowEditingEvents
@@ -78,11 +67,24 @@ export default function TableView({ note, noteIds, notePath, viewConfig, saveCon
rowFormatter={rowFormatter}
{...dataTreeProps}
/>
<TableFooter note={note} />
</>
)}
{attributeDetailWidgetEl}
</div>
);
)
}
function TableFooter({ note }: { note: FNote }) {
return (note.type !== "search" &&
<div className="tabulator-footer">
<div className="tabulator-footer-contents">
<Button triggerCommand="addNewRow" icon="bx bx-plus" text={t("table_view.new-row")} />
{" "}
<Button triggerCommand="addNewTableColumn" icon="bx bx-carousel" text={t("table_view.new-column")} />
</div>
</div>
)
}
function usePersistence(viewConfig: TableConfig | null | undefined, saveConfig: (newConfig: TableConfig) => void) {

View File

@@ -1,20 +1,18 @@
import { useContext, useEffect, useLayoutEffect, useRef } from "preact/hooks";
import { EventCallBackMethods, Module, Options, Tabulator as VanillaTabulator } from "tabulator-tables";
import "tabulator-tables/dist/css/tabulator.css";
import "../../../../src/stylesheets/table.css";
import { isValidElement, RefObject } from "preact";
import { useContext, useEffect, useLayoutEffect, useRef } from "preact/hooks";
import { JSX } from "preact/jsx-runtime";
import { EventCallBackMethods, Module, Options, Tabulator as VanillaTabulator } from "tabulator-tables";
import { ParentComponent, renderReactWidget } from "../../react/react_utils";
import { JSX } from "preact/jsx-runtime";
import { isValidElement, RefObject } from "preact";
interface TableProps<T> extends Omit<Options, "data" | "footerElement" | "index"> {
tabulatorRef?: RefObject<VanillaTabulator>;
tabulatorRef: RefObject<VanillaTabulator>;
className?: string;
data?: T[];
modules?: (new (table: VanillaTabulator) => Module)[];
events?: Partial<EventCallBackMethods>;
index?: keyof T;
index: keyof T;
footerElement?: string | HTMLElement | JSX.Element;
onReady?: () => void;
}
@@ -45,9 +43,7 @@ export default function Tabulator<T>({ className, columns, data, modules, tabula
tabulator.on("tableBuilt", () => {
tabulatorRef.current = tabulator;
if (externalTabulatorRef) {
externalTabulatorRef.current = tabulator;
}
externalTabulatorRef.current = tabulator;
onReady?.();
});
@@ -66,15 +62,12 @@ export default function Tabulator<T>({ className, columns, data, modules, tabula
for (const [ eventName, handler ] of Object.entries(events)) {
tabulator.off(eventName as keyof EventCallBackMethods, handler);
}
};
}
}, Object.values(events ?? {}));
// Change in data.
useEffect(() => { tabulatorRef.current?.setData(data); }, [ data ]);
useEffect(() => {
if (!columns) return;
tabulatorRef.current?.setColumns(columns);
}, [ columns ]);
useEffect(() => { tabulatorRef.current?.setData(data) }, [ data ]);
useEffect(() => { columns && tabulatorRef.current?.setColumns(columns)}, [ data]);
return (
<div ref={containerRef} className={className} />

View File

@@ -1,22 +1,14 @@
.scrolling-container {
--content-margin-inline: 24px;
overflow: auto;
scroll-behavior: smooth;
position: relative;
> .note-detail > .note-detail-editable-text > .note-detail-editable-text-editor,
> .note-list-widget:not(.full-height) .note-list-wrapper {
margin-inline: var(--content-margin-inline);
> .inline-title,
> .note-detail > .note-detail-editable-text,
> .note-list-widget:not(.full-height) {
padding-inline: 24px;
}
> .inline-title {
padding-inline: var(--content-margin-inline);
}
> .note-detail > .note-detail-editable-text > .note-detail-editable-text-editor {
overflow: unset;
}
}
.note-split.type-code:not(.mime-text-x-sqlite) {

View File

@@ -82,18 +82,13 @@ body.mobile .modal.popup-editor-dialog .modal-dialog {
align-items: flex-start;
}
.modal.popup-editor-dialog .note-detail {
width: 100%;
}
.modal.popup-editor-dialog .note-detail.full-height {
flex-grow: 0;
height: 100%;
}
.modal.popup-editor-dialog .note-detail-editable-text-editor {
margin: 0 28px;
overflow: visible; /* Allow selection rectangle to go outside of the editor area */
.modal.popup-editor-dialog .note-detail-editable-text {
padding: 0 1em;
}
.modal.popup-editor-dialog .note-detail-file {
@@ -111,4 +106,4 @@ body.mobile .modal.popup-editor-dialog .modal-dialog {
margin: 0;
border-radius: 0;
}
}
}

View File

@@ -5,15 +5,13 @@ import { useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
import appContext from "../../components/app_context";
import NoteContext from "../../components/note_context";
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
import froca from "../../services/froca";
import { t } from "../../services/i18n";
import tree from "../../services/tree";
import utils from "../../services/utils";
import NoteList from "../collections/NoteList";
import FloatingButtons from "../FloatingButtons";
import { DESKTOP_FLOATING_BUTTONS, POPUP_HIDDEN_FLOATING_BUTTONS } from "../FloatingButtonsDefinitions";
import NoteBadges from "../layout/NoteBadges";
import { DESKTOP_FLOATING_BUTTONS, MOBILE_FLOATING_BUTTONS, POPUP_HIDDEN_FLOATING_BUTTONS } from "../FloatingButtonsDefinitions";
import NoteIcon from "../note_icon";
import NoteTitleWidget from "../note_title";
import NoteDetail from "../NoteDetail";
@@ -25,6 +23,8 @@ import ReadOnlyNoteInfoBar from "../ReadOnlyNoteInfoBar";
import StandaloneRibbonAdapter from "../ribbon/components/StandaloneRibbonAdapter";
import FormattingToolbar from "../ribbon/FormattingToolbar";
import MobileEditorToolbar from "../type_widgets/text/mobile_editor_toolbar";
import NoteBadges from "../layout/NoteBadges";
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
@@ -34,7 +34,7 @@ export default function PopupEditor() {
const [ noteContext, setNoteContext ] = useState(new NoteContext("_popup-editor"));
const isMobile = utils.isMobile();
const items = useMemo(() => {
const baseItems = isMobile ? [] : DESKTOP_FLOATING_BUTTONS;
const baseItems = isMobile ? MOBILE_FLOATING_BUTTONS : DESKTOP_FLOATING_BUTTONS;
return baseItems.filter(item => !POPUP_HIDDEN_FLOATING_BUTTONS.includes(item));
}, [ isMobile ]);

View File

@@ -1,7 +1,7 @@
import appContext from "../../components/app_context";
import { t } from "../../services/i18n";
import options from "../../services/options";
import utils, { isMac } from "../../services/utils";
import utils from "../../services/utils";
/**
* A "call-to-action" is an interactive message for the user, generally to present new features.
@@ -41,6 +41,10 @@ export interface CallToAction {
}[];
}
function isNextTheme() {
return [ "next", "next-light", "next-dark" ].includes(options.get("theme"));
}
const CALL_TO_ACTIONS: CallToAction[] = [
{
id: "new_layout",
@@ -59,7 +63,7 @@ const CALL_TO_ACTIONS: CallToAction[] = [
id: "background_effects",
title: t("call_to_action.background_effects_title"),
message: t("call_to_action.background_effects_message"),
enabled: () => (isMac() && !options.is("backgroundEffects")),
enabled: () => false,
buttons: [
{
text: t("call_to_action.background_effects_button"),
@@ -74,7 +78,7 @@ const CALL_TO_ACTIONS: CallToAction[] = [
id: "next_theme",
title: t("call_to_action.next_theme_title"),
message: t("call_to_action.next_theme_message"),
enabled: () => ![ "next", "next-light", "next-dark" ].includes(options.get("theme")),
enabled: () => !isNextTheme(),
buttons: [
{
text: t("call_to_action.next_theme_button"),

View File

@@ -3,7 +3,6 @@ import { useCallback, useLayoutEffect, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
import froca from "../../services/froca";
import { isDesktop, isMobile } from "../../services/utils";
import TabSwitcher from "../mobile_widgets/TabSwitcher";
import { useTriliumEvent } from "../react/hooks";
import { onWheelHorizontalScroll } from "../widget_utils";
import BookmarkButtons from "./BookmarkButtons";
@@ -98,8 +97,6 @@ function initBuiltinWidget(note: FNote, isHorizontalLayout: boolean) {
return <QuickSearchLauncherWidget />;
case "aiChatLauncher":
return <AiChatButton launcherNote={note} />;
case "mobileTabSwitcher":
return <TabSwitcher />;
default:
throw new Error(`Unrecognized builtin widget ${builtinWidget} for launcher ${note.noteId} "${note.title}"`);
}

View File

@@ -1,4 +1,3 @@
import clsx from "clsx";
import { createContext } from "preact";
import { useContext } from "preact/hooks";
@@ -19,12 +18,12 @@ export interface LauncherNoteProps {
launcherNote: FNote;
}
export function LaunchBarActionButton({ className, ...props }: Omit<ActionButtonProps, "noIconActionClass" | "titlePosition">) {
export function LaunchBarActionButton(props: Omit<ActionButtonProps, "className" | "noIconActionClass" | "titlePosition">) {
const { isHorizontalLayout } = useContext(LaunchBarContext);
return (
<ActionButton
className={clsx("button-widget launcher-button", className)}
className="button-widget launcher-button"
noIconActionClass
titlePosition={isHorizontalLayout ? "bottom" : "right"}
{...props}

View File

@@ -5,10 +5,8 @@ import { Tooltip } from "bootstrap";
import clsx from "clsx";
import { ComponentChild } from "preact";
import { useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
import type React from "react";
import { Trans } from "react-i18next";
import FNote from "../../entities/fnote";
import { ViewScope } from "../../services/link";
import { formatDateTime } from "../../utils/formatters";
import NoteIcon from "../note_icon";
@@ -24,12 +22,12 @@ const supportedNoteTypes = new Set<NoteType>([
export default function InlineTitle() {
const { note, parentComponent, viewScope } = useNoteContext();
const type = useNoteProperty(note, "type");
const [ shown, setShown ] = useState(shouldShow(note, type, viewScope));
const [ shown, setShown ] = useState(shouldShow(note?.noteId, type, viewScope));
const containerRef = useRef<HTMLDivElement>(null);
const [ titleHidden, setTitleHidden ] = useState(false);
useLayoutEffect(() => {
setShown(shouldShow(note, type, viewScope));
setShown(shouldShow(note?.noteId, type, viewScope));
}, [ note, type, viewScope ]);
useLayoutEffect(() => {
@@ -71,10 +69,9 @@ export default function InlineTitle() {
);
}
function shouldShow(note: FNote | null | undefined, type: NoteType | undefined, viewScope: ViewScope | undefined) {
function shouldShow(noteId: string | undefined, type: NoteType | undefined, viewScope: ViewScope | undefined) {
if (viewScope?.viewMode !== "default") return false;
if (note?.noteId?.startsWith("_options")) return true;
if (note?.isTriliumSqlite()) return false;
if (noteId?.startsWith("_options")) return true;
return type && supportedNoteTypes.has(type);
}

View File

@@ -26,6 +26,7 @@ export default function NoteTitleActions() {
<div className="title-actions">
<PromotedAttributes note={note} componentId={componentId} noteContext={noteContext} />
{noteType === "search" && <SearchProperties note={note} ntxId={ntxId} />}
{!isHiddenNote && note && noteType === "book" && <CollectionProperties note={note} />}
<EditedNotes />
<NoteTypeSwitcher />
</div>
@@ -48,7 +49,7 @@ function PromotedAttributes({ note, componentId, noteContext }: {
componentId: string,
noteContext: NoteContext | undefined
}) {
const [ cells, setCells ] = usePromotedAttributeData(note, componentId, noteContext);
const [ cells, setCells ] = usePromotedAttributeData(note, componentId);
const [ expanded, setExpanded ] = useState(false);
useEffect(() => {

View File

@@ -39,7 +39,7 @@ export default function NoteTypeSwitcher() {
const currentNoteTypeData = useMemo(() => NOTE_TYPES.find(t => t.type === currentNoteType), [ currentNoteType ]);
const { builtinTemplates, collectionTemplates } = useBuiltinTemplates();
return (currentNoteType && supportedNoteTypes.has(currentNoteType) && !note?.isTriliumSqlite() &&
return (currentNoteType && supportedNoteTypes.has(currentNoteType) &&
<div
className="note-type-switcher"
onWheel={onWheelHorizontalScroll}

View File

@@ -91,6 +91,68 @@
.note-paths-widget {
padding: 0.5em;
}
.note-path-intro {
color: var(--muted-text-color);
}
.note-path-list {
margin: 12px 0;
padding: 0;
list-style: none;
/* Note path card */
li {
--border-radius: 6px;
position: relative;
background: var(--card-background-color);
padding: 8px 20px 8px 25px;
&:first-child {
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
&:last-child {
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
& + li {
margin-top: 2px;
}
/* Current path arrow */
&.path-current::before {
position: absolute;
display: flex;
justify-content: flex-end;
align-items: center;
content: "\ee8f";
top: 0;
left: 0;
width: 20px;
bottom: 0;
font-family: "boxicons";
font-size: .75em;
color: var(--menu-item-icon-color);
}
}
/* Note path segment */
a {
margin-inline: 2px;
padding-inline: 2px;
color: currentColor;
font-weight: normal;
text-decoration: none;
/* The last segment of the note path */
&.basename {
color: var(--muted-text-color);
}
}
}
}
.backlinks-widget > .dropdown-menu {
@@ -98,6 +160,84 @@
max-height: 60vh;
overflow-y: scroll;
/* Backlink card */
li {
--border-radius: 8px;
max-width: 600px;
padding: 10px 20px;
background: var(--card-background-color);
& + li {
margin-top: 2px;
}
&:first-child {
border-radius: var(--border-radius) var(--border-radius) 0 0;
}
&:last-child {
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
/* Card header */
& > span:first-child {
display: block;
> span {
display: flex;
flex-wrap: wrap;
align-items: center;
/* Note path */
> small {
flex: 100%;
order: -1;
font-size: .65rem;
.note-path {
padding: 0;
}
}
/* Note icon */
> .tn-icon {
color: var(--menu-item-icon-color);
}
/* Note title */
> a {
margin-inline-start: 4px;
color: currentColor;
font-weight: 500;
}
}
}
/* Card content - excerpt */
& > span:nth-child(2) > div {
all: unset; /* TODO: Remove after disposing the old style from FloatingButtons.css */
display: block;
margin: 8px 0;
border-radius: 4px;
background: var(--quick-search-result-content-background);
padding: 8px;
font-size: .75rem;
a {
background: transparent;
color: var(--quick-search-result-highlight-color);
text-decoration: underline;
}
p {
margin: 0;
}
}
}
}
}

View File

@@ -1,6 +1,6 @@
import "./StatusBar.css";
import { Locale, NOTE_TYPE_ICONS, NoteType } from "@triliumnext/commons";
import { Locale, NoteType } from "@triliumnext/commons";
import { Dropdown as BootstrapDropdown } from "bootstrap";
import clsx from "clsx";
import { type ComponentChildren, RefObject } from "preact";
@@ -9,7 +9,7 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "p
import { CommandNames } from "../../components/app_context";
import NoteContext from "../../components/note_context";
import FNote from "../../entities/fnote";
import FNote, { NOTE_TYPE_ICONS } from "../../entities/fnote";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import { ViewScope } from "../../services/link";
@@ -56,7 +56,7 @@ export default function StatusBar() {
similarNotesShown: activePane === "similar-notes",
setSimilarNotesShown: (shown) => setActivePane(shown && "similar-notes")
};
const isHiddenNote = note?.isHiddenCompletely();
const isHiddenNote = note?.isInHiddenSubtree();
return (
<div className="status-bar">
@@ -300,7 +300,7 @@ function BacklinksBadge({ note, viewScope }: StatusBarContext) {
const count = useBacklinkCount(note, viewScope?.viewMode === "default");
return (note && count > 0 &&
<StatusBarDropdown
className="backlinks-badge backlinks-widget tn-backlinks-widget"
className="backlinks-badge backlinks-widget"
icon="bx bx-link"
text={t("status_bar.backlinks", { count })}
title={t("status_bar.backlinks_title", { count })}

View File

@@ -1,133 +0,0 @@
#launcher-container .mobile-tab-switcher {
position: relative;
&::after {
content: attr(data-tab-count);
font-family: var(--main-font-family);
font-size: 10px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.modal.tab-bar-modal {
.modal-dialog {
min-height: 85vh;
}
.tabs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1em;
@media (min-width: 850px) {
grid-template-columns: 1fr 1fr 1fr;
}
.tab-card {
background: var(--card-background-color);
border-radius: 1em;
min-width: 0;
overflow: hidden;
height: 200px;
display: flex;
flex-direction: column;
&.with-hue {
background-color: hsl(var(--bg-hue), 8.8%, 11.2%);
border-color: hsl(var(--bg-hue), 9.4%, 25.1%);
}
&.active {
outline: 4px solid var(--more-accented-background-color);
background: var(--card-background-hover-color);
.title {
font-weight: bold;
}
}
header {
padding: 0.4em 0.5em;
border-bottom: 1px solid rgba(150, 150, 150, 0.1);
display: flex;
overflow: hidden;
align-items: center;
color: var(--custom-color, inherit);
flex-shrink: 0;
&:not(:first-of-type) {
border-top: 1px solid rgba(150, 150, 150, 0.1);
}
>.tn-icon {
margin-inline-end: 0.4em;
}
.title {
overflow: hidden;
text-overflow: ellipsis;
text-wrap: nowrap;
font-size: 0.9em;
flex-grow: 1;
}
.icon-action {
flex-shrink: 0;
}
}
.tab-preview {
flex-grow: 1;
height: 100%;
overflow: hidden;
font-size: 0.5em;
user-select: none;
pointer-events: none;
&.type-text {
padding: 10px;
}
&.type-book,
&.type-contentWidget,
&.type-search,
&.type-empty {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25em;
color: var(--muted-text-color);
}
.preview-placeholder {
font-size: 500%;
}
p { margin-bottom: 0.2em;}
h2 { font-size: 1.20em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.10em; }
h5 { font-size: 1.05em}
h6 { font-size: 1em; }
}
&.with-split {
.preview-placeholder {
font-size: 250%;
}
}
}
}
.modal-footer {
.tn-link {
color: var(--main-text-color);
width: 40%;
text-align: center;
text-decoration: none;
}
}
}

View File

@@ -1,240 +0,0 @@
import "./TabSwitcher.css";
import clsx from "clsx";
import { createPortal, Fragment } from "preact/compat";
import { useCallback, useEffect, useRef, useState } from "preact/hooks";
import appContext, { CommandNames } from "../../components/app_context";
import NoteContext from "../../components/note_context";
import FNote from "../../entities/fnote";
import contextMenu from "../../menus/context_menu";
import { getHue, parseColor } from "../../services/css_class_manager";
import froca from "../../services/froca";
import { t } from "../../services/i18n";
import { NoteContent } from "../collections/legacy/ListOrGridView";
import { LaunchBarActionButton } from "../launch_bar/launch_bar_widgets";
import { ICON_MAPPINGS } from "../note_bars/CollectionProperties";
import ActionButton from "../react/ActionButton";
import { useActiveNoteContext, useNoteIcon, useTriliumEvents } from "../react/hooks";
import Icon from "../react/Icon";
import LinkButton from "../react/LinkButton";
import Modal from "../react/Modal";
export default function TabSwitcher() {
const [ shown, setShown ] = useState(false);
const mainNoteContexts = useMainNoteContexts();
return (
<>
<LaunchBarActionButton
className="mobile-tab-switcher"
icon="bx bx-rectangle"
text="Tabs"
onClick={() => setShown(true)}
data-tab-count={mainNoteContexts.length > 99 ? "∞" : mainNoteContexts.length}
/>
{createPortal(<TabBarModal mainNoteContexts={mainNoteContexts} shown={shown} setShown={setShown} />, document.body)}
</>
);
}
function TabBarModal({ mainNoteContexts, shown, setShown }: {
mainNoteContexts: NoteContext[];
shown: boolean;
setShown: (newValue: boolean) => void;
}) {
const [ fullyShown, setFullyShown ] = useState(false);
const selectTab = useCallback((noteContextToActivate: NoteContext) => {
appContext.tabManager.activateNoteContext(noteContextToActivate.ntxId);
setShown(false);
}, [ setShown ]);
return (
<Modal
className="tab-bar-modal"
size="xl"
title={t("mobile_tab_switcher.title", { count: mainNoteContexts.length})}
show={shown}
onShown={() => setFullyShown(true)}
customTitleBarButtons={[
{
iconClassName: "bx bx-dots-vertical-rounded",
title: t("mobile_tab_switcher.more_options"),
onClick(e) {
contextMenu.show<CommandNames>({
x: e.pageX,
y: e.pageY,
items: [
{ title: t("tab_row.new_tab"), command: "openNewTab", uiIcon: "bx bx-plus" },
{ title: t("tab_row.reopen_last_tab"), command: "reopenLastTab", uiIcon: "bx bx-undo", enabled: appContext.tabManager.recentlyClosedTabs.length !== 0 },
{ kind: "separator" },
{ title: t("tab_row.close_all_tabs"), command: "closeAllTabs", uiIcon: "bx bx-trash destructive-action-icon" },
],
selectMenuItemHandler: ({ command }) => {
if (command) {
appContext.triggerCommand(command);
}
}
});
},
}
]}
footer={<>
<LinkButton
text={t("tab_row.new_tab")}
onClick={() => {
appContext.triggerCommand("openNewTab");
setShown(false);
}}
/>
</>}
scrollable
onHidden={() => {
setShown(false);
setFullyShown(false);
}}
>
<TabBarModelContent mainNoteContexts={mainNoteContexts} selectTab={selectTab} shown={fullyShown} />
</Modal>
);
}
function TabBarModelContent({ mainNoteContexts, selectTab, shown }: {
mainNoteContexts: NoteContext[];
shown: boolean;
selectTab: (noteContextToActivate: NoteContext) => void;
}) {
const activeNoteContext = useActiveNoteContext();
const tabRefs = useRef<Record<string, HTMLDivElement | null>>({});
// Scroll to active tab.
useEffect(() => {
if (!shown || !activeNoteContext?.ntxId) return;
const correspondingEl = tabRefs.current[activeNoteContext.ntxId];
requestAnimationFrame(() => {
correspondingEl?.scrollIntoView();
});
}, [ activeNoteContext, shown ]);
return (
<div className="tabs">
{mainNoteContexts.map((noteContext) => (
<Tab
key={noteContext.ntxId}
noteContext={noteContext}
activeNtxId={activeNoteContext.ntxId}
selectTab={selectTab}
containerRef={el => (tabRefs.current[noteContext.ntxId ?? ""] = el)}
/>
))}
</div>
);
}
function Tab({ noteContext, containerRef, selectTab, activeNtxId }: {
containerRef: (el: HTMLDivElement | null) => void;
noteContext: NoteContext;
selectTab: (noteContextToActivate: NoteContext) => void;
activeNtxId: string | null | undefined;
}) {
const { note } = noteContext;
const iconClass = useNoteIcon(note);
const colorClass = note?.getColorClass() || '';
const workspaceTabBackgroundColorHue = getWorkspaceTabBackgroundColorHue(noteContext);
const subContexts = noteContext.getSubContexts();
return (
<div
ref={containerRef}
class={clsx("tab-card", {
active: noteContext.ntxId === activeNtxId,
"with-hue": workspaceTabBackgroundColorHue !== undefined,
"with-split": subContexts.length > 1
})}
onClick={() => selectTab(noteContext)}
style={{
"--bg-hue": workspaceTabBackgroundColorHue
}}
>
{subContexts.map(subContext => (
<Fragment key={subContext.ntxId}>
<header className={colorClass}>
{subContext.note && <Icon icon={iconClass} />}
<span className="title">{subContext.note?.title ?? t("tab_row.new_tab")}</span>
{subContext.isMainContext() && <ActionButton
icon="bx bx-x"
text={t("tab_row.close_tab")}
onClick={(e) => {
// We are closing a tab, so we need to prevent propagation for click (activate tab).
e.stopPropagation();
appContext.tabManager.removeNoteContext(subContext.ntxId);
}}
/>}
</header>
<div className={clsx("tab-preview", `type-${subContext.note?.type ?? "empty"}`)}>
<TabPreviewContent note={subContext.note} />
</div>
</Fragment>
))}
</div>
);
}
function TabPreviewContent({ note }: {
note: FNote | null
}) {
if (!note) {
return <PreviewPlaceholder icon="bx bx-plus" />;
}
if (note.type === "book") {
return <PreviewPlaceholder icon={ICON_MAPPINGS[note.getLabelValue("viewType") ?? ""] ?? "bx bx-book"} />;
}
return (
<NoteContent
note={note}
highlightedTokens={undefined}
trim
includeArchivedNotes={false}
/>
);
}
function PreviewPlaceholder({ icon}: {
icon: string;
}) {
return (
<div className="preview-placeholder">
<Icon icon={icon} />
</div>
);
}
function getWorkspaceTabBackgroundColorHue(noteContext: NoteContext) {
if (!noteContext.hoistedNoteId) return;
const hoistedNote = froca.getNoteFromCache(noteContext.hoistedNoteId);
if (!hoistedNote) return;
const workspaceTabBackgroundColor = hoistedNote.getWorkspaceTabBackgroundColor();
if (!workspaceTabBackgroundColor) return;
try {
const parsedColor = parseColor(workspaceTabBackgroundColor);
if (!parsedColor) return;
return getHue(parsedColor);
} catch (e) {
// Colors are non-critical, simply ignore.
console.warn(e);
}
}
function useMainNoteContexts() {
const [ noteContexts, setNoteContexts ] = useState(appContext.tabManager.getMainNoteContexts());
useTriliumEvents([ "newNoteContextCreated", "noteContextRemoved" ] , () => {
setNoteContexts(appContext.tabManager.getMainNoteContexts());
});
return noteContexts;
}

View File

@@ -1,128 +1,74 @@
import { createPortal, useState } from "preact/compat";
import FNote, { NotePathRecord } from "../../entities/fnote";
import { useContext } from "preact/hooks";
import appContext, { CommandMappings } from "../../components/app_context";
import contextMenu, { MenuItem } from "../../menus/context_menu";
import branches from "../../services/branches";
import { t } from "../../services/i18n";
import note_create from "../../services/note_create";
import { BacklinksList, useBacklinkCount } from "../FloatingButtonsDefinitions";
import tree from "../../services/tree";
import ActionButton from "../react/ActionButton";
import { FormDropdownDivider, FormListItem } from "../react/FormList";
import { useNoteContext } from "../react/hooks";
import Modal from "../react/Modal";
import { NoteContextMenu } from "../ribbon/NoteActions";
import NoteActionsCustom from "../ribbon/NoteActionsCustom";
import { NotePathsWidget, useSortedNotePaths } from "../ribbon/NotePathsTab";
import { ParentComponent } from "../react/react_utils";
import BasicWidget from "../basic_widget";
export default function MobileDetailMenu() {
const { note, noteContext, parentComponent, ntxId, viewScope, hoistedNoteId } = useNoteContext();
const subContexts = noteContext?.getMainContext().getSubContexts() ?? [];
const isMainContext = noteContext?.isMainContext();
const [ backlinksModalShown, setBacklinksModalShown ] = useState(false);
const [ notePathsModalShown, setNotePathsModalShown ] = useState(false);
const sortedNotePaths = useSortedNotePaths(note, hoistedNoteId);
const backlinksCount = useBacklinkCount(note, viewScope?.viewMode === "default");
function closePane() {
// Wait first for the context menu to be dismissed, otherwise the backdrop stays on.
requestAnimationFrame(() => {
parentComponent.triggerCommand("closeThisNoteSplit", { ntxId });
});
}
const parentComponent = useContext(ParentComponent);
return (
<div style={{ contain: "none" }}>
{note ? (
<NoteContextMenu
note={note} noteContext={noteContext}
extraItems={<>
<div className="form-list-row">
<div className="form-list-col">
<FormListItem
icon="bx bx-link"
onClick={() => setBacklinksModalShown(true)}
disabled={backlinksCount === 0}
>{t("status_bar.backlinks", { count: backlinksCount })}</FormListItem>
</div>
<div className="form-list-col">
<FormListItem
icon="bx bx-directions"
onClick={() => setNotePathsModalShown(true)}
disabled={(sortedNotePaths?.length ?? 0) <= 1}
>{t("status_bar.note_paths", { count: sortedNotePaths?.length })}</FormListItem>
</div>
</div>
<FormDropdownDivider />
<ActionButton
icon="bx bx-dots-vertical-rounded"
text=""
onClick={(e) => {
const ntxId = (parentComponent as BasicWidget | null)?.getClosestNtxId();
if (!ntxId) return;
{noteContext && ntxId && <NoteActionsCustom note={note} noteContext={noteContext} ntxId={ntxId} />}
<FormListItem
onClick={() => noteContext?.notePath && note_create.createNote(noteContext.notePath)}
icon="bx bx-plus"
>{t("mobile_detail_menu.insert_child_note")}</FormListItem>
{subContexts.length < 2 && <>
<FormDropdownDivider />
<FormListItem
onClick={() => parentComponent.triggerCommand("openNewNoteSplit", { ntxId })}
icon="bx bx-dock-right"
>{t("create_pane_button.create_new_split")}</FormListItem>
</>}
{!isMainContext && <>
<FormDropdownDivider />
<FormListItem
icon="bx bx-x"
onClick={closePane}
>{t("close_pane_button.close_this_pane")}</FormListItem>
</>}
<FormDropdownDivider />
</>}
/>
) : (
<ActionButton
icon="bx bx-x"
onClick={closePane}
text={t("close_pane_button.close_this_pane")}
/>
)}
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
const subContexts = noteContext.getMainContext().getSubContexts();
const isMainContext = noteContext?.isMainContext();
const note = noteContext.note;
{createPortal((
<>
<BacklinksModal note={note} modalShown={backlinksModalShown} setModalShown={setBacklinksModalShown} />
<NotePathsModal note={note} modalShown={notePathsModalShown} notePath={noteContext?.notePath} sortedNotePaths={sortedNotePaths} setModalShown={setNotePathsModalShown} />
</>
), document.body)}
</div>
);
}
function BacklinksModal({ note, modalShown, setModalShown }: { note: FNote | null | undefined, modalShown: boolean, setModalShown: (shown: boolean) => void }) {
return (
<Modal
className="backlinks-modal tn-backlinks-widget"
size="md"
title={t("mobile_detail_menu.backlinks")}
show={modalShown}
onHidden={() => setModalShown(false)}
>
<ul className="backlinks-items">
{note && <BacklinksList note={note} />}
</ul>
</Modal>
);
}
function NotePathsModal({ note, modalShown, notePath, sortedNotePaths, setModalShown }: { note: FNote | null | undefined, modalShown: boolean, sortedNotePaths: NotePathRecord[] | undefined, notePath: string | null | undefined, setModalShown: (shown: boolean) => void }) {
return (
<Modal
className="note-paths-modal"
size="md"
title={t("note_paths.title")}
show={modalShown}
onHidden={() => setModalShown(false)}
>
{note && (
<NotePathsWidget
sortedNotePaths={sortedNotePaths}
currentNotePath={notePath}
/>
)}
</Modal>
);
const items: (MenuItem<keyof CommandMappings>)[] = [
{ 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" },
{ kind: "separator" },
{ title: t("mobile_detail_menu.note_revisions"), command: "showRevisions", uiIcon: "bx bx-history" },
{ kind: "separator" },
subContexts.length < 2 && { title: t("create_pane_button.create_new_split"), command: "openNewNoteSplit", uiIcon: "bx bx-dock-right" },
!isMainContext && { title: t("close_pane_button.close_this_pane"), command: "closeThisNoteSplit", uiIcon: "bx bx-x" }
].filter(i => !!i) as MenuItem<keyof CommandMappings>[];
const lastItem = items.at(-1);
if (lastItem && "kind" in lastItem && lastItem.kind === "separator") {
items.pop();
}
contextMenu.show<keyof CommandMappings>({
x: e.pageX,
y: e.pageY,
items,
selectMenuItemHandler: async ({ command }) => {
if (command === "insertChildNote") {
note_create.createNote(appContext.tabManager.getActiveContextNotePath() ?? undefined);
} else if (command === "delete") {
const notePath = appContext.tabManager.getActiveContextNotePath();
if (!notePath) {
throw new Error("Cannot get note path to delete.");
}
const branchId = await tree.getBranchIdFromUrl(notePath);
if (!branchId) {
throw new Error(t("mobile_detail_menu.error_cannot_get_branch_id", { notePath }));
}
if (await branches.deleteNotes([branchId]) && parentComponent) {
parentComponent.triggerCommand("setActiveScreen", { screen: "tree" });
}
} else if (command && parentComponent) {
parentComponent.triggerCommand(command, { ntxId });
}
},
forcePositionOnMobile: true
});
}}
/>
)
}

View File

@@ -1,5 +1,5 @@
.collection-properties {
padding: 0.55em 12px;
padding: 0;
display: flex;
gap: 0.25em;
align-items: center;
@@ -14,31 +14,7 @@
}
}
>div {
display: inline-flex;
align-items: center;
gap: 0.5em;
}
.center-container {
.spacer {
flex-grow: 1;
justify-content: center;
}
.right-container {
justify-content: flex-end;
}
button.btn {
min-width: 0;
}
@media (max-width: 991px) {
flex-wrap: wrap;
padding: 0.55em 1em;
>div {
flex-grow: 1;
}
}
}

View File

@@ -1,22 +1,24 @@
import "./CollectionProperties.css";
import { t } from "i18next";
import { ComponentChildren } from "preact";
import { useContext, useRef } from "preact/hooks";
import { Fragment } from "preact/jsx-runtime";
import FNote from "../../entities/fnote";
import { getHelpUrlForNote } from "../../services/in_app_help";
import { openInAppHelpFromUrl } from "../../services/utils";
import { ViewTypeOptions } from "../collections/interface";
import ActionButton from "../react/ActionButton";
import Dropdown from "../react/Dropdown";
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList";
import FormTextBox from "../react/FormTextBox";
import { useNoteLabel, useNoteLabelBoolean, useNoteLabelWithDefault, useNoteProperty, useTriliumEvent } from "../react/hooks";
import { useNoteLabel, useNoteLabelBoolean, useNoteLabelWithDefault, useTriliumEvent } from "../react/hooks";
import Icon from "../react/Icon";
import { ParentComponent } from "../react/react_utils";
import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config";
import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab";
export const ICON_MAPPINGS: Record<ViewTypeOptions, string> = {
const ICON_MAPPINGS: Record<ViewTypeOptions, string> = {
grid: "bx bxs-grid",
list: "bx bx-list-ul",
calendar: "bx bx-calendar",
@@ -26,26 +28,15 @@ export const ICON_MAPPINGS: Record<ViewTypeOptions, string> = {
presentation: "bx bx-rectangle"
};
export default function CollectionProperties({ note, centerChildren, rightChildren }: {
note: FNote;
centerChildren?: ComponentChildren;
rightChildren?: ComponentChildren;
}) {
export default function CollectionProperties({ note }: { note: FNote }) {
const [ viewType, setViewType ] = useViewType(note);
const noteType = useNoteProperty(note, "type");
return ([ "book", "search" ].includes(noteType ?? "") &&
return (
<div className="collection-properties">
<div className="left-container">
<ViewTypeSwitcher viewType={viewType} setViewType={setViewType} />
<ViewOptions note={note} viewType={viewType} />
</div>
<div className="center-container">
{centerChildren}
</div>
<div className="right-container">
{rightChildren}
</div>
<ViewTypeSwitcher viewType={viewType} setViewType={setViewType} />
<ViewOptions note={note} viewType={viewType} />
<div className="spacer" />
<HelpButton note={note} />
</div>
);
}
@@ -231,3 +222,15 @@ function CheckBoxPropertyView({ note, property }: { note: FNote, property: Check
/>
);
}
function HelpButton({ note }: { note: FNote }) {
const helpUrl = getHelpUrlForNote(note);
return (helpUrl && (
<ActionButton
icon="bx bx-help-circle"
onClick={(() => openInAppHelpFromUrl(helpUrl))}
text={t("help-button.title")}
/>
));
}

View File

@@ -117,35 +117,3 @@ body.experimental-feature-new-layout {
}
}
}
body.mobile .modal.icon-switcher {
.modal-dialog {
left: 0;
right: 0;
margin: unset;
transform: unset;
max-width: 100%;
height: 100%;
}
.modal-body {
padding: 0;
display: flex;
flex-direction: column;
> .filter-row {
padding: 0.25em var(--bs-modal-padding) 0.5em var(--bs-modal-padding);
border-bottom: 1px solid var(--main-border-color);
}
}
.icon-list {
margin: auto;
flex-grow: 1;
height: 100%;
span {
padding: 12px;
}
}
}

View File

@@ -4,24 +4,18 @@ import { IconRegistry } from "@triliumnext/commons";
import { Dropdown as BootstrapDropdown } from "bootstrap";
import clsx from "clsx";
import { t } from "i18next";
import { CSSProperties } from "preact";
import { createPortal } from "preact/compat";
import { CSSProperties, RefObject } from "preact";
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
import type React from "react";
import { CellComponentProps, Grid } from "react-window";
import FNote from "../entities/fnote";
import attributes from "../services/attributes";
import server from "../services/server";
import { isDesktop, isMobile } from "../services/utils";
import ActionButton from "./react/ActionButton";
import Dropdown from "./react/Dropdown";
import { FormDropdownDivider, FormListItem } from "./react/FormList";
import FormTextBox from "./react/FormTextBox";
import { useNoteContext, useNoteLabel, useStaticTooltip, useWindowSize } from "./react/hooks";
import Modal from "./react/Modal";
const ICON_SIZE = isMobile() ? 56 : 48;
import { useNoteContext, useNoteLabel, useStaticTooltip } from "./react/hooks";
interface IconToCountCache {
iconClassToCountMap: Record<string, number>;
@@ -42,10 +36,6 @@ export default function NoteIcon() {
setIcon(note?.getIcon());
}, [ note, iconClass, workspaceIconClass ]);
if (isMobile()) {
return <MobileNoteIconSwitcher note={note} icon={icon} />;
}
return (
<Dropdown
className="note-icon-widget"
@@ -57,47 +47,16 @@ export default function NoteIcon() {
hideToggleArrow
disabled={viewScope?.viewMode !== "default"}
>
{ note && <NoteIconList note={note} onHide={() => dropdownRef?.current?.hide()} columnCount={12} /> }
{ note && <NoteIconList note={note} dropdownRef={dropdownRef} /> }
</Dropdown>
);
}
function MobileNoteIconSwitcher({ note, icon }: {
note: FNote | null | undefined;
icon: string | null | undefined;
}) {
const [ modalShown, setModalShown ] = useState(false);
const { windowWidth } = useWindowSize();
return (note &&
<div className="note-icon-widget">
<ActionButton
className="note-icon"
icon={icon ?? "bx bx-empty"}
text={t("note_icon.change_note_icon")}
onClick={() => setModalShown(true)}
/>
{createPortal((
<Modal
title={t("note_icon.change_note_icon")}
size="xl"
show={modalShown} onHidden={() => setModalShown(false)}
className="icon-switcher note-icon-widget"
scrollable
>
<NoteIconList note={note} onHide={() => setModalShown(false)} columnCount={Math.max(1, Math.floor(windowWidth / ICON_SIZE))} />
</Modal>
), document.body)}
</div>
);
}
function NoteIconList({ note, onHide, columnCount }: {
note: FNote;
onHide: () => void;
columnCount: number;
function NoteIconList({ note, dropdownRef }: {
note: FNote,
dropdownRef: RefObject<BootstrapDropdown>;
}) {
const searchBoxRef = useRef<HTMLInputElement>(null);
const iconListRef = useRef<HTMLDivElement>(null);
const [ search, setSearch ] = useState<string>();
const [ filterByPrefix, setFilterByPrefix ] = useState<string | null>(null);
@@ -113,22 +72,53 @@ function NoteIconList({ note, onHide, columnCount }: {
return (
<>
<FilterRow
note={note}
filterByPrefix={filterByPrefix}
search={search}
setSearch={setSearch}
setFilterByPrefix={setFilterByPrefix}
filteredIcons={filteredIcons}
onHide={onHide}
/>
<div class="filter-row">
<span>{t("note_icon.search")}</span>
<FormTextBox
inputRef={searchBoxRef}
type="text"
name="icon-search"
placeholder={ filterByPrefix
? t("note_icon.search_placeholder_filtered", {
number: filteredIcons.length ?? 0,
name: glob.iconRegistry.sources.find(s => s.prefix === filterByPrefix)?.name ?? ""
})
: t("note_icon.search_placeholder", { number: filteredIcons.length ?? 0, count: glob.iconRegistry.sources.length })}
currentValue={search} onChange={setSearch}
autoFocus
/>
{getIconLabels(note).length > 0 && (
<div style={{ textAlign: "center" }}>
<ActionButton
icon="bx bx-reset"
text={t("note_icon.reset-default")}
onClick={() => {
if (!note) return;
for (const label of getIconLabels(note)) {
attributes.removeAttributeById(note.noteId, label.attributeId);
}
dropdownRef?.current?.hide();
}}
/>
</div>
)}
{glob.iconRegistry.sources.length > 0 && <Dropdown
buttonClassName="bx bx-filter-alt"
hideToggleArrow
noSelectButtonStyle
noDropdownListStyle
iconAction
title={t("note_icon.filter")}
>
<IconFilterContent filterByPrefix={filterByPrefix} setFilterByPrefix={setFilterByPrefix} />
</Dropdown>}
</div>
<div
class="icon-list"
ref={iconListRef}
style={{
width: (columnCount * ICON_SIZE + 10),
}}
onClick={(e) => {
// Make sure we are not clicking on something else than a button.
const clickedTarget = e.target as HTMLElement;
@@ -139,19 +129,18 @@ function NoteIconList({ note, onHide, columnCount }: {
const attributeToSet = note.hasOwnedLabel("workspace") ? "workspaceIconClass" : "iconClass";
attributes.setLabel(note.noteId, attributeToSet, iconClass);
}
onHide();
dropdownRef?.current?.hide();
}}
>
{filteredIcons.length ? (
<Grid
columnCount={columnCount}
columnWidth={ICON_SIZE}
rowCount={Math.ceil(filteredIcons.length / columnCount)}
rowHeight={ICON_SIZE}
columnCount={12}
columnWidth={48}
rowCount={Math.ceil(filteredIcons.length / 12)}
rowHeight={48}
cellComponent={IconItemCell}
cellProps={{
filteredIcons,
columnCount
filteredIcons
}}
/>
) : (
@@ -162,97 +151,12 @@ function NoteIconList({ note, onHide, columnCount }: {
);
}
function FilterRow({ note, filterByPrefix, search, setSearch, setFilterByPrefix, filteredIcons, onHide }: {
note: FNote;
filterByPrefix: string | null;
search: string | undefined;
setSearch: (value: string | undefined) => void;
setFilterByPrefix: (value: string | null) => void;
function IconItemCell({ rowIndex, columnIndex, style, filteredIcons }: CellComponentProps<{
filteredIcons: IconWithName[];
onHide: () => void;
}) {
const searchBoxRef = useRef<HTMLInputElement>(null);
const hasCustomIcon = getIconLabels(note).length > 0;
function resetToDefaultIcon() {
if (!note) return;
for (const label of getIconLabels(note)) {
attributes.removeAttributeById(note.noteId, label.attributeId);
}
onHide();
}
return (
<div class="filter-row">
<span>{t("note_icon.search")}</span>
<FormTextBox
inputRef={searchBoxRef}
type="text"
name="icon-search"
placeholder={ filterByPrefix
? t("note_icon.search_placeholder_filtered", {
number: filteredIcons.length ?? 0,
name: glob.iconRegistry.sources.find(s => s.prefix === filterByPrefix)?.name ?? ""
})
: t("note_icon.search_placeholder", { number: filteredIcons.length ?? 0, count: glob.iconRegistry.sources.length })}
currentValue={search} onChange={setSearch}
autoFocus
/>
{isDesktop()
? <>
{hasCustomIcon && (
<div style={{ textAlign: "center" }}>
<ActionButton
icon="bx bx-reset"
text={t("note_icon.reset-default")}
onClick={resetToDefaultIcon}
/>
</div>
)}
{<Dropdown
buttonClassName="bx bx-filter-alt"
hideToggleArrow
noSelectButtonStyle
noDropdownListStyle
iconAction
title={t("note_icon.filter")}
>
<IconFilterContent filterByPrefix={filterByPrefix} setFilterByPrefix={setFilterByPrefix} />
</Dropdown>}
</> : (
<Dropdown
buttonClassName="bx bx-dots-vertical-rounded"
hideToggleArrow
noSelectButtonStyle
noDropdownListStyle
iconAction
dropdownContainerClassName="mobile-bottom-menu"
>
{hasCustomIcon && <>
<FormListItem
icon="bx bx-reset"
onClick={resetToDefaultIcon}
disabled={!hasCustomIcon}
>{t("note_icon.reset-default")}</FormListItem>
<FormDropdownDivider />
</>}
<IconFilterContent filterByPrefix={filterByPrefix} setFilterByPrefix={setFilterByPrefix} />
</Dropdown>
)}
</div>
);
}
function IconItemCell({ rowIndex, columnIndex, style, filteredIcons, columnCount }: CellComponentProps<{
filteredIcons: IconWithName[];
columnCount: number;
}>) {
const iconIndex = rowIndex * columnCount + columnIndex;
}>): React.JSX.Element {
const iconIndex = rowIndex * 12 + columnIndex;
const iconData = filteredIcons[iconIndex] as IconWithName | undefined;
if (!iconData) return <></> as React.ReactElement;
if (!iconData) return <></>;
const { id, terms, iconPack } = iconData;
return (
@@ -262,7 +166,7 @@ function IconItemCell({ rowIndex, columnIndex, style, filteredIcons, columnCount
title={t("note_icon.icon_tooltip", { name: terms?.[0] ?? id, iconPack })}
style={style as CSSProperties}
/>
) as React.ReactElement;
);
}
function IconFilterContent({ filterByPrefix, setFilterByPrefix }: {
@@ -279,7 +183,7 @@ function IconFilterContent({ filterByPrefix, setFilterByPrefix }: {
checked={filterByPrefix === "bx"}
onClick={() => setFilterByPrefix("bx")}
>{t("note_icon.filter-default")}</FormListItem>
{glob.iconRegistry.sources.length > 1 && <FormDropdownDivider />}
<FormDropdownDivider />
{glob.iconRegistry.sources.map(({ prefix, name, icon }) => (
prefix !== "bx" && <FormListItem

View File

@@ -109,29 +109,4 @@ body.experimental-feature-new-layout {
--input-focus-color: initial;
}
}
&.mobile .title-row {
.icon-action:not(.note-icon) {
--icon-button-size: 45px;
--icon-button-icon-ratio: 0.5;
flex-shrink: 0;
}
.note-actions {
width: auto;
}
.note-badges {
margin-inline: 0.5em;
flex-shrink: 0;
}
.note-icon-widget {
margin-inline: 0.5em;
.note-icon {
--icon-button-size: 24px;
}
}
}
}

View File

@@ -1232,9 +1232,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
refreshCtx.noteIdsToUpdate.add(noteId);
}
const hasNotesToUpdateOrReload = refreshCtx.noteIdsToUpdate.size + refreshCtx.noteIdsToReload.size > 0;
const hasNoteReorderingChange = loadResults.getNoteReorderings().length > 0;
if (hasNotesToUpdateOrReload || hasNoteReorderingChange) {
if (refreshCtx.noteIdsToUpdate.size + refreshCtx.noteIdsToReload.size > 0) {
await this.#executeTreeUpdates(refreshCtx, loadResults);
}
@@ -1395,7 +1393,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
for (const parentNoteId of loadResults.getNoteReorderings()) {
for (const node of this.getNodesByNoteId(parentNoteId)) {
console.log("Reordering ", node);
if (node.isLoaded()) {
this.sortChildren(node);
}

View File

@@ -12,7 +12,7 @@ import { TypeWidgetProps } from "./type_widgets/type_widget";
* 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.
*/
export type ExtendedNoteType = Exclude<NoteType, "launcher" | "text" | "code"> | "empty" | "readOnlyCode" | "readOnlyText" | "editableText" | "editableCode" | "attachmentDetail" | "attachmentList" | "protectedSession" | "aiChat" | "sqlConsole";
export type ExtendedNoteType = Exclude<NoteType, "launcher" | "text" | "code"> | "empty" | "readOnlyCode" | "readOnlyText" | "editableText" | "editableCode" | "attachmentDetail" | "attachmentList" | "protectedSession" | "aiChat";
export type TypeWidget = ((props: TypeWidgetProps) => VNode | JSX.Element | undefined);
type NoteTypeView = () => (Promise<{ default: TypeWidget } | TypeWidget> | TypeWidget);
@@ -140,10 +140,5 @@ export const TYPE_MAPPINGS: Record<ExtendedNoteType, NoteTypeMapping> = {
view: () => import("./type_widgets/AiChat"),
className: "ai-chat-widget-container",
isFullHeight: true
},
sqlConsole: {
view: () => import("./type_widgets/SqlConsole"),
className: "sql-console-widget-container",
isFullHeight: true
}
};

View File

@@ -1,11 +1,10 @@
import { HTMLAttributes } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { CommandNames } from "../../components/app_context";
import keyboard_actions from "../../services/keyboard_actions";
import { useStaticTooltip } from "./hooks";
import keyboard_actions from "../../services/keyboard_actions";
import { HTMLAttributes } from "preact";
export interface ActionButtonProps extends Pick<HTMLAttributes<HTMLButtonElement>, "onClick" | "onAuxClick" | "onContextMenu" | "style"> {
export interface ActionButtonProps extends Pick<HTMLAttributes<HTMLButtonElement>, "onClick" | "onAuxClick" | "onContextMenu"> {
text: string;
titlePosition?: "top" | "right" | "bottom" | "left";
icon: string;

View File

@@ -1,11 +1,8 @@
import type { ComponentChildren, RefObject } from "preact";
import type { CSSProperties } from "preact/compat";
import { memo } from "preact/compat";
import { useMemo } from "preact/hooks";
import { memo } from "preact/compat";
import { CommandNames } from "../../components/app_context";
import { isDesktop } from "../../services/utils";
import ActionButton from "./ActionButton";
import Icon from "./Icon";
export interface ButtonProps {
@@ -81,7 +78,7 @@ export function ButtonGroup({ children }: { children: ComponentChildren }) {
<div className="btn-group" role="group">
{children}
</div>
);
)
}
export function SplitButton({ text, icon, children, ...restProps }: {
@@ -106,17 +103,7 @@ export function SplitButton({ text, icon, children, ...restProps }: {
{children}
</ul>
</ButtonGroup>
);
}
export function ButtonOrActionButton(props: {
text: string;
icon: string;
} & Pick<ButtonProps, "onClick" | "triggerCommand" | "disabled" | "title">) {
if (isDesktop()) {
return <Button {...props} />;
}
return <ActionButton {...props} />;
)
}
export default Button;

View File

@@ -17,34 +17,21 @@
.collapsible-body {
height: 0;
overflow: hidden;
&.fully-expanded {
overflow: visible;
}
}
.collapsible-inner-body {
padding-top: 0.5em;
opacity: 0;
}
&.expanded {
.collapsible-title .arrow {
transform: rotate(90deg);
}
.collapsible-inner-body {
opacity: 1;
}
}
&.with-transition {
.collapsible-body {
transition: height 250ms ease-in;
}
.collapsible-inner-body {
transition: opacity 250ms ease-in;
}
}
}

View File

@@ -27,7 +27,6 @@ export function ExternallyControlledCollapsible({ title, children, className, ex
const { height } = useElementSize(innerRef) ?? {};
const contentId = useUniqueName();
const [ transitionEnabled, setTransitionEnabled ] = useState(false);
const [ fullyExpanded, setFullyExpanded ] = useState(false);
useEffect(() => {
const timeout = setTimeout(() => {
@@ -36,21 +35,6 @@ export function ExternallyControlledCollapsible({ title, children, className, ex
return () => clearTimeout(timeout);
}, []);
useEffect(() => {
if (expanded) {
if (transitionEnabled) {
const timeout = setTimeout(() => {
setFullyExpanded(true);
}, 250);
return () => clearTimeout(timeout);
} else {
setFullyExpanded(true);
}
} else {
setFullyExpanded(false);
}
}, [expanded, transitionEnabled])
return (
<div className={clsx("collapsible", className, {
expanded,
@@ -69,7 +53,7 @@ export function ExternallyControlledCollapsible({ title, children, className, ex
<div
id={contentId}
ref={bodyRef}
className={clsx("collapsible-body", {"fully-expanded": fullyExpanded})}
className="collapsible-body"
style={{ height: expanded ? height : "0" }}
aria-hidden={!expanded}
>

View File

@@ -3,7 +3,6 @@ import { ComponentChildren, HTMLAttributes } from "preact";
import { CSSProperties, HTMLProps } from "preact/compat";
import { MutableRef, useCallback, useEffect, useRef, useState } from "preact/hooks";
import { isMobile } from "../../services/utils";
import { useTooltip, useUniqueName } from "./hooks";
type DataAttributes = {
@@ -33,10 +32,9 @@ export interface DropdownProps extends Pick<HTMLProps<HTMLDivElement>, "id" | "c
dropdownRef?: MutableRef<BootstrapDropdown | null>;
titlePosition?: "top" | "right" | "bottom" | "left";
titleOptions?: Partial<Tooltip.Options>;
mobileBackdrop?: boolean;
}
export default function Dropdown({ id, className, buttonClassName, isStatic, children, title, text, dropdownContainerStyle, dropdownContainerClassName, dropdownContainerRef: externalContainerRef, hideToggleArrow, iconAction, disabled, noSelectButtonStyle, noDropdownListStyle, forceShown, onShown: externalOnShown, onHidden: externalOnHidden, dropdownOptions, buttonProps, dropdownRef, titlePosition, titleOptions, mobileBackdrop }: DropdownProps) {
export default function Dropdown({ id, className, buttonClassName, isStatic, children, title, text, dropdownContainerStyle, dropdownContainerClassName, dropdownContainerRef: externalContainerRef, hideToggleArrow, iconAction, disabled, noSelectButtonStyle, noDropdownListStyle, forceShown, onShown: externalOnShown, onHidden: externalOnHidden, dropdownOptions, buttonProps, dropdownRef, titlePosition, titleOptions }: DropdownProps) {
const containerRef = useRef<HTMLDivElement | null>(null);
const triggerRef = useRef<HTMLButtonElement | null>(null);
const dropdownContainerRef = useRef<HTMLUListElement | null>(null);
@@ -76,18 +74,12 @@ export default function Dropdown({ id, className, buttonClassName, isStatic, chi
setShown(true);
externalOnShown?.();
hideTooltip();
if (mobileBackdrop && isMobile()) {
document.getElementById("context-menu-cover")?.classList.add("show", "global-menu-cover");
}
}, [ hideTooltip, mobileBackdrop ]);
}, [ hideTooltip ]);
const onHidden = useCallback(() => {
setShown(false);
externalOnHidden?.();
if (mobileBackdrop && isMobile()) {
document.getElementById("context-menu-cover")?.classList.remove("show", "global-menu-cover");
}
}, [ mobileBackdrop ]);
}, []);
useEffect(() => {
if (!containerRef.current) return;

View File

@@ -3,9 +3,8 @@ import { useEffect, useRef } from "preact/hooks";
import ActionButton, { ActionButtonProps } from "./ActionButton";
import Button, { ButtonProps } from "./Button";
import { FormListItem, FormListItemOpts } from "./FormList";
export interface FormFileUploadProps {
interface FormFileUploadProps {
name?: string;
onChange: (files: FileList | null) => void;
multiple?: boolean;
@@ -76,25 +75,3 @@ export function FormFileUploadActionButton({ onChange, ...buttonProps }: Omit<Ac
</>
);
}
/**
* Similar to {@link FormFileUploadButton}, but uses an {@link FormListItem} instead of a normal {@link Button}.
* @param param the change listener for the file upload and the properties for the button.
*/
export function FormFileUploadFormListItem({ onChange, children, ...buttonProps }: Omit<FormListItemOpts, "onClick"> & Pick<FormFileUploadProps, "onChange">) {
const inputRef = useRef<HTMLInputElement>(null);
return (
<>
<FormListItem
{...buttonProps}
onClick={() => inputRef.current?.click()}
>{children}</FormListItem>
<FormFileUpload
inputRef={inputRef}
hidden
onChange={onChange}
/>
</>
);
}

View File

@@ -27,13 +27,3 @@
}
}
}
.form-list-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1em;
.form-list-col {
flex-grow: 1;
}
}

View File

@@ -78,7 +78,7 @@ export interface FormListBadge {
text: string;
}
export interface FormListItemOpts {
interface FormListItemOpts {
children: ComponentChildren;
icon?: string;
value?: string;

Some files were not shown because too many files have changed in this diff Show More