Merge branch 'TriliumNext:main' into main
@ -9,7 +9,7 @@
|
||||
"keywords": [],
|
||||
"author": "Elian Doran <contact@eliandoran.me>",
|
||||
"license": "AGPL-3.0-only",
|
||||
"packageManager": "pnpm@10.28.0",
|
||||
"packageManager": "pnpm@10.28.1",
|
||||
"devDependencies": {
|
||||
"@redocly/cli": "2.14.5",
|
||||
"archiver": "7.0.1",
|
||||
|
||||
@ -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.5.1",
|
||||
"@preact/signals": "2.6.0",
|
||||
"@triliumnext/ckeditor5": "workspace:*",
|
||||
"@triliumnext/codemirror": "workspace:*",
|
||||
"@triliumnext/commons": "workspace:*",
|
||||
"@triliumnext/highlightjs": "workspace:*",
|
||||
"@triliumnext/share-theme": "workspace:*",
|
||||
"@triliumnext/split.js": "workspace:*",
|
||||
"@zumer/snapdom": "2.0.1",
|
||||
"@zumer/snapdom": "2.0.2",
|
||||
"autocomplete.js": "0.38.1",
|
||||
"bootstrap": "5.3.8",
|
||||
"boxicons": "2.1.4",
|
||||
@ -44,9 +44,9 @@
|
||||
"draggabilly": "3.0.0",
|
||||
"force-graph": "1.51.0",
|
||||
"globals": "17.0.0",
|
||||
"i18next": "25.7.4",
|
||||
"i18next": "25.8.0",
|
||||
"i18next-http-backend": "3.0.2",
|
||||
"jquery": "3.7.1",
|
||||
"jquery": "4.0.0",
|
||||
"jquery.fancytree": "2.38.5",
|
||||
"jsplumb": "2.15.6",
|
||||
"katex": "0.16.27",
|
||||
@ -56,7 +56,7 @@
|
||||
"mark.js": "8.11.1",
|
||||
"marked": "17.0.1",
|
||||
"mermaid": "11.12.2",
|
||||
"mind-elixir": "5.5.0",
|
||||
"mind-elixir": "5.6.1",
|
||||
"normalize.css": "8.0.1",
|
||||
"panzoom": "9.4.3",
|
||||
"preact": "10.28.2",
|
||||
@ -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.3.0",
|
||||
"lightningcss": "1.30.2",
|
||||
"happy-dom": "20.3.4",
|
||||
"lightningcss": "1.31.1",
|
||||
"script-loader": "0.7.2",
|
||||
"vite-plugin-static-copy": "3.1.4"
|
||||
"vite-plugin-static-copy": "3.1.5"
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import type { CKTextEditor } from "@triliumnext/ckeditor5";
|
||||
import type CodeMirror from "@triliumnext/codemirror";
|
||||
import { SqlExecuteResults } from "@triliumnext/commons";
|
||||
import { SqlExecuteResponse } 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 & {
|
||||
results: SqlExecuteResults;
|
||||
response: SqlExecuteResponse;
|
||||
};
|
||||
readOnlyTemporarilyDisabled: {
|
||||
noteContext: NoteContext;
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
import utils from "../services/utils.js";
|
||||
import { CreateChildrenResponse, SqlExecuteResponse } from "@triliumnext/commons";
|
||||
|
||||
import bundleService from "../services/bundle.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() {
|
||||
@ -187,13 +188,8 @@ 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 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 });
|
||||
const response = await server.post<SqlExecuteResponse>(`sql/execute/${note.noteId}`);
|
||||
await appContext.triggerEvent("sqlQueryResults", { ntxId, response });
|
||||
}
|
||||
|
||||
toastService.showMessage(t("entrypoints.note-executed"));
|
||||
|
||||
@ -16,6 +16,17 @@ 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() {
|
||||
@ -39,22 +50,25 @@ async function loadBootstrapCss() {
|
||||
}
|
||||
|
||||
function loadStylesheets() {
|
||||
const { assetPath, themeCssUrl, themeUseNextAsBase } = window.glob;
|
||||
const { device, assetPath, themeCssUrl, themeUseNextAsBase } = window.glob;
|
||||
|
||||
const cssToLoad: string[] = [];
|
||||
cssToLoad.push(`${assetPath}/stylesheets/ckeditor-theme.css`);
|
||||
cssToLoad.push(`api/fonts`);
|
||||
cssToLoad.push(`${assetPath}/stylesheets/theme-light.css`);
|
||||
if (themeCssUrl) {
|
||||
cssToLoad.push(themeCssUrl);
|
||||
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`);
|
||||
}
|
||||
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");
|
||||
@ -91,10 +105,17 @@ function setBodyAttributes() {
|
||||
}
|
||||
|
||||
async function loadScripts() {
|
||||
if (glob.device === "mobile") {
|
||||
await import("./mobile.js");
|
||||
} else {
|
||||
await import("./desktop.js");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -46,8 +46,6 @@ 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";
|
||||
@ -163,11 +161,9 @@ 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 />)
|
||||
|
||||
@ -29,7 +29,9 @@ async function main() {
|
||||
const froca = (await import("./services/froca")).default;
|
||||
const note = await froca.getNote(noteId);
|
||||
|
||||
render(<App note={note} noteId={noteId} />, document.body);
|
||||
const bodyWrapper = document.createElement("div");
|
||||
render(<App note={note} noteId={noteId} />, bodyWrapper);
|
||||
document.body.appendChild(bodyWrapper);
|
||||
}
|
||||
|
||||
function App({ note, noteId }: { note: FNote | null | undefined, noteId: string }) {
|
||||
|
||||
@ -8,6 +8,17 @@ 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();
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
||||
import shortcuts, { keyMatches, matchesShortcut, isIMEComposing } from "./shortcuts.js";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import shortcuts, { isIMEComposing, keyMatches, matchesShortcut } from "./shortcuts.js";
|
||||
|
||||
// Mock utils module
|
||||
vi.mock("./utils.js", () => ({
|
||||
|
||||
@ -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,10 +40,42 @@
|
||||
border-bottom: var(--col-header-bottom-border);
|
||||
background: var(--col-header-background-color);
|
||||
color: var(--col-header-text-color);
|
||||
}
|
||||
font-weight: normal;
|
||||
|
||||
.tabulator .tabulator-col-content {
|
||||
padding: 8px 4px !important;
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
@ -80,7 +112,6 @@
|
||||
|
||||
.tabulator-tableholder {
|
||||
padding-top: 10px;
|
||||
height: unset !important; /* Don't extend on the full height */
|
||||
}
|
||||
|
||||
/* Rows */
|
||||
@ -99,6 +130,14 @@
|
||||
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 {
|
||||
@ -120,11 +159,14 @@
|
||||
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);
|
||||
}
|
||||
@ -174,10 +216,6 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tabulator .tabulator-footer {
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
|
||||
/* Context menus */
|
||||
|
||||
.tabulator-popup-container {
|
||||
@ -192,8 +230,27 @@
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
:root .tabulator .tabulator-footer {
|
||||
border-top: unset;
|
||||
background: transparent;
|
||||
color: var(--main-text-color);
|
||||
border-top: 1px solid var(--main-border-color);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1816,7 +1816,11 @@
|
||||
"configure_launchbar": "Configure Launchbar"
|
||||
},
|
||||
"sql_result": {
|
||||
"no_rows": "No rows have been returned for this query"
|
||||
"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"
|
||||
},
|
||||
"sql_table_schemas": {
|
||||
"tables": "Tables"
|
||||
|
||||
@ -7,7 +7,6 @@ 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";
|
||||
@ -101,7 +100,8 @@ function SwitchSplitOrientationButton({ note, isReadOnly, isDefaultViewMode }: F
|
||||
|
||||
function ToggleReadOnlyButton({ note, viewType, isDefaultViewMode }: FloatingButtonContext) {
|
||||
const [ isReadOnly, setReadOnly ] = useNoteLabelBoolean(note, "readOnly");
|
||||
const isEnabled = ([ "mermaid", "mindMap", "canvas" ].includes(note.type) || viewType === "geoMap")
|
||||
const isSavedSqlite = note.isTriliumSqlite() && !note.isHiddenCompletely();
|
||||
const isEnabled = ([ "mermaid", "mindMap", "canvas" ].includes(note.type) || viewType === "geoMap" || isSavedSqlite)
|
||||
&& note.isContentAvailable() && isDefaultViewMode;
|
||||
|
||||
return isEnabled && <FloatingButton
|
||||
|
||||
@ -265,9 +265,13 @@ 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);
|
||||
@ -318,6 +322,8 @@ 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") {
|
||||
@ -342,9 +348,8 @@ 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 && !isSqlNote)
|
||||
return (!noteContext?.hasNoteList() && isFullHeightNoteType)
|
||||
|| noteContext?.viewScope?.viewMode === "attachments"
|
||||
|| isBackendNote;
|
||||
}
|
||||
@ -358,8 +363,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",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import "./NoteList.css";
|
||||
|
||||
import { WebSocketMessage } from "@triliumnext/commons";
|
||||
import { VNode } from "preact";
|
||||
import { Component, VNode } from "preact";
|
||||
import { lazy, Suspense } from "preact/compat";
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
@ -123,7 +123,9 @@ export function CustomNoteList({ note, viewType, isEnabled: shouldEnable, notePa
|
||||
}
|
||||
|
||||
const ComponentToRender = viewType && props && isEnabled && (
|
||||
props.media === "print" ? ViewComponents[viewType].print : ViewComponents[viewType].normal
|
||||
props.media === "print"
|
||||
? ViewComponents[viewType].print ?? ViewComponents[viewType].normal
|
||||
: ViewComponents[viewType].normal
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@ -4,6 +4,10 @@
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
padding: 0 5px 0 10px;
|
||||
|
||||
.tabulator-tableholder {
|
||||
height: unset !important;
|
||||
}
|
||||
}
|
||||
|
||||
.table-view-container {
|
||||
@ -68,4 +72,4 @@
|
||||
inset-inline-start: 0;
|
||||
font-size: 1.5em;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
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 { ParentComponent, renderReactWidget } from "../../react/react_utils";
|
||||
import { JSX } from "preact/jsx-runtime";
|
||||
|
||||
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";
|
||||
|
||||
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;
|
||||
}
|
||||
@ -43,7 +45,9 @@ export default function Tabulator<T>({ className, columns, data, modules, tabula
|
||||
|
||||
tabulator.on("tableBuilt", () => {
|
||||
tabulatorRef.current = tabulator;
|
||||
externalTabulatorRef.current = tabulator;
|
||||
if (externalTabulatorRef) {
|
||||
externalTabulatorRef.current = tabulator;
|
||||
}
|
||||
onReady?.();
|
||||
});
|
||||
|
||||
@ -62,12 +66,15 @@ 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(() => { columns && tabulatorRef.current?.setColumns(columns)}, [ data]);
|
||||
useEffect(() => { tabulatorRef.current?.setData(data); }, [ data ]);
|
||||
useEffect(() => {
|
||||
if (!columns) return;
|
||||
tabulatorRef.current?.setColumns(columns);
|
||||
}, [ columns ]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={className} />
|
||||
|
||||
@ -7,6 +7,7 @@ import { ComponentChild } from "preact";
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
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";
|
||||
@ -22,12 +23,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?.noteId, type, viewScope));
|
||||
const [ shown, setShown ] = useState(shouldShow(note, type, viewScope));
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [ titleHidden, setTitleHidden ] = useState(false);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setShown(shouldShow(note?.noteId, type, viewScope));
|
||||
setShown(shouldShow(note, type, viewScope));
|
||||
}, [ note, type, viewScope ]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
@ -69,9 +70,10 @@ export default function InlineTitle() {
|
||||
);
|
||||
}
|
||||
|
||||
function shouldShow(noteId: string | undefined, type: NoteType | undefined, viewScope: ViewScope | undefined) {
|
||||
function shouldShow(note: FNote | null | undefined, type: NoteType | undefined, viewScope: ViewScope | undefined) {
|
||||
if (viewScope?.viewMode !== "default") return false;
|
||||
if (noteId?.startsWith("_options")) return true;
|
||||
if (note?.noteId?.startsWith("_options")) return true;
|
||||
if (note?.isTriliumSqlite()) return false;
|
||||
return type && supportedNoteTypes.has(type);
|
||||
}
|
||||
|
||||
|
||||
@ -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) &&
|
||||
return (currentNoteType && supportedNoteTypes.has(currentNoteType) && !note?.isTriliumSqlite() &&
|
||||
<div
|
||||
className="note-type-switcher"
|
||||
onWheel={onWheelHorizontalScroll}
|
||||
|
||||
@ -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";
|
||||
export type ExtendedNoteType = Exclude<NoteType, "launcher" | "text" | "code"> | "empty" | "readOnlyCode" | "readOnlyText" | "editableText" | "editableCode" | "attachmentDetail" | "attachmentList" | "protectedSession" | "aiChat" | "sqlConsole";
|
||||
|
||||
export type TypeWidget = ((props: TypeWidgetProps) => VNode | JSX.Element | undefined);
|
||||
type NoteTypeView = () => (Promise<{ default: TypeWidget } | TypeWidget> | TypeWidget);
|
||||
@ -140,5 +140,10 @@ 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
|
||||
}
|
||||
};
|
||||
|
||||
18
apps/client/src/widgets/react/NoItems.css
Normal file
@ -0,0 +1,18 @@
|
||||
.no-items {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
padding: 0.75em;
|
||||
color: var(--muted-text-color);
|
||||
height: 100%;
|
||||
|
||||
.tn-icon {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
21
apps/client/src/widgets/react/NoItems.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import "./NoItems.css";
|
||||
|
||||
import { ComponentChildren } from "preact";
|
||||
|
||||
import Icon from "./Icon";
|
||||
|
||||
interface NoItemsProps {
|
||||
icon: string;
|
||||
text: string;
|
||||
children?: ComponentChildren;
|
||||
}
|
||||
|
||||
export default function NoItems({ icon, text, children }: NoItemsProps) {
|
||||
return (
|
||||
<div className="no-items">
|
||||
<Icon icon={icon} />
|
||||
{text}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -184,7 +184,8 @@ function SwitchSplitOrientationButton({ note, isReadOnly, isDefaultViewMode }: N
|
||||
|
||||
function ToggleReadOnlyButton({ note, viewType, isDefaultViewMode }: NoteActionsCustomInnerProps) {
|
||||
const [ isReadOnly, setReadOnly ] = useNoteLabelBoolean(note, "readOnly");
|
||||
const isEnabled = ([ "mermaid", "mindMap", "canvas" ].includes(note.type) || viewType === "geoMap")
|
||||
const isSavedSqlite = note.isTriliumSqlite() && !note.isHiddenCompletely();
|
||||
const isEnabled = ([ "mermaid", "mindMap", "canvas" ].includes(note.type) || viewType === "geoMap" || isSavedSqlite)
|
||||
&& note.isContentAvailable() && isDefaultViewMode;
|
||||
|
||||
return isEnabled && <ActionButton
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
import { useNoteContext } from "./react/hooks";
|
||||
|
||||
export default function ScrollPadding() {
|
||||
const { note, parentComponent, ntxId, viewScope } = useNoteContext();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [height, setHeight] = useState<number>(10);
|
||||
const isEnabled = ["text", "code"].includes(note?.type ?? "") && viewScope?.viewMode === "default";
|
||||
const isEnabled = ["text", "code"].includes(note?.type ?? "")
|
||||
&& viewScope?.viewMode === "default"
|
||||
&& !note?.isTriliumSqlite();
|
||||
|
||||
const refreshHeight = () => {
|
||||
if (!ref.current) return;
|
||||
@ -37,6 +40,6 @@ export default function ScrollPadding() {
|
||||
style={{ height }}
|
||||
onClick={() => parentComponent.triggerCommand("scrollToEnd", { ntxId })}
|
||||
/>
|
||||
: <div></div>
|
||||
)
|
||||
: <div />
|
||||
);
|
||||
}
|
||||
|
||||
@ -40,22 +40,4 @@ body.experimental-feature-new-layout #right-pane {
|
||||
.gutter-vertical + .card .card-header {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.no-items {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
padding: 0.75em;
|
||||
color: var(--muted-text-color);
|
||||
|
||||
.tn-icon {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import "./RightPanelContainer.css";
|
||||
|
||||
import Split from "@triliumnext/split.js";
|
||||
import { VNode } from "preact";
|
||||
import { useState, useEffect, useRef, useCallback } from "preact/hooks";
|
||||
import { useCallback, useEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
import appContext from "../../components/app_context";
|
||||
import { WidgetsByParent } from "../../services/bundle";
|
||||
@ -12,7 +12,7 @@ import options from "../../services/options";
|
||||
import { DEFAULT_GUTTER_SIZE } from "../../services/resizer";
|
||||
import Button from "../react/Button";
|
||||
import { useActiveNoteContext, useLegacyWidget, useNoteProperty, useTriliumEvent, useTriliumOptionJson } from "../react/hooks";
|
||||
import Icon from "../react/Icon";
|
||||
import NoItems from "../react/NoItems";
|
||||
import LegacyRightPanelWidget from "../right_panel_widget";
|
||||
import HighlightsList from "./HighlightsList";
|
||||
import PdfAttachments from "./pdf/PdfAttachments";
|
||||
@ -47,14 +47,15 @@ export default function RightPanelContainer({ widgetsByParent }: { widgetsByPare
|
||||
items.length > 0 ? (
|
||||
items
|
||||
) : (
|
||||
<div className="no-items">
|
||||
<Icon icon="bx bx-sidebar" />
|
||||
{t("right_pane.empty_message")}
|
||||
<NoItems
|
||||
icon="bx bx-sidebar"
|
||||
text={t("right_pane.empty_message")}
|
||||
>
|
||||
<Button
|
||||
text={t("right_pane.empty_button")}
|
||||
triggerCommand="toggleRightPane"
|
||||
/>
|
||||
</div>
|
||||
</NoItems>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
.sql-result-widget {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.sql-console-result-container td {
|
||||
white-space: preserve;
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
import { SqlExecuteResults } from "@triliumnext/commons";
|
||||
import { useNoteContext, useTriliumEvent } from "./react/hooks";
|
||||
import "./sql_result.css";
|
||||
import { useState } from "preact/hooks";
|
||||
import Alert from "./react/Alert";
|
||||
import { t } from "../services/i18n";
|
||||
|
||||
export default function SqlResults() {
|
||||
const { note, ntxId } = useNoteContext();
|
||||
const [ results, setResults ] = useState<SqlExecuteResults>();
|
||||
|
||||
useTriliumEvent("sqlQueryResults", ({ ntxId: eventNtxId, results }) => {
|
||||
if (eventNtxId !== ntxId) return;
|
||||
setResults(results);
|
||||
})
|
||||
|
||||
const isEnabled = note?.mime === "text/x-sqlite;schema=trilium";
|
||||
return (
|
||||
<div className={`sql-result-widget ${!isEnabled ? "hidden-ext" : ""}`}>
|
||||
{isEnabled && (
|
||||
results?.length === 1 && Array.isArray(results[0]) && results[0].length === 0 ? (
|
||||
<Alert type="info">
|
||||
{t("sql_result.no_rows")}
|
||||
</Alert>
|
||||
) : (
|
||||
<div className="sql-console-result-container selectable-text">
|
||||
{results?.map(rows => {
|
||||
// inserts, updates
|
||||
if (typeof rows === "object" && !Array.isArray(rows)) {
|
||||
return <pre>{JSON.stringify(rows, null, "\t")}</pre>
|
||||
}
|
||||
|
||||
// selects
|
||||
return <SqlResultTable rows={rows} />
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SqlResultTable({ rows }: { rows: object[] }) {
|
||||
if (!rows.length) return;
|
||||
|
||||
return (
|
||||
<table className="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
{Object.keys(rows[0]).map(key => <th>{key}</th>)}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{rows.map(row => (
|
||||
<tr>
|
||||
{Object.values(row).map(cell => <td>{cell}</td>)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
.sql-table-schemas-widget {
|
||||
padding: 12px;
|
||||
padding-inline-end: 10%;
|
||||
contain: none !important;
|
||||
}
|
||||
|
||||
.sql-table-schemas > .dropdown {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.sql-table-schemas button.btn {
|
||||
padding: 0.25rem 0.4rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 0.5;
|
||||
border: 1px solid var(--button-border-color);
|
||||
border-radius: var(--button-border-radius);
|
||||
background: var(--button-background-color);
|
||||
color: var(--button-text-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sql-console-result-container {
|
||||
width: 100%;
|
||||
font-size: smaller;
|
||||
margin-top: 10px;
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.table-schema td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.dropdown .table-schema {
|
||||
font-family: var(--monospace-font-family);
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
/* Data type */
|
||||
.dropdown .table-schema td:nth-child(2) {
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { t } from "../services/i18n";
|
||||
import { useNoteContext } from "./react/hooks";
|
||||
import "./sql_table_schemas.css";
|
||||
import { SchemaResponse } from "@triliumnext/commons";
|
||||
import server from "../services/server";
|
||||
import Dropdown from "./react/Dropdown";
|
||||
|
||||
export default function SqlTableSchemas() {
|
||||
const { note } = useNoteContext();
|
||||
const [ schemas, setSchemas ] = useState<SchemaResponse[]>();
|
||||
|
||||
useEffect(() => {
|
||||
server.get<SchemaResponse[]>("sql/schema").then(setSchemas);
|
||||
}, []);
|
||||
|
||||
const isEnabled = note?.mime === "text/x-sqlite;schema=trilium" && schemas;
|
||||
return (
|
||||
<div className={`sql-table-schemas-widget ${!isEnabled ? "hidden-ext" : ""}`}>
|
||||
{isEnabled && (
|
||||
<>
|
||||
{t("sql_table_schemas.tables")}{": "}
|
||||
|
||||
<span class="sql-table-schemas">
|
||||
{schemas.map(({ name, columns }) => (
|
||||
<>
|
||||
<Dropdown text={name} noSelectButtonStyle hideToggleArrow
|
||||
>
|
||||
<table className="table-schema">
|
||||
{columns.map(column => (
|
||||
<tr>
|
||||
<td>{column.name}</td>
|
||||
<td>{column.type}</td>
|
||||
</tr>
|
||||
))}
|
||||
</table>
|
||||
</Dropdown>
|
||||
{" "}
|
||||
</>
|
||||
))}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
81
apps/client/src/widgets/type_widgets/SqlConsole.css
Normal file
@ -0,0 +1,81 @@
|
||||
.sql-console-widget-container {
|
||||
.note-detail-split.split-vertical {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.note-detail-split-preview {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.gutter {
|
||||
background-color: var(--accented-background-color) !important;
|
||||
}
|
||||
|
||||
.sql-result-widget {
|
||||
height: 100%;
|
||||
|
||||
> .sql-console-result-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: smaller;
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
|
||||
> .tabulator {
|
||||
--cell-vert-padding-size: 4px;
|
||||
|
||||
> .tabulator-tableholder {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
> .tabulator-footer,
|
||||
> .tabulator-footer .tabulator-footer-contents {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sql-table-schemas-widget {
|
||||
padding: 12px;
|
||||
padding-inline-end: 10%;
|
||||
contain: none !important;
|
||||
|
||||
.sql-table-schemas {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
> .dropdown {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
button.btn {
|
||||
padding: 0.25rem 0.4rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 0.5;
|
||||
border: 1px solid var(--button-border-color);
|
||||
border-radius: var(--button-border-radius);
|
||||
background: var(--button-background-color);
|
||||
color: var(--button-text-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.table-schema td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.dropdown .table-schema {
|
||||
font-family: var(--monospace-font-family);
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
/* Data type */
|
||||
.dropdown .table-schema td:nth-child(2) {
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
176
apps/client/src/widgets/type_widgets/SqlConsole.tsx
Normal file
@ -0,0 +1,176 @@
|
||||
import "./SqlConsole.css";
|
||||
|
||||
import { SchemaResponse, SqlExecuteResponse } from "@triliumnext/commons";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { ClipboardModule, EditModule, ExportModule, FilterModule, FormatModule, FrozenColumnsModule, KeybindingsModule, PageModule, ResizeColumnsModule, SelectRangeModule, SelectRowModule, SortModule } from "tabulator-tables";
|
||||
|
||||
import { t } from "../../services/i18n";
|
||||
import server from "../../services/server";
|
||||
import Tabulator from "../collections/table/tabulator";
|
||||
import Button from "../react/Button";
|
||||
import Dropdown from "../react/Dropdown";
|
||||
import { useTriliumEvent } from "../react/hooks";
|
||||
import NoItems from "../react/NoItems";
|
||||
import SplitEditor from "./helpers/SplitEditor";
|
||||
import { TypeWidgetProps } from "./type_widget";
|
||||
|
||||
export default function SqlConsole(props: TypeWidgetProps) {
|
||||
return (
|
||||
<SplitEditor
|
||||
noteType="code"
|
||||
{...props}
|
||||
editorBefore={<SqlTableSchemas {...props} />}
|
||||
previewContent={<SqlResults key={props.note.noteId} {...props} />}
|
||||
forceOrientation="vertical"
|
||||
splitOptions={{
|
||||
sizes: [ 70, 30 ]
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SqlResults({ ntxId }: TypeWidgetProps) {
|
||||
const [ response, setResponse ] = useState<SqlExecuteResponse>();
|
||||
|
||||
useTriliumEvent("sqlQueryResults", ({ ntxId: eventNtxId, response }) => {
|
||||
if (eventNtxId !== ntxId) return;
|
||||
setResponse(response);
|
||||
});
|
||||
|
||||
// Not yet executed.
|
||||
if (response === undefined) {
|
||||
return (
|
||||
<NoItems
|
||||
icon="bx bx-data"
|
||||
text={t("sql_result.not_executed")}
|
||||
>
|
||||
<Button
|
||||
text={t("sql_result.execute_now")}
|
||||
triggerCommand="runActiveNote"
|
||||
/>
|
||||
</NoItems>
|
||||
);
|
||||
}
|
||||
|
||||
// Executed but failed.
|
||||
if (response && !response.success) {
|
||||
return (
|
||||
<NoItems
|
||||
icon="bx bx-error"
|
||||
text={t("sql_result.failed")}
|
||||
>
|
||||
<pre className="sql-error-message selectable-text">{response.error}</pre>
|
||||
</NoItems>
|
||||
);
|
||||
}
|
||||
|
||||
// Zero results.
|
||||
if (response?.results.length === 1 && Array.isArray(response.results[0]) && response.results[0].length === 0) {
|
||||
return (
|
||||
<NoItems
|
||||
icon="bx bx-rectangle"
|
||||
text={t("sql_result.no_rows")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sql-result-widget">
|
||||
<div className="sql-console-result-container selectable-text">
|
||||
{response?.results.map((rows, index) => {
|
||||
// inserts, updates
|
||||
if (typeof rows === "object" && !Array.isArray(rows)) {
|
||||
return (
|
||||
<NoItems
|
||||
key={index}
|
||||
icon="bx bx-play"
|
||||
text={t("sql_result.statement_result")}
|
||||
>
|
||||
<pre key={index}>{JSON.stringify(rows, null, "\t")}</pre>
|
||||
</NoItems>
|
||||
);
|
||||
}
|
||||
|
||||
// selects
|
||||
return <SqlResultTable key={index} rows={rows} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SqlResultTable({ rows }: { rows: object[] }) {
|
||||
if (!rows.length) return;
|
||||
|
||||
return (
|
||||
<Tabulator
|
||||
layout="fitDataFill"
|
||||
modules={[ ResizeColumnsModule, SortModule, SelectRangeModule, ClipboardModule, KeybindingsModule, EditModule, ExportModule, SelectRowModule, FormatModule, FrozenColumnsModule, FilterModule, PageModule ]}
|
||||
selectableRange
|
||||
clipboard="copy"
|
||||
clipboardCopyRowRange="range"
|
||||
clipboardCopyConfig={{
|
||||
rowHeaders: false,
|
||||
columnHeaders: false
|
||||
}}
|
||||
pagination
|
||||
paginationSize={15}
|
||||
paginationSizeSelector
|
||||
paginationCounter="rows"
|
||||
height="100%"
|
||||
columns={[
|
||||
{
|
||||
title: "#",
|
||||
formatter: "rownum",
|
||||
width: 60,
|
||||
hozAlign: "right",
|
||||
frozen: true
|
||||
},
|
||||
...Object.keys(rows[0]).map(key => ({
|
||||
title: key,
|
||||
field: key,
|
||||
width: 250,
|
||||
minWidth: 100,
|
||||
widthGrow: 1,
|
||||
resizable: true,
|
||||
headerFilter: true as const
|
||||
}))
|
||||
]}
|
||||
data={rows}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function SqlTableSchemas({ note }: TypeWidgetProps) {
|
||||
const [ schemas, setSchemas ] = useState<SchemaResponse[]>();
|
||||
|
||||
useEffect(() => {
|
||||
server.get<SchemaResponse[]>("sql/schema").then(setSchemas);
|
||||
}, []);
|
||||
|
||||
const isEnabled = note.isTriliumSqlite() && schemas;
|
||||
return (
|
||||
<div className={`sql-table-schemas-widget ${!isEnabled ? "hidden-ext" : ""}`}>
|
||||
{isEnabled && (
|
||||
<>
|
||||
{t("sql_table_schemas.tables")}{": "}
|
||||
|
||||
<span class="sql-table-schemas">
|
||||
{schemas.map(({ name, columns }) => (
|
||||
<Dropdown key={name} text={name} noSelectButtonStyle hideToggleArrow>
|
||||
<table className="table-schema">
|
||||
{columns.map(column => (
|
||||
<tr key={column.name}>
|
||||
<td>{column.name}</td>
|
||||
<td>{column.type}</td>
|
||||
</tr>
|
||||
))}
|
||||
</table>
|
||||
</Dropdown>
|
||||
))}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,13 +1,15 @@
|
||||
import "./SplitEditor.css";
|
||||
|
||||
import Split from "@triliumnext/split.js";
|
||||
import { ComponentChildren } from "preact";
|
||||
import { useEffect, useRef } from "preact/hooks";
|
||||
|
||||
import { DEFAULT_GUTTER_SIZE } from "../../../services/resizer";
|
||||
import utils, { isMobile } from "../../../services/utils";
|
||||
import ActionButton, { ActionButtonProps } from "../../react/ActionButton";
|
||||
import Admonition from "../../react/Admonition";
|
||||
import { useNoteLabelBoolean, useTriliumOption } from "../../react/hooks";
|
||||
import "./SplitEditor.css";
|
||||
import Split from "@triliumnext/split.js";
|
||||
import { DEFAULT_GUTTER_SIZE } from "../../../services/resizer";
|
||||
import { EditableCode, EditableCodeProps } from "../code/Code";
|
||||
import { ComponentChildren } from "preact";
|
||||
import ActionButton, { ActionButtonProps } from "../../react/ActionButton";
|
||||
|
||||
export interface SplitEditorProps extends EditableCodeProps {
|
||||
className?: string;
|
||||
@ -15,6 +17,8 @@ export interface SplitEditorProps extends EditableCodeProps {
|
||||
splitOptions?: Split.Options;
|
||||
previewContent: ComponentChildren;
|
||||
previewButtons?: ComponentChildren;
|
||||
editorBefore?: ComponentChildren;
|
||||
forceOrientation?: "horizontal" | "vertical";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -26,13 +30,14 @@ export interface SplitEditorProps extends EditableCodeProps {
|
||||
* - Can display errors to the user via {@link setError}.
|
||||
* - Horizontal or vertical orientation for the editor/preview split, adjustable via the switch split orientation button floating button.
|
||||
*/
|
||||
export default function SplitEditor({ note, error, splitOptions, previewContent, previewButtons, className, ...editorProps }: SplitEditorProps) {
|
||||
const splitEditorOrientation = useSplitOrientation();
|
||||
export default function SplitEditor({ note, error, splitOptions, previewContent, previewButtons, className, editorBefore, forceOrientation, ...editorProps }: SplitEditorProps) {
|
||||
const splitEditorOrientation = useSplitOrientation(forceOrientation);
|
||||
const [ readOnly ] = useNoteLabelBoolean(note, "readOnly");
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const editor = (!readOnly &&
|
||||
<div className="note-detail-split-editor-col">
|
||||
{editorBefore}
|
||||
<div className="note-detail-split-editor">
|
||||
<EditableCode
|
||||
note={note}
|
||||
@ -74,12 +79,12 @@ export default function SplitEditor({ note, error, splitOptions, previewContent,
|
||||
}, [ readOnly, splitEditorOrientation ]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={`note-detail-split note-detail-printable ${"split-" + splitEditorOrientation} ${readOnly ? "split-read-only" : ""} ${className ?? ""}`}>
|
||||
<div ref={containerRef} className={`note-detail-split note-detail-printable ${`split-${splitEditorOrientation}`} ${readOnly ? "split-read-only" : ""} ${className ?? ""}`}>
|
||||
{splitEditorOrientation === "horizontal"
|
||||
? <>{editor}{preview}</>
|
||||
: <>{preview}{editor}</>}
|
||||
? <>{editor}{preview}</>
|
||||
: <>{preview}{editor}</>}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function PreviewButton(props: Omit<ActionButtonProps, "titlePosition">) {
|
||||
@ -88,11 +93,12 @@ export function PreviewButton(props: Omit<ActionButtonProps, "titlePosition">) {
|
||||
className="tn-tool-button"
|
||||
noIconActionClass
|
||||
titlePosition="top"
|
||||
/>
|
||||
/>;
|
||||
}
|
||||
|
||||
function useSplitOrientation() {
|
||||
function useSplitOrientation(forceOrientation?: "horizontal" | "vertical") {
|
||||
const [ splitEditorOrientation ] = useTriliumOption("splitEditorOrientation");
|
||||
if (forceOrientation) return forceOrientation;
|
||||
if (isMobile()) return "vertical";
|
||||
if (!splitEditorOrientation) return "horizontal";
|
||||
return splitEditorOrientation as "horizontal" | "vertical";
|
||||
|
||||
@ -93,7 +93,15 @@ export default defineConfig(() => ({
|
||||
print: join(__dirname, "src", "print.tsx")
|
||||
},
|
||||
output: {
|
||||
entryFileNames: "src/[name].js",
|
||||
entryFileNames: (chunk) => {
|
||||
// We enforce a hash in the main index file to avoid caching issues, this only works because we have the HTML entry point.
|
||||
if (chunk.name === "index") {
|
||||
return "src/[name]-[hash].js";
|
||||
}
|
||||
|
||||
// For EJS-rendered pages (e.g. login) we need to have a stable name.
|
||||
return "src/[name].js";
|
||||
},
|
||||
chunkFileNames: "src/[name]-[hash].js",
|
||||
assetFileNames: "src/[name]-[hash].[ext]",
|
||||
manualChunks: {
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "2.1.3",
|
||||
"better-sqlite3": "12.6.0",
|
||||
"better-sqlite3": "12.6.2",
|
||||
"electron-debug": "4.1.0",
|
||||
"electron-dl": "4.0.0",
|
||||
"electron-squirrel-startup": "1.0.1",
|
||||
@ -35,7 +35,7 @@
|
||||
"@triliumnext/commons": "workspace:*",
|
||||
"@triliumnext/server": "workspace:*",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"electron": "39.2.7",
|
||||
"electron": "40.0.0",
|
||||
"@electron-forge/cli": "7.11.1",
|
||||
"@electron-forge/maker-deb": "7.11.1",
|
||||
"@electron-forge/maker-dmg": "7.11.1",
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"description": "Standalone tool to dump contents of Trilium document.db file into a directory tree of notes",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"better-sqlite3": "12.6.0",
|
||||
"better-sqlite3": "12.6.2",
|
||||
"mime-types": "3.0.2",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"tsx": "4.21.0",
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
"description": "Desktop version of Trilium which imports the demo database (presented to new users at start-up) or the user guide and other documentation and saves the modifications for committing.",
|
||||
"dependencies": {
|
||||
"archiver": "7.0.1",
|
||||
"better-sqlite3": "12.6.0"
|
||||
"better-sqlite3": "12.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@triliumnext/client": "workspace:*",
|
||||
"@triliumnext/desktop": "workspace:*",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"electron": "39.2.7",
|
||||
"electron": "40.0.0",
|
||||
"fs-extra": "11.3.3"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@ -185,6 +185,9 @@ async function exportData(noteId: string, format: ExportFormat, outputPath: stri
|
||||
return components.join("/");
|
||||
});
|
||||
|
||||
// Remove data-list-item-id created by CKEditor for lists
|
||||
content = content.replace(/ data-list-item-id="[^"]*"/g, "");
|
||||
|
||||
return content;
|
||||
|
||||
function findAttachment(targetAttachmentId: string) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"better-sqlite3": "12.6.0"
|
||||
"better-sqlite3": "12.6.2"
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,7 @@
|
||||
"proxy-nginx-subdir": "docker run --name trilium-nginx-subdir --rm --network=host -v ./docker/nginx.conf:/etc/nginx/conf.d/default.conf:ro nginx:latest"
|
||||
},
|
||||
"dependencies": {
|
||||
"better-sqlite3": "12.6.0",
|
||||
"better-sqlite3": "12.6.2",
|
||||
"html-to-text": "9.0.5",
|
||||
"node-html-parser": "7.0.2",
|
||||
"sucrase": "3.35.1"
|
||||
@ -82,8 +82,8 @@
|
||||
"csrf-csrf": "3.2.2",
|
||||
"debounce": "3.0.0",
|
||||
"debug": "4.4.3",
|
||||
"ejs": "3.1.10",
|
||||
"electron": "39.2.7",
|
||||
"ejs": "4.0.1",
|
||||
"electron": "40.0.0",
|
||||
"electron-debug": "4.1.0",
|
||||
"electron-window-state": "5.0.3",
|
||||
"escape-html": "1.0.3",
|
||||
@ -99,7 +99,7 @@
|
||||
"html2plaintext": "2.1.4",
|
||||
"http-proxy-agent": "7.0.2",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"i18next": "25.7.4",
|
||||
"i18next": "25.8.0",
|
||||
"i18next-fs-backend": "2.6.1",
|
||||
"image-type": "6.0.0",
|
||||
"ini": "6.0.0",
|
||||
@ -126,7 +126,7 @@
|
||||
"swagger-jsdoc": "6.2.8",
|
||||
"time2fa": "1.4.2",
|
||||
"tmp": "0.2.5",
|
||||
"turndown": "7.2.2",
|
||||
"turnish": "1.8.0",
|
||||
"unescape": "1.0.1",
|
||||
"vite": "7.3.1",
|
||||
"ws": "8.19.0",
|
||||
|
||||
@ -1,25 +1,27 @@
|
||||
import express from "express";
|
||||
import path from "path";
|
||||
import favicon from "serve-favicon";
|
||||
import cookieParser from "cookie-parser";
|
||||
import helmet from "helmet";
|
||||
import compression from "compression";
|
||||
import config from "./services/config.js";
|
||||
import utils, { getResourceDir, isDev } from "./services/utils.js";
|
||||
import assets from "./routes/assets.js";
|
||||
import routes from "./routes/routes.js";
|
||||
import custom from "./routes/custom.js";
|
||||
import error_handlers from "./routes/error_handlers.js";
|
||||
import { startScheduledCleanup } from "./services/erase.js";
|
||||
import sql_init from "./services/sql_init.js";
|
||||
import { auth } from "express-openid-connect";
|
||||
import openID from "./services/open_id.js";
|
||||
import { t } from "i18next";
|
||||
import eventService from "./services/events.js";
|
||||
import log from "./services/log.js";
|
||||
import "./services/handlers.js";
|
||||
import "./becca/becca_loader.js";
|
||||
|
||||
import compression from "compression";
|
||||
import cookieParser from "cookie-parser";
|
||||
import ejs from "ejs";
|
||||
import express from "express";
|
||||
import { auth } from "express-openid-connect";
|
||||
import helmet from "helmet";
|
||||
import { t } from "i18next";
|
||||
import path from "path";
|
||||
import favicon from "serve-favicon";
|
||||
|
||||
import assets from "./routes/assets.js";
|
||||
import custom from "./routes/custom.js";
|
||||
import error_handlers from "./routes/error_handlers.js";
|
||||
import routes from "./routes/routes.js";
|
||||
import config from "./services/config.js";
|
||||
import { startScheduledCleanup } from "./services/erase.js";
|
||||
import log from "./services/log.js";
|
||||
import openID from "./services/open_id.js";
|
||||
import { RESOURCE_DIR } from "./services/resource_dir.js";
|
||||
import sql_init from "./services/sql_init.js";
|
||||
import utils, { getResourceDir, isDev } from "./services/utils.js";
|
||||
|
||||
export default async function buildApp() {
|
||||
const app = express();
|
||||
@ -33,7 +35,7 @@ export default async function buildApp() {
|
||||
|
||||
// view engine setup
|
||||
app.set("views", path.join(assetsDir, "views"));
|
||||
app.engine("ejs", (await import("ejs")).renderFile);
|
||||
app.engine("ejs", (filePath, options, callback) => ejs.renderFile(filePath, options, callback));
|
||||
app.set("view engine", "ejs");
|
||||
|
||||
app.use((req, res, next) => {
|
||||
|
||||
2
apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
generated
vendored
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 230 B |
|
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 265 B |
@ -1,36 +1,43 @@
|
||||
<p>The SQL Console is Trilium's built-in database editor.</p>
|
||||
<p>It can be accessed by going to the <a href="#root/_help_Vc8PjrjAGuOp">global menu</a> →
|
||||
<p>It can be accessed by going to the <a class="reference-link" href="#root/_help_x3i7MxGccDuM">Global menu</a> →
|
||||
Advanced → Open SQL Console.</p>
|
||||
<p>
|
||||
<img src="SQL Console_image.png">
|
||||
</p>
|
||||
<h3>Interaction</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Hovering the mouse over one of the tables listed at the top of the document
|
||||
will show the columns and their data type.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Only one SQL statement can be run at once.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>To run the statement, press the
|
||||
<img src="3_SQL Console_image.png">icon.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>For queries that return a result, the data will displayed in a table.</p>
|
||||
<p>
|
||||
<img src="1_SQL Console_image.png">
|
||||
</p>
|
||||
</li>
|
||||
<li>Hovering the mouse over one of the tables listed at the top of the document
|
||||
will show the columns and their data type.</li>
|
||||
<li>Only one SQL statement can be run at once.</li>
|
||||
<li>To run the statement, press the <em>Execute</em> icon.</li>
|
||||
<li>For queries that return a result, the data will displayed in a table.</li>
|
||||
<li>For statements (e.g. <code spellcheck="false">INSERT</code>, <code spellcheck="false">UPDATE</code>),
|
||||
the number of affected rows is displayed.</li>
|
||||
</ul>
|
||||
<figure class="image">
|
||||
<img style="aspect-ratio:1124/571;" src="2_SQL Console_image.png"
|
||||
width="1124" height="571">
|
||||
</figure>
|
||||
|
||||
<h3>Interacting with the table</h3>
|
||||
<p>After executing a query, a table with the results will be displayed:</p>
|
||||
<ul>
|
||||
<li>Clicking on a column allows sorting ascending or descending.</li>
|
||||
<li>Underneath each column there is an input field which allows filtering
|
||||
by text.</li>
|
||||
<li>Press <kbd>Ctrl</kbd>+<kbd>C</kbd> to copy the current cell to clipboard.</li>
|
||||
<li>Multiple cells can be selected by dragging or by holding <kbd>Shift</kbd> +
|
||||
arrow keys</li>
|
||||
<li>Results are paginated for performance reasons. The controls at the bottom
|
||||
of the table can be used to navigate through pages.</li>
|
||||
</ul>
|
||||
<h3>Saved SQL console</h3>
|
||||
<p>SQL queries or commands can be saved into a dedicated note.</p>
|
||||
<p>To do so, simply write the query and press the
|
||||
<img src="2_SQL Console_image.png">button. Once saved, the note will appear in <a href="#root/_help_l0tKav7yLHGF">Day Notes</a>.</p>
|
||||
<ul>
|
||||
<li>The SQL expression will not be displayed by default, but it can still
|
||||
be viewed by going to the note context menu and selecting <em>Note source</em>.</li>
|
||||
<li>The expression cannot be modified. If needed, recreate it by copying the
|
||||
statement back into the SQL console and then saving it again.</li>
|
||||
</ul>
|
||||
<img src="1_SQL Console_image.png">button. Once saved, the note will appear in <a class="reference-link"
|
||||
href="#root/_help_l0tKav7yLHGF">Day Notes</a>.</p>
|
||||
<p>The note can be locked for editing by pressing the <em>Lock</em> button
|
||||
in the note actions section near the title bar (on the <a class="reference-link"
|
||||
href="#root/_help_IjZS7iK5EXtb">New Layout</a>, or in the <a class="reference-link"
|
||||
href="#root/_help_XpOYSgsLkTJy">Floating buttons</a> area if using the old
|
||||
layout). When editing is locked, the SQL statement is hidden from view.</p>
|
||||
34
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Advanced Usage/Sharing.html
generated
vendored
@ -38,17 +38,17 @@ class="image">
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e26b4ce9ba4e9dfe224d04e0f341925ed">Table of contents.</li>
|
||||
<li data-list-item-id="e9707fdfa2c92d66690cf932f7e647253">Syntax highlight of code blocks, provided a language is selected (does
|
||||
<li>Table of contents.</li>
|
||||
<li>Syntax highlight of code blocks, provided a language is selected (does
|
||||
not work if “Auto-detected” is enabled).</li>
|
||||
<li data-list-item-id="e84420a10c6d64bd107edb6e867c91d4b">Rendering for math equations.</li>
|
||||
<li data-list-item-id="e10834dcd0619d77ae2e94d3695bedf58"><a href="#root/_help_nBAXQFj20hS1">Including notes</a> (only if the included
|
||||
<li>Rendering for math equations.</li>
|
||||
<li><a href="#root/_help_nBAXQFj20hS1">Including notes</a> (only if the included
|
||||
notes are also shared).</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e41cc4139377f9f88d653d1eb8ca47bb4">Inline Mermaid diagrams are not rendered.</li>
|
||||
<li>Inline Mermaid diagrams are not rendered.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -57,12 +57,12 @@ class="image">
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e291ae6d5130677b4c99f7c3bdbe974b4">Basic support (displaying the contents of the note in a monospace font).</li>
|
||||
<li>Basic support (displaying the contents of the note in a monospace font).</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e0270680bbdd7a129306e61e11691e36d">No syntax highlight.</li>
|
||||
<li>No syntax highlight.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -95,12 +95,12 @@ class="image">
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="ea031e1d4149eb443ace756234490c5a4">The child notes are displayed in a fixed format. </li>
|
||||
<li>The child notes are displayed in a fixed format. </li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="ea4a9d424aec2afbaecc07bbf64b7bebd">More advanced view types such as the calendar view are not supported.</li>
|
||||
<li>More advanced view types such as the calendar view are not supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -109,12 +109,12 @@ class="image">
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e582d283f2b1b30cbe5ae35d8e01b2bf2">The diagram is displayed as a vector image.</li>
|
||||
<li>The diagram is displayed as a vector image.</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e33268686446e3c217077201bb5964364">No further interaction supported.</li>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -123,12 +123,12 @@ class="image">
|
||||
</th>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e443dd0e97c30cb12c77e8906a71569ea">The diagram is displayed as a vector image.</li>
|
||||
<li>The diagram is displayed as a vector image.</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="efe151ef3f3826c825416417525fb5fb2">No further interaction supported.</li>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -144,7 +144,7 @@ class="image">
|
||||
<td>The diagram is displayed as a vector image.</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="ed3b4fb473042f6e32b4502d4fa11a767">No further interaction supported.</li>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -160,7 +160,7 @@ class="image">
|
||||
<td>Basic interaction (downloading the file).</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="ed87e836a39d127ebcbb33e9e59045afb">No further interaction supported.</li>
|
||||
<li>No further interaction supported.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -392,8 +392,8 @@ for (const attr of parentNote.attributes) {
|
||||
<p>Indicates to web crawlers that the page should not be indexed of this
|
||||
note by:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e6baa9f60bf59d085fd31aa2cce07a0e7">Setting the <code>X-Robots-Tag: noindex</code> HTTP header.</li>
|
||||
<li data-list-item-id="ec0d067db136ef9794e4f1033405880b7">Setting the <code>noindex, follow</code> meta tag.</li>
|
||||
<li>Setting the <code>X-Robots-Tag: noindex</code> HTTP header.</li>
|
||||
<li>Setting the <code>noindex, follow</code> meta tag.</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -1,57 +1,54 @@
|
||||
<p>
|
||||
<meta>Trilium can import ENEX files, which are used by Evernote for backup/export.
|
||||
<p>Trilium can import ENEX files, which are used by Evernote for backup/export.
|
||||
One ENEX file represents the content (notes and resources) of one notebook.</p>
|
||||
<h2>Export ENEX from Evernote</h2>
|
||||
<p>To export ENEX files from Evernote, you can use:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="eabf6814a75b1ddc665781ed732fa65ff">Evernote desktop application. See Evernote documentation. Note that
|
||||
the limitation of this method is that you can only export 100 notes at
|
||||
a time or one notebook at a time.</li>
|
||||
<li data-list-item-id="e7c66b50692c80eb3a325841ae5218eb7">A third-party evernote-backup CLI tool. This tool can export all
|
||||
of your notebooks in bulk.</li>
|
||||
<li>Evernote desktop application. See Evernote <a href="https://help.evernote.com/hc/en-us/articles/209005557-Export-Notes-and-Notebooks-as-ENEX-or-HTML">documentation</a>.
|
||||
Note that the limitation of this method is that you can only export 100
|
||||
notes at a time or one notebook at a time.</li>
|
||||
<li>A third-party <a href="https://github.com/vzhd1701/evernote-backup">evernote-backup</a> CLI
|
||||
tool. This tool can export all of your notebooks in bulk.</li>
|
||||
</ul>
|
||||
<h2>Import ENEX in Trilium</h2>
|
||||
<p>Once you have your ENEX files, do the following to import them in Trilium:</p>
|
||||
<ol>
|
||||
<li data-list-item-id="e86484b3caf72ef69321bc8639024a991">In the Trilium note tree, right-click the note under which you want to
|
||||
<li>In the Trilium note tree, right-click the note under which you want to
|
||||
import one or more of your ENEX files. The notes in the files will be imported
|
||||
as child notes of the selected note.</li>
|
||||
<li data-list-item-id="ec8593d763d8375e841f68731e454b548">Click Import into note.</li>
|
||||
<li data-list-item-id="e1fb52a96a47f9d881342148ace1353ce">Choose your ENEX file or files and click Import.</li>
|
||||
<li data-list-item-id="e44b2f10dc3fdd60956774647c440c9c3">During the import, you will see "Import in progress" message. If the import
|
||||
<li>Click Import into note.</li>
|
||||
<li>Choose your ENEX file or files and click Import.</li>
|
||||
<li>During the import, you will see "Import in progress" message. If the import
|
||||
is successful, the message will change to “Import finished successfully”
|
||||
and then disappear.</li>
|
||||
<li data-list-item-id="ebcec83cfa6d093376cd6fbcfff3db532">We recommend you to check the imported notes and their attachments to
|
||||
<li>We recommend you to check the imported notes and their attachments to
|
||||
verify that you haven’t lost any data.</li>
|
||||
</ol>
|
||||
<p>A non-exhaustive list of what the importer preserves:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e3180fcb6ac86b4543e514a9209c5e789">Attachments</li>
|
||||
<li data-list-item-id="e7e9720e6bbadf04dde505b2ec56159ec">The hierarchy of headings (these are shifted to start with H2 because
|
||||
H1 is reserved for note title, see Headings)</li>
|
||||
<li data-list-item-id="e6798ed61930845577e1f0750378bc52c">Tables</li>
|
||||
<li data-list-item-id="ef9ddc278d5a093dfd5488c2e07b6278d">Bulleted lists</li>
|
||||
<li data-list-item-id="ed8ef6d14f74b4fffd08bd66d9c859d36">Numbered lists</li>
|
||||
<li data-list-item-id="e989884d7888ef1d6ce062c4e71b68636">Bold</li>
|
||||
<li data-list-item-id="e438a5188d95036c5bfa91def39b38259">Italics</li>
|
||||
<li data-list-item-id="eda9f7b42b3e02ba4e54a805b2def949b">Strikethrough</li>
|
||||
<li data-list-item-id="e095a40c46c7f8d1083e9d74258ee85e4">Highlights</li>
|
||||
<li data-list-item-id="e822d52533a7e7876e0963e043de49e48">Font colors</li>
|
||||
<li data-list-item-id="ece8974b757a014f25208911706a0462d">Soft line breaks</li>
|
||||
<li data-list-item-id="eb22c11732fea1beee2976ede9d81ca5e">External links</li>
|
||||
<li>Attachments</li>
|
||||
<li>The hierarchy of headings (these are shifted to start with H2 because
|
||||
H1 is reserved for note title, see <a href="#root/_help_Gr6xFaF6ioJ5">Headings</a>)</li>
|
||||
<li>Tables</li>
|
||||
<li>Bulleted lists</li>
|
||||
<li>Numbered lists</li>
|
||||
<li>Bold</li>
|
||||
<li>Italics</li>
|
||||
<li>Strikethrough</li>
|
||||
<li>Highlights</li>
|
||||
<li>Font colors</li>
|
||||
<li>Soft line breaks</li>
|
||||
<li>External links</li>
|
||||
</ul>
|
||||
<p>However, we do not guarantee that all of your formatting will be imported
|
||||
100% correctly.</p>
|
||||
<h2>Limitations</h2>
|
||||
<ul>
|
||||
<li data-list-item-id="ece66c1841631b7250ddb6c101d28afd6">The size limit of one import is 250Mb. If the total size of your files
|
||||
is larger, you can increase the upload limit, or divide your files,
|
||||
and run the import as many times as necessary.</li>
|
||||
<li data-list-item-id="ece322cd2ca2b9332154a25f48c16309d">All resources (except for images) are created as notes’ attachments.</li>
|
||||
<li
|
||||
data-list-item-id="ee13e15fa378905573e13b348441be574">If you have HTML inside ENEX files, the HTML formatting may be broken
|
||||
or lost after import in Trilium. You can report major problems at Trilium
|
||||
issue tracker.</li>
|
||||
<li>The size limit of one import is 250Mb. If the total size of your files
|
||||
is larger, you can increase the <a href="#root/_help_WOcw2SLH6tbX">upload limit</a>,
|
||||
or divide your files, and run the import as many times as necessary.</li>
|
||||
<li>All resources (except for images) are created as notes’ attachments.</li>
|
||||
<li>If you have HTML inside ENEX files, the HTML formatting may be broken
|
||||
or lost after import in Trilium. See <a class="reference-link" href="#root/_help_wy8So3yZZlH9">Reporting issues</a>.</li>
|
||||
</ul>
|
||||
<h3>Internal links</h3>
|
||||
<p>The importer cannot transform Evernote internal links into Trilium internal
|
||||
@ -59,24 +56,24 @@
|
||||
<p>If you want to restore the internal links in Trilium after you import
|
||||
all of your ENEX files, you can use or adapt this custom script:
|
||||
<a
|
||||
class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/mHbBMPDPkVV5/syuSEKf2rUGr/_help_dj3j8dG4th4l">Process internal links by title</a>
|
||||
class="reference-link" href="#root/_help_dj3j8dG4th4l">Process internal links by title</a>
|
||||
</p>
|
||||
<p>The script does the following:</p>
|
||||
<ol>
|
||||
<li data-list-item-id="e6c3f7477c6a241ef97cb9bf15623a2c0">It finds all Evernote internal links.</li>
|
||||
<li data-list-item-id="e928015e4a024f40ebdb85b2e5b77844f">For each one, it checks if its link text matches a note title, and if
|
||||
<li>It finds all Evernote internal links.</li>
|
||||
<li>For each one, it checks if its link text matches a note title, and if
|
||||
yes, it replaces the Evernote link with an internal Trilium link. If not,
|
||||
it leaves the Evernote link in place.</li>
|
||||
<li data-list-item-id="e5fb070b954d7cba355c3c9065e8e4b1f">If it finds more than one note with a matching note title, it leaves the
|
||||
<li>If it finds more than one note with a matching note title, it leaves the
|
||||
Evernote link in place.</li>
|
||||
<li data-list-item-id="eea5e5efb229b5a731e3c980160de29f8">It outputs the results in a log that you can see in the respective code
|
||||
note in Trilium. </li>
|
||||
<li>It outputs the results in a log that you can see in the respective code
|
||||
note in Trilium.</li>
|
||||
</ol>
|
||||
<p>The script has the following limitations:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e467615aa997504c5aa55f2f169d0028f">It will not fix links to anchors and links to notes that you renamed in
|
||||
<li>It will not fix links to anchors and links to notes that you renamed in
|
||||
Evernote after you created the links.</li>
|
||||
<li data-list-item-id="e83d3745ce3531ae99a9619274ad42440">Some note titles might not be well identified, even if they exist. This
|
||||
<li>Some note titles might not be well identified, even if they exist. This
|
||||
is especially the case if the note title contains some special characters.
|
||||
Should this be problematic, consider <a class="reference-link" href="#root/pOsGYCXsbNQG/BgmBlOIl72jZ/_help_wy8So3yZZlH9">Reporting issues</a>.</li>
|
||||
Should this be problematic, consider <a class="reference-link" href="#root/_help_wy8So3yZZlH9">Reporting issues</a>.</li>
|
||||
</ul>
|
||||
8
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Calendar.html
generated
vendored
@ -148,10 +148,10 @@
|
||||
<td>
|
||||
<p>Which view to display in the calendar:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e2cd230dc41f41fe91ee74d7d1fa87372"><code>timeGridWeek</code> for the <em>week</em> view;</li>
|
||||
<li data-list-item-id="eee1dba4c6cc51ebd53d0a0dd52044cd6"><code>dayGridMonth</code> for the <em>month</em> view;</li>
|
||||
<li data-list-item-id="ed8721a76a1865dac882415f662ed45b9"><code>multiMonthYear</code> for the <em>year</em> view;</li>
|
||||
<li data-list-item-id="edf09a13759102d98dac34c33eb690c05"><code>listMonth</code> for the <em>list</em> view.</li>
|
||||
<li><code>timeGridWeek</code> for the <em>week</em> view;</li>
|
||||
<li><code>dayGridMonth</code> for the <em>month</em> view;</li>
|
||||
<li><code>multiMonthYear</code> for the <em>year</em> view;</li>
|
||||
<li><code>listMonth</code> for the <em>list</em> view.</li>
|
||||
</ul>
|
||||
<p>Any other value will be dismissed and the default view (month) will be
|
||||
used instead.</p>
|
||||
|
||||
68
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text.html
generated
vendored
@ -33,12 +33,12 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e04c84d59d44645ee89b2a8541ed99f90">Headings (section titles, paragraph)</li>
|
||||
<li data-list-item-id="e39d25bd3d8bd06185b9d259e5827d451">Font size</li>
|
||||
<li data-list-item-id="e1f7e2a2f4b03449d82bdf5b5c6ea8d44">Bold, italic, underline, strike-through</li>
|
||||
<li data-list-item-id="e3decae72884f65b4d538151b6a297072">Superscript, subscript</li>
|
||||
<li data-list-item-id="e59adf00fef65304c163ae190fac5e92a">Font color & background color</li>
|
||||
<li data-list-item-id="ed3f09156147a2769e91db111c76376e2">Remove formatting</li>
|
||||
<li>Headings (section titles, paragraph)</li>
|
||||
<li>Font size</li>
|
||||
<li>Bold, italic, underline, strike-through</li>
|
||||
<li>Superscript, subscript</li>
|
||||
<li>Font color & background color</li>
|
||||
<li>Remove formatting</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -47,9 +47,9 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="ee87806a913900d85d8f018af81f41df8">Bulleted lists</li>
|
||||
<li data-list-item-id="e3ae314e365fa418ca6e0f061d63834c5">Numbered lists</li>
|
||||
<li data-list-item-id="ee84e08694165f95430046cb34f4cd123">To-do lists</li>
|
||||
<li>Bulleted lists</li>
|
||||
<li>Numbered lists</li>
|
||||
<li>To-do lists</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -58,8 +58,8 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e2892dc35a0d4b7ad65daffb8f9404daa">Block quotes</li>
|
||||
<li data-list-item-id="e7297e3ad1002f8de15aa0bd66c6f3f22">Admonitions</li>
|
||||
<li>Block quotes</li>
|
||||
<li>Admonitions</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -68,10 +68,10 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="eb358a4567d93f66004f4195df2dda05a">Basic tables</li>
|
||||
<li data-list-item-id="e6135a555d6c63c30e4b84806a4870830">Merging cells</li>
|
||||
<li data-list-item-id="e29ac76563d0998b28fb1baf94dbdac8c">Styling tables and cells.</li>
|
||||
<li data-list-item-id="e372446e81fdedada64b8bed89ca93d1a">Table captions</li>
|
||||
<li>Basic tables</li>
|
||||
<li>Merging cells</li>
|
||||
<li>Styling tables and cells.</li>
|
||||
<li>Table captions</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -80,9 +80,9 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="eb260b76afcbc07bd9d4ceec4e000e8a0">Inline code</li>
|
||||
<li data-list-item-id="e9864352286369ebe7b41c1599f498de8">Code blocks</li>
|
||||
<li data-list-item-id="ee62fb9ed7f349178e8f2a2bd9ec8cd74">Keyboard shortcuts</li>
|
||||
<li>Inline code</li>
|
||||
<li>Code blocks</li>
|
||||
<li>Keyboard shortcuts</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -91,7 +91,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="edf62ec004eff35cfcb7e361deef19aaf">Footnotes</li>
|
||||
<li>Footnotes</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -100,7 +100,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="ebe6277e643041403489c3ceb30c36f7f">Images</li>
|
||||
<li>Images</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -109,8 +109,8 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e3f988be2f259bb40607cb61541955395">External links</li>
|
||||
<li data-list-item-id="e3f91cc4f0cccd2c077cc306bacd68ef2">Internal Trilium links</li>
|
||||
<li>External links</li>
|
||||
<li>Internal Trilium links</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -119,7 +119,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="eac8015a64bce7b749cc67d1599062007">Include note</li>
|
||||
<li>Include note</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -128,12 +128,12 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e5cdf5d3885ec0ea67f924b4b8fe5c483">Symbols</li>
|
||||
<li data-list-item-id="e95082e6642ed5b1eec6e4e116b899a40"><a class="reference-link" href="#root/_help_YfYAtQBcfo5V">Math Equations</a>
|
||||
<li>Symbols</li>
|
||||
<li><a class="reference-link" href="#root/_help_YfYAtQBcfo5V">Math Equations</a>
|
||||
</li>
|
||||
<li data-list-item-id="ecbef6a358a5b8d27f0d3e08bbc750aa9">Mermaid diagrams</li>
|
||||
<li data-list-item-id="e6e97ee14dd29b7ccf53227107e5dc72d">Horizontal ruler</li>
|
||||
<li data-list-item-id="e6198c7c535c249faec2e8906775f11de">Page break</li>
|
||||
<li>Mermaid diagrams</li>
|
||||
<li>Horizontal ruler</li>
|
||||
<li>Page break</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
@ -142,12 +142,12 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e0c14456cb83d483b07ea432ef9d4728e">Indentation
|
||||
<li>Indentation
|
||||
<ul>
|
||||
<li data-list-item-id="e2029812c5e105c595590f70ee227631e">Markdown import</li>
|
||||
<li>Markdown import</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li data-list-item-id="ea1ee012286e05190c89c9f4e64cf2036"><a class="reference-link" href="#root/_help_2x0ZAX9ePtzV">Cut to subnote</a>
|
||||
<li><a class="reference-link" href="#root/_help_2x0ZAX9ePtzV">Cut to subnote</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
@ -157,11 +157,11 @@
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="e1ab173193a533ccf33dccfd0cb916f1f"><a class="reference-link" href="#root/_help_ZlN4nump6EbW">Slash Commands</a>
|
||||
<li><a class="reference-link" href="#root/_help_ZlN4nump6EbW">Slash Commands</a>
|
||||
</li>
|
||||
<li data-list-item-id="e564b978c09fe5adf476b331b1e0640e3"><a class="reference-link" href="#root/_help_KC1HB96bqqHX">Templates</a>
|
||||
<li><a class="reference-link" href="#root/_help_KC1HB96bqqHX">Templates</a>
|
||||
</li>
|
||||
<li data-list-item-id="e756306c31d9beffbba3820b6d1b9bc61"><a class="reference-link" href="#root/_help_5wZallV2Qo1t">Format Painter</a>
|
||||
<li><a class="reference-link" href="#root/_help_5wZallV2Qo1t">Format Painter</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
|
||||
@ -19,14 +19,14 @@
|
||||
<td>
|
||||
<p>Defines on which events script should run. Possible values are:</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e244b14e102cf1b0d4954e8fd455ea77b"><code>frontendStartup</code> - when Trilium frontend starts up (or is refreshed),
|
||||
<li><code>frontendStartup</code> - when Trilium frontend starts up (or is refreshed),
|
||||
but not on mobile.</li>
|
||||
<li data-list-item-id="ea8f8ca86e7b351dd86108848ccb9103a"><code>mobileStartup</code> - when Trilium frontend starts up (or is refreshed),
|
||||
<li><code>mobileStartup</code> - when Trilium frontend starts up (or is refreshed),
|
||||
on mobile.</li>
|
||||
<li data-list-item-id="e658488cf1a0862603088ef384e41b8b6"><code>backendStartup</code> - when Trilium backend starts up</li>
|
||||
<li data-list-item-id="ef40ba992fc450d33a18ca4cb031eca66"><code>hourly</code> - run once an hour. You can use additional label <code>runAtHour</code> to
|
||||
<li><code>backendStartup</code> - when Trilium backend starts up</li>
|
||||
<li><code>hourly</code> - run once an hour. You can use additional label <code>runAtHour</code> to
|
||||
specify at which hour, on the back-end.</li>
|
||||
<li data-list-item-id="e07458d4f55b6eb42468a5535b8425c5f"><code>daily</code> - run once a day, on the back-end</li>
|
||||
<li><code>daily</code> - run once a day, on the back-end</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
27
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Breaking changes.html
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<h2>v0.102.0: Upgrade to jQuery 4.0.0</h2>
|
||||
<p>jQuery 4 removes legacy browser support (such as IE11 support), but it
|
||||
also removes some APIs that are considered deprecated such as:</p>
|
||||
<blockquote>
|
||||
<p><code spellcheck="false">jQuery.isArray</code>, <code spellcheck="false">jQuery.parseJSON</code>,
|
||||
<code
|
||||
spellcheck="false">jQuery.trim</code>, <code spellcheck="false">jQuery.type</code>, <code spellcheck="false">jQuery.now</code>,
|
||||
<code
|
||||
spellcheck="false">jQuery.isNumeric</code>, <code spellcheck="false">jQuery.isFunction</code>,
|
||||
<code
|
||||
spellcheck="false">jQuery.isWindow</code>, <code spellcheck="false">jQuery.camelCase</code>,
|
||||
<code
|
||||
spellcheck="false">jQuery.nodeName</code>, <code spellcheck="false">jQuery.cssNumber</code>,
|
||||
<code
|
||||
spellcheck="false">jQuery.cssProps</code>, and <code spellcheck="false">jQuery.fx.interval</code>.</p>
|
||||
<p>Use native equivalents like <code spellcheck="false">Array.isArray()</code>,
|
||||
<code
|
||||
spellcheck="false">JSON.parse()</code>, <code spellcheck="false">String.prototype.trim()</code>,
|
||||
and <code spellcheck="false">Date.now()</code> instead.</p>
|
||||
</blockquote>
|
||||
<p>This may affect custom scripts if they (or the custom jQuery libraries
|
||||
used) depend on the deprecated APIs.</p>
|
||||
<p>Note that Trilium polyfills <code spellcheck="false">jQuery.isArray</code>,
|
||||
<code
|
||||
spellcheck="false">isFunction</code>and <code spellcheck="false">isPlainObject</code> because
|
||||
they were required by one of our dependencies (the autocomplete).</p>
|
||||
<p>For more information, consult <a href="https://blog.jquery.com/2026/01/17/jquery-4-0-0/">the official blog post</a>.</p>
|
||||
@ -107,10 +107,10 @@ class="ck-table-resized">
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="ec06332efcc3039721606c052f0d913fa">The widget must export a <code>class</code> and not an instance of the class
|
||||
<li>The widget must export a <code>class</code> and not an instance of the class
|
||||
(e.g. <code>no new</code>) because it needs to be multiplied for each note,
|
||||
so that splits work correctly.</li>
|
||||
<li data-list-item-id="e8da690a2a8df148f6b5fc04ba1611688">Since the <code>class</code> is exported instead of an instance, the <code>parentWidget</code> getter
|
||||
<li>Since the <code>class</code> is exported instead of an instance, the <code>parentWidget</code> getter
|
||||
must be <code>static</code>, otherwise the widget is ignored.</li>
|
||||
</ul>
|
||||
</td>
|
||||
@ -124,7 +124,7 @@ class="ck-table-resized">
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li data-list-item-id="efe008d361e224f422582552648e1afe7">Although not mandatory, it's best to use a <code>RightPanelWidget</code> instead
|
||||
<li>Although not mandatory, it's best to use a <code>RightPanelWidget</code> instead
|
||||
of a <code>BasicWidget</code> or a <code>NoteContextAwareWidget</code>.</li>
|
||||
</ul>
|
||||
</td>
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
"search-in-subtree": "एक्टिव नोट के सब-ट्री में नोट्स खोजें",
|
||||
"expand-subtree": "मौजूदा नोट के सब-ट्री को (subtree) एक्सपैंड करें",
|
||||
"delete-note": "नोट डिलीट करें",
|
||||
"move-note-up-in-hierarchy": "नोट एक लेवल ऊपर मूव करें"
|
||||
"move-note-up-in-hierarchy": "नोट एक लेवल ऊपर मूव करें",
|
||||
"move-note-down-in-hierarchy": "नोट एक लेवल नीचे ले जाएँ",
|
||||
"dialogs": "डायलॉग्स"
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,8 @@
|
||||
"search-in-subtree": "Søk etter notater i det aktive notatets understruktur",
|
||||
"creating-and-moving-notes": "Lage og flytte notater",
|
||||
"dialogs": "Dialogbokser",
|
||||
"other": "Andre"
|
||||
"other": "Andre",
|
||||
"expand-subtree": "Utvid undertre for gjeldende notat"
|
||||
},
|
||||
"setup_sync-from-desktop": {
|
||||
"step6-here": "her"
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
"collapse-subtree": "Geçerli notun alt ağacını daraltır",
|
||||
"sort-child-notes": "Alt notları sırala",
|
||||
"creating-and-moving-notes": "Notları oluşturma ve yerlerini değiştirme",
|
||||
"create-note-into": "Aktif nota bağlı alt not oluştur"
|
||||
"create-note-into": "Aktif nota bağlı alt not oluştur",
|
||||
"create-note-after": "Aktif nottan sonra yeni bir not oluştur",
|
||||
"delete-note": "Notu sil",
|
||||
"move-note-down": "Notu aşağıya kaydır"
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,4 +387,58 @@ describe("Markdown export", () => {
|
||||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||||
});
|
||||
|
||||
it("maintains escaped HTML tags", () => {
|
||||
const html = /*html*/`<p><div>Hello World</div></p>`;
|
||||
const expected = `\\<div\\>Hello World\\</div\\>`;
|
||||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||||
});
|
||||
|
||||
it("escapes HTML tags inside list", () => {
|
||||
const html = trimIndentation/*html*/`\
|
||||
<ul>
|
||||
<li data-list-item-id="e07fda078f7dd7103a3b9017f49eb1589">
|
||||
<note> is note.
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
const expected = trimIndentation`\
|
||||
* \\<note\\> is note.`;
|
||||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||||
});
|
||||
|
||||
it("exports jQuery code in table properly", () => {
|
||||
const html = trimIndentation`\
|
||||
<figure class="table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Code
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<pre>
|
||||
<code class="language-text-x-trilium-auto">this.$widget = $("<div>");</code>
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
`;
|
||||
const expected = trimIndentation`\
|
||||
<table><thead><tr><th>Code</th></tr></thead><tbody><tr><td><pre><code class="language-text-x-trilium-auto">this.$widget = $("<div>");</code>
|
||||
</pre></td></tr></tbody></table>`;
|
||||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||||
});
|
||||
|
||||
it("renders emphasis with underscore", () => {
|
||||
const html = /*html*/`<p>This is <em>underlined</em> text.</p>`;
|
||||
const expected = `This is _underlined_ text.`;
|
||||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
import TurndownService, { type Rule } from "turndown";
|
||||
import { gfm } from "@triliumnext/turndown-plugin-gfm";
|
||||
import Turnish, { type Rule } from "turnish";
|
||||
|
||||
let instance: TurndownService | null = null;
|
||||
let instance: Turnish | null = null;
|
||||
|
||||
// TODO: Move this to a dedicated file someday.
|
||||
export const ADMONITION_TYPE_MAPPINGS: Record<string, string> = {
|
||||
@ -16,12 +14,12 @@ export const ADMONITION_TYPE_MAPPINGS: Record<string, string> = {
|
||||
|
||||
export const DEFAULT_ADMONITION_TYPE = ADMONITION_TYPE_MAPPINGS.note;
|
||||
|
||||
const fencedCodeBlockFilter: TurndownService.Rule = {
|
||||
filter: function (node, options) {
|
||||
const fencedCodeBlockFilter: Rule = {
|
||||
filter (node, options) {
|
||||
return options.codeBlockStyle === "fenced" && node.nodeName === "PRE" && node.firstChild !== null && node.firstChild.nodeName === "CODE";
|
||||
},
|
||||
|
||||
replacement: function (content, node, options) {
|
||||
replacement (content, node, options) {
|
||||
if (!node.firstChild || !("getAttribute" in node.firstChild) || typeof node.firstChild.getAttribute !== "function") {
|
||||
return content;
|
||||
}
|
||||
@ -29,23 +27,25 @@ const fencedCodeBlockFilter: TurndownService.Rule = {
|
||||
const className = node.firstChild.getAttribute("class") || "";
|
||||
const language = rewriteLanguageTag((className.match(/language-(\S+)/) || [null, ""])[1]);
|
||||
|
||||
return "\n\n" + options.fence + language + "\n" + node.firstChild.textContent + "\n" + options.fence + "\n\n";
|
||||
return `\n\n${options.fence}${language}\n${node.firstChild.textContent}\n${options.fence}\n\n`;
|
||||
}
|
||||
};
|
||||
|
||||
function toMarkdown(content: string) {
|
||||
if (instance === null) {
|
||||
instance = new TurndownService({
|
||||
instance = new Turnish({
|
||||
headingStyle: "atx",
|
||||
bulletListMarker: "*",
|
||||
emDelimiter: "_",
|
||||
codeBlockStyle: "fenced",
|
||||
blankReplacement(content, node, options) {
|
||||
if (node.nodeName === "SECTION" && (node as HTMLElement).classList.contains("include-note")) {
|
||||
return (node as HTMLElement).outerHTML;
|
||||
blankReplacement(_content, node) {
|
||||
if (node.nodeName === "SECTION" && node.classList.contains("include-note")) {
|
||||
return node.outerHTML;
|
||||
}
|
||||
|
||||
// Original implementation as per https://github.com/mixmark-io/turndown/blob/master/src/turndown.js.
|
||||
return ("isBlock" in node && node.isBlock) ? '\n\n' : ''
|
||||
}
|
||||
return ("isBlock" in node && node.isBlock) ? '\n\n' : '';
|
||||
},
|
||||
});
|
||||
// Filter is heavily based on: https://github.com/mixmark-io/turndown/issues/274#issuecomment-458730974
|
||||
instance.addRule("fencedCodeBlock", fencedCodeBlockFilter);
|
||||
@ -59,7 +59,7 @@ function toMarkdown(content: string) {
|
||||
instance.keep([ "kbd", "sup", "sub" ]);
|
||||
}
|
||||
|
||||
return instance.turndown(content);
|
||||
return instance.render(content);
|
||||
}
|
||||
|
||||
function rewriteLanguageTag(source: string) {
|
||||
@ -85,14 +85,14 @@ function buildImageFilter() {
|
||||
const ESCAPE_PATTERNS = {
|
||||
before: /([\\*`[\]_]|(?:^[-+>])|(?:^~~~)|(?:^#{1-6}))/g,
|
||||
after: /((?:^\d+(?=\.)))/
|
||||
}
|
||||
};
|
||||
|
||||
const escapePattern = new RegExp('(?:' + ESCAPE_PATTERNS.before.source + '|' + ESCAPE_PATTERNS.after.source + ')', 'g');
|
||||
const escapePattern = new RegExp(`(?:${ESCAPE_PATTERNS.before.source}|${ESCAPE_PATTERNS.after.source})`, 'g');
|
||||
|
||||
function escapeMarkdown (content: string) {
|
||||
return content.replace(escapePattern, function (match, before, after) {
|
||||
return before ? '\\' + before : after + '\\'
|
||||
})
|
||||
return content.replace(escapePattern, (match, before, after) => {
|
||||
return before ? `\\${before}` : `${after}\\`;
|
||||
});
|
||||
}
|
||||
|
||||
function escapeLinkDestination(destination: string) {
|
||||
@ -102,10 +102,10 @@ function buildImageFilter() {
|
||||
}
|
||||
|
||||
function escapeLinkTitle (title: string) {
|
||||
return title.replace(/"/g, '\\"')
|
||||
return title.replace(/"/g, '\\"');
|
||||
}
|
||||
|
||||
const imageFilter: TurndownService.Rule = {
|
||||
const imageFilter: Rule = {
|
||||
filter: "img",
|
||||
replacement(content, _node) {
|
||||
const node = _node as HTMLElement;
|
||||
@ -117,12 +117,12 @@ function buildImageFilter() {
|
||||
|
||||
// TODO: Deduplicate with upstream.
|
||||
const untypedNode = (node as any);
|
||||
const alt = escapeMarkdown(cleanAttribute(untypedNode.getAttribute('alt')))
|
||||
const src = escapeLinkDestination(untypedNode.getAttribute('src') || '')
|
||||
const title = cleanAttribute(untypedNode.getAttribute('title'))
|
||||
const titlePart = title ? ' "' + escapeLinkTitle(title) + '"' : ''
|
||||
const alt = escapeMarkdown(cleanAttribute(untypedNode.getAttribute('alt')));
|
||||
const src = escapeLinkDestination(untypedNode.getAttribute('src') || '');
|
||||
const title = cleanAttribute(untypedNode.getAttribute('title'));
|
||||
const titlePart = title ? ` "${escapeLinkTitle(title)}"` : '';
|
||||
|
||||
return src ? '![' + alt + ']' + '(' + src + titlePart + ')' : ''
|
||||
return src ? `` : '';
|
||||
}
|
||||
};
|
||||
return imageFilter;
|
||||
@ -151,7 +151,7 @@ function buildAdmonitionFilter() {
|
||||
return DEFAULT_ADMONITION_TYPE;
|
||||
}
|
||||
|
||||
const admonitionFilter: TurndownService.Rule = {
|
||||
const admonitionFilter: Rule = {
|
||||
filter(node, options) {
|
||||
return node.nodeName === "ASIDE" && node.classList.contains("admonition");
|
||||
},
|
||||
@ -161,11 +161,11 @@ function buildAdmonitionFilter() {
|
||||
|
||||
content = content.replace(/^\n+|\n+$/g, '');
|
||||
content = content.replace(/^/gm, '> ');
|
||||
content = `> [!${admonitionType}]\n` + content;
|
||||
content = `> [!${admonitionType}]\n${content}`;
|
||||
|
||||
return "\n\n" + content + "\n\n";
|
||||
return `\n\n${content}\n\n`;
|
||||
}
|
||||
}
|
||||
};
|
||||
return admonitionFilter;
|
||||
}
|
||||
|
||||
@ -178,15 +178,15 @@ function buildAdmonitionFilter() {
|
||||
*/
|
||||
function buildInlineLinkFilter(): Rule {
|
||||
return {
|
||||
filter: function (node, options) {
|
||||
filter (node, options) {
|
||||
return (
|
||||
options.linkStyle === 'inlined' &&
|
||||
node.nodeName === 'A' &&
|
||||
!!node.getAttribute('href')
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
replacement: function (content, _node) {
|
||||
replacement (content, _node) {
|
||||
const node = _node as HTMLElement;
|
||||
|
||||
// Return reference links verbatim.
|
||||
@ -196,13 +196,13 @@ function buildInlineLinkFilter(): Rule {
|
||||
|
||||
// Otherwise treat as normal.
|
||||
// TODO: Call super() somehow instead of duplicating the implementation.
|
||||
let href = node.getAttribute('href')
|
||||
if (href) href = href.replace(/([()])/g, '\\$1')
|
||||
let title = cleanAttribute(node.getAttribute('title'))
|
||||
if (title) title = ' "' + title.replace(/"/g, '\\"') + '"'
|
||||
return '[' + content + '](' + href + title + ')'
|
||||
let href = node.getAttribute('href');
|
||||
if (href) href = href.replace(/([()])/g, '\\$1');
|
||||
let title = cleanAttribute(node.getAttribute('title'));
|
||||
if (title) title = ` "${title.replace(/"/g, '\\"')}"`;
|
||||
return `[${content}](${href}${title})`;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function buildFigureFilter(): Rule {
|
||||
@ -214,7 +214,7 @@ function buildFigureFilter(): Rule {
|
||||
replacement(content, node) {
|
||||
return (node as HTMLElement).outerHTML;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Keep in line with https://github.com/mixmark-io/turndown/blob/master/src/commonmark-rules.js.
|
||||
@ -224,13 +224,13 @@ function buildListItemFilter(): Rule {
|
||||
replacement(content, node, options) {
|
||||
content = content
|
||||
.trim()
|
||||
.replace(/\n/gm, '\n ') // indent
|
||||
let prefix = options.bulletListMarker + ' '
|
||||
.replace(/\n/gm, '\n '); // indent
|
||||
let prefix = `${options.bulletListMarker} `;
|
||||
const parent = node.parentNode as HTMLElement;
|
||||
if (parent.nodeName === 'OL') {
|
||||
var start = parent.getAttribute('start')
|
||||
var index = Array.prototype.indexOf.call(parent.children, node)
|
||||
prefix = (start ? Number(start) + index : index + 1) + '. '
|
||||
const start = parent.getAttribute('start');
|
||||
const index = Array.prototype.indexOf.call(parent.children, node);
|
||||
prefix = `${start ? Number(start) + index : index + 1}. `;
|
||||
} else if (parent.classList.contains("todo-list")) {
|
||||
const isChecked = node.querySelector("input[type=checkbox]:checked");
|
||||
prefix = (isChecked ? "- [x] " : "- [ ] ");
|
||||
@ -239,7 +239,7 @@ function buildListItemFilter(): Rule {
|
||||
const result = prefix + content + (node.nextSibling && !/\n$/.test(content) ? '\n' : '');
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function buildMathFilter(): Rule {
|
||||
@ -270,13 +270,13 @@ function buildMathFilter(): Rule {
|
||||
// Unknown.
|
||||
return content;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Taken from upstream since it's not exposed.
|
||||
// https://github.com/mixmark-io/turndown/blob/master/src/commonmark-rules.js
|
||||
function cleanAttribute(attribute: string | null | undefined) {
|
||||
return attribute ? attribute.replace(/(\n+\s*)+/g, '\n') : ''
|
||||
return attribute ? attribute.replace(/(\n+\s*)+/g, '\n') : '';
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@ -314,4 +314,9 @@ $$`;
|
||||
expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it("preserves HTML entities in list", () => {
|
||||
const input = `* <note> is note.`;
|
||||
const expected = /*html*/`<ul><li><note> is note.</li></ul>`;
|
||||
expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
"preview": "pnpm build && vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"i18next": "25.7.4",
|
||||
"i18next": "25.8.0",
|
||||
"i18next-http-backend": "3.0.2",
|
||||
"preact": "10.28.2",
|
||||
"preact-iso": "2.11.1",
|
||||
|
||||
@ -24,7 +24,8 @@
|
||||
"mindmap_title": "माइंडमैप"
|
||||
},
|
||||
"extensibility_benefits": {
|
||||
"share_title": "वेब पर नोट्स शेयर करें"
|
||||
"share_title": "वेब पर नोट्स शेयर करें",
|
||||
"share_description": "अगर आपके पास सर्वर है, तो इसका उपयोग अपने नोट्स के एक हिस्से को अन्य लोगों के साथ शेयर करने के लिए किया जा सकता है।"
|
||||
},
|
||||
"collections": {
|
||||
"calendar_title": "कैलेंडर",
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
"productivity_benefits": {
|
||||
"sync_title": "Synkronisering",
|
||||
"search_title": "Kraftig søk",
|
||||
"web_clipper_title": "Web clipper",
|
||||
"web_clipper_title": "Webklipper",
|
||||
"revisions_title": "Notatrevisjon",
|
||||
"protected_notes_title": "Beskyttede notater",
|
||||
"title": "Produktivitet og sikkerhet",
|
||||
@ -87,7 +87,7 @@
|
||||
"github": "GitHub",
|
||||
"matrix": "Matrix",
|
||||
"reddit": "Reddit",
|
||||
"github_discussions": "GitHub Discussions"
|
||||
"github_discussions": "GitHub-diskusjoner"
|
||||
},
|
||||
"support_us": {
|
||||
"paypal": "PayPal",
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
"organization_benefits": {
|
||||
"title": "Organizasyon",
|
||||
"note_structure_title": "Not yapısı",
|
||||
"note_structure_description": "Notlar hiyerarşik olarak düzenlenebilir. Her not 'alt notlar' içerebildiği için klasörlere ihtiyaç duyulmaz. Tek bir not, hiyerarşinin birden fazla noktasına eklenebilir."
|
||||
"note_structure_description": "Notlar hiyerarşik olarak düzenlenebilir. Her not 'alt notlar' içerebildiği için klasörlere ihtiyaç duyulmaz. Tek bir not, hiyerarşinin birden fazla noktasına eklenebilir.",
|
||||
"attributes_description": "Notlar arasında ilişkiler kurun veya kolay kategorizasyon için etiketler ekleyin. Tablolarda ve panolarda kullanılabilen yapılandırılmış bilgileri eklemek için öne çıkan öznitelikleri kullanın.",
|
||||
"hoisting_description": "Kişisel ve iş notlarınızı bir çalışma alanı altında gruplandırarak kolayca ayırın; bu sayede not ağacınız yalnızca belirli bir not kümesini gösterecek şekilde odaklanacaktır."
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# Documentation
|
||||
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/fX56iuus8ySW/Documentation_image.png" width="205" height="162">
|
||||
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/TzNwfb67k5Dh/Documentation_image.png" width="205" height="162">
|
||||
|
||||
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
|
||||
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
|
||||
|
||||
10
docs/README-hi.md
vendored
@ -69,12 +69,14 @@ application with focus on building large personal knowledge bases.
|
||||
|
||||
## 🎁 खासियतें
|
||||
|
||||
* Notes can be arranged into arbitrarily deep tree. Single note can be placed
|
||||
into multiple places in the tree (see
|
||||
* नोट्स को मनचाहे गहरे ट्री (tree) स्ट्रक्चर में व्यवस्थित किया जा सकता है। एक
|
||||
ही नोट को ट्री में कई जगहों पर रखा जा सकता है (देखें
|
||||
[cloning](https://docs.triliumnotes.org/user-guide/concepts/notes/cloning))
|
||||
* Rich WYSIWYG note editor including e.g. tables, images and
|
||||
[math](https://docs.triliumnotes.org/user-guide/note-types/text) with markdown
|
||||
* बेहतरीन WYSIWYG नोट एडिटर, जिसमें टेबल, इमेज और
|
||||
[math](https://docs.triliumnotes.org/user-guide/note-types/text) के साथ-साथ
|
||||
मार्कडाउन
|
||||
[autoformat](https://docs.triliumnotes.org/user-guide/note-types/text/markdown-formatting)
|
||||
की सुविधा शामिल है
|
||||
* [सोर्स कोड वाले
|
||||
नोट्स](https://docs.triliumnotes.org/user-guide/note-types/code) को एडिट करने
|
||||
की सुविधा, जिसमें सिंटैक्स हाइलाइटिंग (syntax highlighting) भी शामिल है
|
||||
|
||||
4
docs/README-id.md
vendored
@ -88,8 +88,8 @@ Dokumentasi kami tersedia dalam berbagai format:
|
||||
[attributes](https://docs.triliumnotes.org/user-guide/advanced-usage/attributes)
|
||||
can be used for note organization, querying and advanced
|
||||
[scripting](https://docs.triliumnotes.org/user-guide/scripts)
|
||||
* UI available in English, German, Spanish, French, Romanian, and Chinese
|
||||
(simplified and traditional)
|
||||
* Antarmuka pengguna tersedia dalam bahasa Inggris, Jerman, Spanyol, Prancis,
|
||||
Rumania, dan Tionghoa (sederhana dan tradisional)
|
||||
* Direct [OpenID and TOTP
|
||||
integration](https://docs.triliumnotes.org/user-guide/setup/server/mfa) for
|
||||
more secure login
|
||||
|
||||
20
docs/README-nb_NO.md
vendored
@ -28,8 +28,8 @@ script)](./README-ZH_TW.md) | [English](../README.md) | [French](./README-fr.md)
|
||||
[Spanish](./README-es.md)
|
||||
<!-- translate:on -->
|
||||
|
||||
Trilium Notes is a free and open-source, cross-platform hierarchical note taking
|
||||
application with focus on building large personal knowledge bases.
|
||||
Trilium Notes er et gratis og åpen kildekode-basert, plattformuavhengig
|
||||
hierarkisk notatprogram med fokus på å bygge store personlige kunnskapsbaser.
|
||||
|
||||
<img src="./app.png" alt="Trilium Screenshot" width="1000">
|
||||
|
||||
@ -62,8 +62,8 @@ Vår dokumentasjon er tilgjengelig i flere format:
|
||||
TriliumNext](https://docs.triliumnotes.org/user-guide/setup/upgrading)
|
||||
- [Grunnleggende konsepter og
|
||||
funksjoner](https://docs.triliumnotes.org/user-guide/concepts/notes)
|
||||
- [Patterns of Personal Knowledge
|
||||
Base](https://docs.triliumnotes.org/user-guide/misc/patterns-of-personal-knowledge)
|
||||
- [Modeller for personlig
|
||||
kunnskapsbase](https://docs.triliumnotes.org/user-guide/misc/patterns-of-personal-knowledge)
|
||||
|
||||
## 🎁 Funksjoner
|
||||
|
||||
@ -109,8 +109,8 @@ Vår dokumentasjon er tilgjengelig i flere format:
|
||||
* Mind maps, based on [Mind Elixir](https://docs.mind-elixir.com/)
|
||||
* [Geo maps](https://docs.triliumnotes.org/user-guide/collections/geomap) with
|
||||
location pins and GPX tracks
|
||||
* [Scripting](https://docs.triliumnotes.org/user-guide/scripts) - see [Advanced
|
||||
showcases](https://docs.triliumnotes.org/user-guide/advanced-usage/advanced-showcases)
|
||||
* [Skripting](https://docs.triliumnotes.org/user-guide/scripts) - se [Avanserte
|
||||
bruksområder](https://docs.triliumnotes.org/user-guide/advanced-usage/advanced-showcases)
|
||||
* [REST API](https://docs.triliumnotes.org/user-guide/advanced-usage/etapi) for
|
||||
automatisering
|
||||
* Scales well in both usability and performance upwards of 100 000 notes
|
||||
@ -156,13 +156,13 @@ compatible with the latest zadam/trilium version of
|
||||
versions of TriliumNext/Trilium have their sync versions incremented which
|
||||
prevents direct migration.
|
||||
|
||||
## 💬 Discuss with us
|
||||
## 💬Diskuter med oss
|
||||
|
||||
Feel free to join our official conversations. We would love to hear what
|
||||
features, suggestions, or issues you may have!
|
||||
|
||||
- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous
|
||||
discussions.)
|
||||
- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synkrone
|
||||
diskusjoner.)
|
||||
- The `General` Matrix room is also bridged to
|
||||
[XMPP](xmpp:discuss@trilium.thisgreat.party?join)
|
||||
- [Github Discussions](https://github.com/TriliumNext/Trilium/discussions) (For
|
||||
@ -322,7 +322,7 @@ Consider supporting the main developer
|
||||
|
||||
- [GitHub Sponsors](https://github.com/sponsors/eliandoran)
|
||||
- [PayPal](https://paypal.me/eliandoran)
|
||||
- [Buy Me a Coffee](https://buymeacoffee.com/eliandoran)
|
||||
- [Spander en kaffe](https://buymeacoffee.com/eliandoran)
|
||||
|
||||
## 🔑 Lisens
|
||||
|
||||
|
||||
11
docs/README-tr.md
vendored
@ -57,13 +57,10 @@ Dokümantasyonumuz birden fazla formatta mevcuttur:
|
||||
|
||||
### Hızlı linkler
|
||||
- [Başlangıç Kılavuzu](https://docs.triliumnotes.org/)
|
||||
- [Installation Instructions](https://docs.triliumnotes.org/user-guide/setup)
|
||||
- [Docker
|
||||
Setup](https://docs.triliumnotes.org/user-guide/setup/server/installation/docker)
|
||||
- [Upgrading
|
||||
TriliumNext](https://docs.triliumnotes.org/user-guide/setup/upgrading)
|
||||
- [Basic Concepts and
|
||||
Features](https://docs.triliumnotes.org/user-guide/concepts/notes)
|
||||
- Kurulum Klavuzu
|
||||
- Docker kurulumu
|
||||
- [TrilliumNext Güncelleme]
|
||||
- Basit Kavramlar ve Özellikler
|
||||
- [Patterns of Personal Knowledge
|
||||
Base](https://docs.triliumnotes.org/user-guide/misc/patterns-of-personal-knowledge)
|
||||
|
||||
|
||||
2
docs/Release Notes/Release Notes/v0.101.2.md
vendored
@ -18,5 +18,5 @@
|
||||
* [Max content width is not respected when switching between note types in the same tab](https://github.com/TriliumNext/Trilium/issues/8065)
|
||||
* [Crash When a Note Includes Itself](https://github.com/TriliumNext/Trilium/issues/8294)
|
||||
* [Severe Performance Degradation and Crash Issues Due to Recursive Inclusion in Included Notes](https://github.com/TriliumNext/Trilium/issues/8017)
|
||||
* [<note> is not a launcher even though it's in the launcher subtree](https://github.com/TriliumNext/Trilium/issues/8218)
|
||||
* [is not a launcher even though it's in the launcher subtree](https://github.com/TriliumNext/Trilium/issues/8218)
|
||||
* [Archived subnotes of direct children appear in grid view without #includeArchived](https://github.com/TriliumNext/Trilium/issues/8184)
|
||||
106
docs/User Guide/!!!meta.json
vendored
@ -6169,6 +6169,34 @@
|
||||
"type": "text",
|
||||
"mime": "text/markdown",
|
||||
"attributes": [
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "Gr6xFaF6ioJ5",
|
||||
"isInheritable": false,
|
||||
"position": 10
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "WOcw2SLH6tbX",
|
||||
"isInheritable": false,
|
||||
"position": 20
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "wy8So3yZZlH9",
|
||||
"isInheritable": false,
|
||||
"position": 30
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "dj3j8dG4th4l",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"name": "shareAlias",
|
||||
@ -6182,20 +6210,6 @@
|
||||
"value": "bx bx-window-open",
|
||||
"isInheritable": false,
|
||||
"position": 30
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "dj3j8dG4th4l",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "wy8So3yZZlH9",
|
||||
"isInheritable": false,
|
||||
"position": 50
|
||||
}
|
||||
],
|
||||
"format": "markdown",
|
||||
@ -14189,7 +14203,7 @@
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "Vc8PjrjAGuOp",
|
||||
"value": "x3i7MxGccDuM",
|
||||
"isInheritable": false,
|
||||
"position": 10
|
||||
},
|
||||
@ -14200,6 +14214,20 @@
|
||||
"isInheritable": false,
|
||||
"position": 20
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "IjZS7iK5EXtb",
|
||||
"isInheritable": false,
|
||||
"position": 30
|
||||
},
|
||||
{
|
||||
"type": "relation",
|
||||
"name": "internalLink",
|
||||
"value": "XpOYSgsLkTJy",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"name": "iconClass",
|
||||
@ -14226,29 +14254,21 @@
|
||||
"position": 10,
|
||||
"dataFileName": "SQL Console_image.png"
|
||||
},
|
||||
{
|
||||
"attachmentId": "827EgLgWhZWF",
|
||||
"title": "image.png",
|
||||
"role": "image",
|
||||
"mime": "image/jpg",
|
||||
"position": 10,
|
||||
"dataFileName": "1_SQL Console_image.png"
|
||||
},
|
||||
{
|
||||
"attachmentId": "gIbK7NNLu3iZ",
|
||||
"title": "image.png",
|
||||
"role": "image",
|
||||
"mime": "image/png",
|
||||
"position": 10,
|
||||
"dataFileName": "2_SQL Console_image.png"
|
||||
"dataFileName": "1_SQL Console_image.png"
|
||||
},
|
||||
{
|
||||
"attachmentId": "pP87PB9ELjQn",
|
||||
"attachmentId": "wdBs3e0MApgs",
|
||||
"title": "image.png",
|
||||
"role": "image",
|
||||
"mime": "image/png",
|
||||
"position": 10,
|
||||
"dataFileName": "3_SQL Console_image.png"
|
||||
"dataFileName": "2_SQL Console_image.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -17566,6 +17586,40 @@
|
||||
"dataFileName": "Logging_image.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"isClone": false,
|
||||
"noteId": "cNpC0ITcfX0N",
|
||||
"notePath": [
|
||||
"pOsGYCXsbNQG",
|
||||
"CdNpE2pqjmI6",
|
||||
"cNpC0ITcfX0N"
|
||||
],
|
||||
"title": "Breaking changes",
|
||||
"notePosition": 130,
|
||||
"prefix": null,
|
||||
"isExpanded": false,
|
||||
"type": "text",
|
||||
"mime": "text/html",
|
||||
"attributes": [
|
||||
{
|
||||
"type": "label",
|
||||
"name": "iconClass",
|
||||
"value": "bx bx-up-arrow-alt",
|
||||
"isInheritable": false,
|
||||
"position": 30
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"name": "shareAlias",
|
||||
"value": "breaking-changes",
|
||||
"isInheritable": false,
|
||||
"position": 40
|
||||
}
|
||||
],
|
||||
"format": "markdown",
|
||||
"dataFileName": "Breaking changes.md",
|
||||
"attachments": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 230 B |
|
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 265 B |
@ -1,7 +1,7 @@
|
||||
# SQL Console
|
||||
The SQL Console is Trilium's built-in database editor.
|
||||
|
||||
It can be accessed by going to the [global menu](../../../Basic%20Concepts%20and%20Features/UI%20Elements) → Advanced → Open SQL Console.
|
||||
It can be accessed by going to the <a class="reference-link" href="../../../Basic%20Concepts%20and%20Features/UI%20Elements/Global%20menu.md">Global menu</a> → Advanced → Open SQL Console.
|
||||
|
||||

|
||||
|
||||
@ -9,16 +9,26 @@ It can be accessed by going to the [global menu](../../../Basic%20Concepts%20and
|
||||
|
||||
* Hovering the mouse over one of the tables listed at the top of the document will show the columns and their data type.
|
||||
* Only one SQL statement can be run at once.
|
||||
* To run the statement, press the icon.
|
||||
* To run the statement, press the _Execute_ icon.
|
||||
* For queries that return a result, the data will displayed in a table.
|
||||
|
||||

|
||||
* For statements (e.g. `INSERT`, `UPDATE`), the number of affected rows is displayed.
|
||||
|
||||
<figure class="image"><img style="aspect-ratio:1124/571;" src="2_SQL Console_image.png" width="1124" height="571"></figure>
|
||||
|
||||
### Interacting with the table
|
||||
|
||||
After executing a query, a table with the results will be displayed:
|
||||
|
||||
* Clicking on a column allows sorting ascending or descending.
|
||||
* Underneath each column there is an input field which allows filtering by text.
|
||||
* Press <kbd>Ctrl</kbd>+<kbd>C</kbd> to copy the current cell to clipboard.
|
||||
* Multiple cells can be selected by dragging or by holding <kbd>Shift</kbd> + arrow keys
|
||||
* Results are paginated for performance reasons. The controls at the bottom of the table can be used to navigate through pages.
|
||||
|
||||
### Saved SQL console
|
||||
|
||||
SQL queries or commands can be saved into a dedicated note.
|
||||
|
||||
To do so, simply write the query and press the button. Once saved, the note will appear in [Day Notes](../../Advanced%20Showcases/Day%20Notes.md).
|
||||
To do so, simply write the query and press the  button. Once saved, the note will appear in <a class="reference-link" href="../../Advanced%20Showcases/Day%20Notes.md">Day Notes</a>.
|
||||
|
||||
* The SQL expression will not be displayed by default, but it can still be viewed by going to the note context menu and selecting _Note source_.
|
||||
* The expression cannot be modified. If needed, recreate it by copying the statement back into the SQL console and then saving it again.
|
||||
The note can be locked for editing by pressing the _Lock_ button in the note actions section near the title bar (on the <a class="reference-link" href="../../../Basic%20Concepts%20and%20Features/UI%20Elements/New%20Layout.md">New Layout</a>, or in the <a class="reference-link" href="../../../Basic%20Concepts%20and%20Features/UI%20Elements/Floating%20buttons.md">Floating buttons</a> area if using the old layout). When editing is locked, the SQL statement is hidden from view.
|
||||
@ -5,8 +5,8 @@ Trilium can import ENEX files, which are used by Evernote for backup/export. One
|
||||
|
||||
To export ENEX files from Evernote, you can use:
|
||||
|
||||
* Evernote desktop application. See Evernote documentation. Note that the limitation of this method is that you can only export 100 notes at a time or one notebook at a time.
|
||||
* A third-party evernote-backup CLI tool. This tool can export all of your notebooks in bulk.
|
||||
* Evernote desktop application. See Evernote [documentation](https://help.evernote.com/hc/en-us/articles/209005557-Export-Notes-and-Notebooks-as-ENEX-or-HTML). Note that the limitation of this method is that you can only export 100 notes at a time or one notebook at a time.
|
||||
* A third-party [evernote-backup](https://github.com/vzhd1701/evernote-backup) CLI tool. This tool can export all of your notebooks in bulk.
|
||||
|
||||
## Import ENEX in Trilium
|
||||
|
||||
@ -21,7 +21,7 @@ Once you have your ENEX files, do the following to import them in Trilium:
|
||||
A non-exhaustive list of what the importer preserves:
|
||||
|
||||
* Attachments
|
||||
* The hierarchy of headings (these are shifted to start with H2 because H1 is reserved for note title, see Headings)
|
||||
* The hierarchy of headings (these are shifted to start with H2 because H1 is reserved for note title, see [Headings](../../Note%20Types/Text/General%20formatting.md))
|
||||
* Tables
|
||||
* Bulleted lists
|
||||
* Numbered lists
|
||||
@ -37,9 +37,9 @@ However, we do not guarantee that all of your formatting will be imported 100% c
|
||||
|
||||
## Limitations
|
||||
|
||||
* The size limit of one import is 250Mb. If the total size of your files is larger, you can increase the upload limit, or divide your files, and run the import as many times as necessary.
|
||||
* The size limit of one import is 250Mb. If the total size of your files is larger, you can increase the [upload limit](../../Installation%20%26%20Setup/Server%20Installation.md), or divide your files, and run the import as many times as necessary.
|
||||
* All resources (except for images) are created as notes’ attachments.
|
||||
* If you have HTML inside ENEX files, the HTML formatting may be broken or lost after import in Trilium. You can report major problems at Trilium issue tracker.
|
||||
* If you have HTML inside ENEX files, the HTML formatting may be broken or lost after import in Trilium. See <a class="reference-link" href="../../Troubleshooting/Reporting%20issues.md">Reporting issues</a>.
|
||||
|
||||
### Internal links
|
||||
|
||||
|
||||
@ -28,4 +28,4 @@ Sorting is done by comparing note properties or specific labels on child notes.
|
||||
* **Label Sorting**: If `#sorted` has any other value, this value is treated as the name of a child note's label, and sorting is based on the values of this label. For example, setting `#sorted=myOrder` on the parent note and using `#myOrder=001`, `#myOrder=002`, etc., on child notes.
|
||||
4. **Alphabetical Sorting**: Used as a last resort when other criteria result in equality.
|
||||
|
||||
All comparisons are made string-wise (e.g., "1" < "2" or "2020-10-10" < "2021-01-15", but also "2" > "10").
|
||||
All comparisons are made string-wise (e.g., "1" \< "2" or "2020-10-10" < "2021-01-15", but also "2" \> "10").
|
||||
14
docs/User Guide/User Guide/Scripting/Breaking changes.md
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
# Breaking changes
|
||||
## v0.102.0: Upgrade to jQuery 4.0.0
|
||||
|
||||
jQuery 4 removes legacy browser support (such as IE11 support), but it also removes some APIs that are considered deprecated such as:
|
||||
|
||||
> `jQuery.isArray`, `jQuery.parseJSON`, `jQuery.trim`, `jQuery.type`, `jQuery.now`, `jQuery.isNumeric`, `jQuery.isFunction`, `jQuery.isWindow`, `jQuery.camelCase`, `jQuery.nodeName`, `jQuery.cssNumber`, `jQuery.cssProps`, and `jQuery.fx.interval`.
|
||||
>
|
||||
> Use native equivalents like `Array.isArray()`, `JSON.parse()`, `String.prototype.trim()`, and `Date.now()` instead.
|
||||
|
||||
This may affect custom scripts if they (or the custom jQuery libraries used) depend on the deprecated APIs.
|
||||
|
||||
Note that Trilium polyfills `jQuery.isArray`, `isFunction` and `isPlainObject` because they were required by one of our dependencies (the autocomplete).
|
||||
|
||||
For more information, consult [the official blog post](https://blog.jquery.com/2026/01/17/jquery-4-0-0/).
|
||||
@ -63,7 +63,7 @@
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-plugin-playwright": "2.5.0",
|
||||
"eslint-plugin-simple-import-sort": "12.1.1",
|
||||
"happy-dom": "20.3.0",
|
||||
"happy-dom": "20.3.4",
|
||||
"http-server": "14.1.1",
|
||||
"jiti": "2.6.1",
|
||||
"js-yaml": "4.1.1",
|
||||
@ -93,7 +93,7 @@
|
||||
"url": "https://github.com/TriliumNext/Trilium/issues"
|
||||
},
|
||||
"homepage": "https://triliumnotes.org",
|
||||
"packageManager": "pnpm@10.28.0",
|
||||
"packageManager": "pnpm@10.28.1",
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"@ckeditor/ckeditor5-mention": "patches/@ckeditor__ckeditor5-mention.patch",
|
||||
@ -115,7 +115,7 @@
|
||||
"on-headers@<1.1.0": ">=1.1.0",
|
||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.4",
|
||||
"form-data@>=3.0.0 <3.0.4": ">=3.0.4",
|
||||
"node-abi": "4.24.0"
|
||||
"node-abi": "4.26.0"
|
||||
},
|
||||
"ignoredBuiltDependencies": [
|
||||
"sqlite3"
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
"@typescript-eslint/parser": "8.53.0",
|
||||
"@vitest/browser": "4.0.17",
|
||||
"@vitest/coverage-istanbul": "4.0.17",
|
||||
"ckeditor5": "47.3.0",
|
||||
"ckeditor5": "47.4.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-config-ckeditor5": ">=9.1.0",
|
||||
"http-server": "14.1.1",
|
||||
@ -39,10 +39,10 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "2.0.0",
|
||||
"vitest": "4.0.17",
|
||||
"webdriverio": "9.23.0"
|
||||
"webdriverio": "9.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.3.0"
|
||||
"ckeditor5": "47.4.0"
|
||||
},
|
||||
"author": "Elian Doran <contact@eliandoran.me>",
|
||||
"license": "GPL-2.0-or-later",
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
"@typescript-eslint/parser": "8.53.0",
|
||||
"@vitest/browser": "4.0.17",
|
||||
"@vitest/coverage-istanbul": "4.0.17",
|
||||
"ckeditor5": "47.3.0",
|
||||
"ckeditor5": "47.4.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-config-ckeditor5": ">=9.1.0",
|
||||
"http-server": "14.1.1",
|
||||
@ -40,10 +40,10 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "2.0.0",
|
||||
"vitest": "4.0.17",
|
||||
"webdriverio": "9.23.0"
|
||||
"webdriverio": "9.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.3.0"
|
||||
"ckeditor5": "47.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node ./scripts/build-dist.mjs",
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
"@typescript-eslint/parser": "8.53.0",
|
||||
"@vitest/browser": "4.0.17",
|
||||
"@vitest/coverage-istanbul": "4.0.17",
|
||||
"ckeditor5": "47.3.0",
|
||||
"ckeditor5": "47.4.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-config-ckeditor5": ">=9.1.0",
|
||||
"http-server": "14.1.1",
|
||||
@ -42,10 +42,10 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "2.0.0",
|
||||
"vitest": "4.0.17",
|
||||
"webdriverio": "9.23.0"
|
||||
"webdriverio": "9.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.3.0"
|
||||
"ckeditor5": "47.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node ./scripts/build-dist.mjs",
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
"@typescript-eslint/parser": "8.53.0",
|
||||
"@vitest/browser": "4.0.17",
|
||||
"@vitest/coverage-istanbul": "4.0.17",
|
||||
"ckeditor5": "47.3.0",
|
||||
"ckeditor5": "47.4.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-config-ckeditor5": ">=9.1.0",
|
||||
"http-server": "14.1.1",
|
||||
@ -42,10 +42,10 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "2.0.0",
|
||||
"vitest": "4.0.17",
|
||||
"webdriverio": "9.23.0"
|
||||
"webdriverio": "9.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.3.0"
|
||||
"ckeditor5": "47.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node ./scripts/build-dist.mjs",
|
||||
@ -70,7 +70,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@ckeditor/ckeditor5-icons": "47.3.0",
|
||||
"@ckeditor/ckeditor5-icons": "47.4.0",
|
||||
"mathlive": "0.108.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
"@typescript-eslint/parser": "8.53.0",
|
||||
"@vitest/browser": "4.0.17",
|
||||
"@vitest/coverage-istanbul": "4.0.17",
|
||||
"ckeditor5": "47.3.0",
|
||||
"ckeditor5": "47.4.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-config-ckeditor5": ">=9.1.0",
|
||||
"http-server": "14.1.1",
|
||||
@ -42,10 +42,10 @@
|
||||
"typescript": "5.9.3",
|
||||
"vite-plugin-svgo": "2.0.0",
|
||||
"vitest": "4.0.17",
|
||||
"webdriverio": "9.23.0"
|
||||
"webdriverio": "9.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ckeditor5": "47.3.0"
|
||||
"ckeditor5": "47.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node ./scripts/build-dist.mjs",
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
"@triliumnext/ckeditor5-keyboard-marker": "workspace:*",
|
||||
"@triliumnext/ckeditor5-math": "workspace:*",
|
||||
"@triliumnext/ckeditor5-mermaid": "workspace:*",
|
||||
"ckeditor5": "47.3.0",
|
||||
"ckeditor5-premium-features": "47.3.0"
|
||||
"ckeditor5": "47.4.0",
|
||||
"ckeditor5-premium-features": "47.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@smithy/middleware-retry": "4.4.23",
|
||||
"@smithy/middleware-retry": "4.4.26",
|
||||
"@types/jquery": "3.5.33"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
|
||||
import { EditorView, highlightActiveLine, keymap, lineNumbers, placeholder, ViewPlugin, ViewUpdate, type EditorViewConfig } from "@codemirror/view";
|
||||
import { EditorView, highlightActiveLine, keymap, lineNumbers, placeholder, ViewPlugin, ViewUpdate, type EditorViewConfig, KeyBinding } from "@codemirror/view";
|
||||
import { defaultHighlightStyle, StreamLanguage, syntaxHighlighting, indentUnit, bracketMatching, foldGutter, codeFolding } from "@codemirror/language";
|
||||
import { Compartment, EditorSelection, EditorState, type Extension } from "@codemirror/state";
|
||||
import { highlightSelectionMatches } from "@codemirror/search";
|
||||
@ -12,6 +12,17 @@ import { createSearchHighlighter, SearchHighlighter, searchMatchHighlightTheme }
|
||||
|
||||
export { default as ColorThemes, type ThemeDefinition, getThemeById } from "./color_themes.js";
|
||||
|
||||
// Custom keymap to prevent Ctrl+Enter from inserting a newline
|
||||
// This allows the parent application to handle the shortcut (e.g., for "Run Active Note")
|
||||
const preventCtrlEnterKeymap: readonly KeyBinding[] = [
|
||||
{
|
||||
key: "Ctrl-Enter",
|
||||
mac: "Cmd-Enter",
|
||||
run: () => true, // Return true to mark event as handled, preventing default newline insertion
|
||||
preventDefault: true
|
||||
}
|
||||
];
|
||||
|
||||
type ContentChangedListener = () => void;
|
||||
|
||||
export interface EditorConfig {
|
||||
@ -59,6 +70,7 @@ export default class CodeMirror extends EditorView {
|
||||
lineNumbers(),
|
||||
indentUnit.of(" ".repeat(4)),
|
||||
keymap.of([
|
||||
...preventCtrlEnterKeymap,
|
||||
...defaultKeymap,
|
||||
...historyKeymap,
|
||||
...smartIndentWithTab
|
||||
|
||||