mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 18:05:55 +01:00 
			
		
		
		
	Merge branch 'main' into feat/llm-tool-improvement
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -120,7 +120,7 @@ jobs: | |||||||
|           body_path: docs/Release Notes/Release Notes/${{ github.ref_name }}.md |           body_path: docs/Release Notes/Release Notes/${{ github.ref_name }}.md | ||||||
|           fail_on_unmatched_files: true |           fail_on_unmatched_files: true | ||||||
|           files: upload/*.* |           files: upload/*.* | ||||||
|           discussion_category_name: Announcements |           discussion_category_name: Releases | ||||||
|           make_latest: ${{ !contains(github.ref, 'rc') }} |           make_latest: ${{ !contains(github.ref, 'rc') }} | ||||||
|           prerelease: ${{ contains(github.ref, 'rc') }} |           prerelease: ${{ contains(github.ref, 'rc') }} | ||||||
|           token: ${{ secrets.RELEASE_PAT }} |           token: ${{ secrets.RELEASE_PAT }} | ||||||
|   | |||||||
| @@ -35,13 +35,13 @@ | |||||||
|     "chore:generate-openapi": "tsx bin/generate-openapi.js" |     "chore:generate-openapi": "tsx bin/generate-openapi.js" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": {     |   "devDependencies": {     | ||||||
|     "@playwright/test": "1.53.1", |     "@playwright/test": "1.53.2", | ||||||
|     "@stylistic/eslint-plugin": "5.0.0",         |     "@stylistic/eslint-plugin": "5.1.0",         | ||||||
|     "@types/express": "5.0.3",     |     "@types/express": "5.0.3",     | ||||||
|     "@types/node": "22.15.33",     |     "@types/node": "22.16.0",     | ||||||
|     "@types/yargs": "17.0.33", |     "@types/yargs": "17.0.33", | ||||||
|     "@vitest/coverage-v8": "3.2.4", |     "@vitest/coverage-v8": "3.2.4", | ||||||
|     "eslint": "9.29.0", |     "eslint": "9.30.1", | ||||||
|     "eslint-plugin-simple-import-sort": "12.1.1", |     "eslint-plugin-simple-import-sort": "12.1.1", | ||||||
|     "esm": "3.2.25", |     "esm": "3.2.25", | ||||||
|     "jsdoc": "4.0.4", |     "jsdoc": "4.0.4", | ||||||
| @@ -49,7 +49,7 @@ | |||||||
|     "rcedit": "4.0.1", |     "rcedit": "4.0.1", | ||||||
|     "rimraf": "6.0.1",     |     "rimraf": "6.0.1",     | ||||||
|     "tslib": "2.8.1",     |     "tslib": "2.8.1",     | ||||||
|     "typedoc": "0.28.5", |     "typedoc": "0.28.7", | ||||||
|     "typedoc-plugin-missing-exports": "4.0.0" |     "typedoc-plugin-missing-exports": "4.0.0" | ||||||
|   }, |   }, | ||||||
|   "optionalDependencies": { |   "optionalDependencies": { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@triliumnext/client", |   "name": "@triliumnext/client", | ||||||
|   "version": "0.95.0", |   "version": "0.96.0", | ||||||
|   "description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)", |   "description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)", | ||||||
|   "private": true, |   "private": true, | ||||||
|   "license": "AGPL-3.0-only", |   "license": "AGPL-3.0-only", | ||||||
| @@ -10,14 +10,14 @@ | |||||||
|     "url": "https://github.com/TriliumNext/Notes" |     "url": "https://github.com/TriliumNext/Notes" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@eslint/js": "9.29.0", |     "@eslint/js": "9.30.1", | ||||||
|     "@excalidraw/excalidraw": "0.18.0", |     "@excalidraw/excalidraw": "0.18.0", | ||||||
|     "@fullcalendar/core": "6.1.17", |     "@fullcalendar/core": "6.1.18", | ||||||
|     "@fullcalendar/daygrid": "6.1.17", |     "@fullcalendar/daygrid": "6.1.18", | ||||||
|     "@fullcalendar/interaction": "6.1.17", |     "@fullcalendar/interaction": "6.1.18", | ||||||
|     "@fullcalendar/list": "6.1.17", |     "@fullcalendar/list": "6.1.18", | ||||||
|     "@fullcalendar/multimonth": "6.1.17", |     "@fullcalendar/multimonth": "6.1.18", | ||||||
|     "@fullcalendar/timegrid": "6.1.17", |     "@fullcalendar/timegrid": "6.1.18", | ||||||
|     "@mermaid-js/layout-elk": "0.1.8", |     "@mermaid-js/layout-elk": "0.1.8", | ||||||
|     "@mind-elixir/node-menu": "1.0.5", |     "@mind-elixir/node-menu": "1.0.5", | ||||||
|     "@popperjs/core": "2.11.8", |     "@popperjs/core": "2.11.8", | ||||||
| @@ -33,9 +33,9 @@ | |||||||
|     "dayjs-plugin-utc": "0.1.2", |     "dayjs-plugin-utc": "0.1.2", | ||||||
|     "debounce": "2.2.0", |     "debounce": "2.2.0", | ||||||
|     "draggabilly": "3.0.0", |     "draggabilly": "3.0.0", | ||||||
|     "force-graph": "1.49.6", |     "force-graph": "1.50.1", | ||||||
|     "globals": "16.2.0", |     "globals": "16.3.0", | ||||||
|     "i18next": "25.2.1", |     "i18next": "25.3.0", | ||||||
|     "i18next-http-backend": "3.0.2", |     "i18next-http-backend": "3.0.2", | ||||||
|     "jquery": "3.7.1", |     "jquery": "3.7.1", | ||||||
|     "jquery-hotkeys": "0.2.2", |     "jquery-hotkeys": "0.2.2", | ||||||
| @@ -46,14 +46,15 @@ | |||||||
|     "leaflet": "1.9.4", |     "leaflet": "1.9.4", | ||||||
|     "leaflet-gpx": "2.2.0", |     "leaflet-gpx": "2.2.0", | ||||||
|     "mark.js": "8.11.1", |     "mark.js": "8.11.1", | ||||||
|     "marked": "15.0.12", |     "marked": "16.0.0", | ||||||
|     "mermaid": "11.7.0", |     "mermaid": "11.8.0", | ||||||
|     "mind-elixir": "4.6.1", |     "mind-elixir": "4.6.2", | ||||||
|     "normalize.css": "8.0.1", |     "normalize.css": "8.0.1", | ||||||
|     "panzoom": "9.4.3", |     "panzoom": "9.4.3", | ||||||
|     "preact": "10.26.9", |     "preact": "10.26.9", | ||||||
|     "split.js": "1.6.5", |     "split.js": "1.6.5", | ||||||
|     "svg-pan-zoom": "3.6.2", |     "svg-pan-zoom": "3.6.2", | ||||||
|  |     "tabulator-tables": "6.3.1", | ||||||
|     "vanilla-js-wheel-zoom": "9.0.4" |     "vanilla-js-wheel-zoom": "9.0.4" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
| @@ -63,6 +64,7 @@ | |||||||
|     "@types/leaflet": "1.9.19", |     "@types/leaflet": "1.9.19", | ||||||
|     "@types/leaflet-gpx": "1.3.7", |     "@types/leaflet-gpx": "1.3.7", | ||||||
|     "@types/mark.js": "8.11.12", |     "@types/mark.js": "8.11.12", | ||||||
|  |     "@types/tabulator-tables": "6.2.6", | ||||||
|     "copy-webpack-plugin": "13.0.0", |     "copy-webpack-plugin": "13.0.0", | ||||||
|     "happy-dom": "18.0.1", |     "happy-dom": "18.0.1", | ||||||
|     "script-loader": "0.7.2", |     "script-loader": "0.7.2", | ||||||
|   | |||||||
| @@ -93,11 +93,7 @@ export class TypedComponent<ChildT extends TypedComponent<ChildT>> { | |||||||
|  |  | ||||||
|         if (fun) { |         if (fun) { | ||||||
|             return this.callMethod(fun, data); |             return this.callMethod(fun, data); | ||||||
|         } else { |         } else if (this.parent) { | ||||||
|             if (!this.parent) { |  | ||||||
|                 throw new Error(`Component "${this.componentId}" does not have a parent attached to propagate a command.`); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             return this.parent.triggerCommand(name, data); |             return this.parent.triggerCommand(name, data); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -315,14 +315,38 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded"> | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     hasNoteList() { |     hasNoteList() { | ||||||
|         return ( |         const note = this.note; | ||||||
|             this.note && |  | ||||||
|             ["default", "contextual-help"].includes(this.viewScope?.viewMode ?? "") && |         if (!note) { | ||||||
|             (this.note.hasChildren() || this.note.getLabelValue("viewType") === "calendar") && |             return false; | ||||||
|             ["book", "text", "code"].includes(this.note.type) && |         } | ||||||
|             this.note.mime !== "text/x-sqlite;schema=trilium" && |  | ||||||
|             !this.note.isLabelTruthy("hideChildrenOverview") |         if (!["default", "contextual-help"].includes(this.viewScope?.viewMode ?? "")) { | ||||||
|         ); |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Some book types must always display a note list, even if no children. | ||||||
|  |         if (["calendar", "table"].includes(note.getLabelValue("viewType") ?? "")) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!note.hasChildren()) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!["book", "text", "code"].includes(note.type)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (note.mime === "text/x-sqlite;schema=trilium") { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (note.isLabelTruthy("hideChildrenOverview")) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async getTextEditor(callback?: GetTextEditorCallback) { |     async getTextEditor(callback?: GetTextEditorCallback) { | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import keyboardActionService from "../services/keyboard_actions.js"; | |||||||
| import note_tooltip from "../services/note_tooltip.js"; | import note_tooltip from "../services/note_tooltip.js"; | ||||||
| import utils from "../services/utils.js"; | import utils from "../services/utils.js"; | ||||||
|  |  | ||||||
| interface ContextMenuOptions<T> { | export interface ContextMenuOptions<T> { | ||||||
|     x: number; |     x: number; | ||||||
|     y: number; |     y: number; | ||||||
|     orientation?: "left"; |     orientation?: "left"; | ||||||
| @@ -28,6 +28,7 @@ export interface MenuCommandItem<T> { | |||||||
|     items?: MenuItem<T>[] | null; |     items?: MenuItem<T>[] | null; | ||||||
|     shortcut?: string; |     shortcut?: string; | ||||||
|     spellingSuggestion?: string; |     spellingSuggestion?: string; | ||||||
|  |     checked?: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
| export type MenuItem<T> = MenuCommandItem<T> | MenuSeparatorItem; | export type MenuItem<T> = MenuCommandItem<T> | MenuSeparatorItem; | ||||||
| @@ -146,10 +147,13 @@ class ContextMenu { | |||||||
|             } else { |             } else { | ||||||
|                 const $icon = $("<span>"); |                 const $icon = $("<span>"); | ||||||
|  |  | ||||||
|                 if ("uiIcon" in item && item.uiIcon) { |                 if ("uiIcon" in item || "checked" in item) { | ||||||
|                     $icon.addClass(item.uiIcon); |                     const icon = (item.checked ? "bx bx-check" : item.uiIcon); | ||||||
|                 } else { |                     if (icon) { | ||||||
|                     $icon.append(" "); |                         $icon.addClass(icon); | ||||||
|  |                     } else { | ||||||
|  |                         $icon.append(" "); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 const $link = $("<span>") |                 const $link = $("<span>") | ||||||
|   | |||||||
| @@ -3,15 +3,16 @@ import froca from "./froca.js"; | |||||||
| import type FNote from "../entities/fnote.js"; | import type FNote from "../entities/fnote.js"; | ||||||
| import type { AttributeRow } from "./load_results.js"; | import type { AttributeRow } from "./load_results.js"; | ||||||
|  |  | ||||||
| async function addLabel(noteId: string, name: string, value: string = "") { | async function addLabel(noteId: string, name: string, value: string = "", isInheritable = false) { | ||||||
|     await server.put(`notes/${noteId}/attribute`, { |     await server.put(`notes/${noteId}/attribute`, { | ||||||
|         type: "label", |         type: "label", | ||||||
|         name: name, |         name: name, | ||||||
|         value: value |         value: value, | ||||||
|  |         isInheritable | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function setLabel(noteId: string, name: string, value: string = "") { | export async function setLabel(noteId: string, name: string, value: string = "") { | ||||||
|     await server.put(`notes/${noteId}/set-attribute`, { |     await server.put(`notes/${noteId}/set-attribute`, { | ||||||
|         type: "label", |         type: "label", | ||||||
|         name: name, |         name: name, | ||||||
| @@ -49,7 +50,7 @@ function removeOwnedLabelByName(note: FNote, labelName: string) { | |||||||
|  * @param name the name of the attribute to set. |  * @param name the name of the attribute to set. | ||||||
|  * @param value the value of the attribute to set. |  * @param value the value of the attribute to set. | ||||||
|  */ |  */ | ||||||
| async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) { | export async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) { | ||||||
|     if (value) { |     if (value) { | ||||||
|         // Create or update the attribute. |         // Create or update the attribute. | ||||||
|         await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value }); |         await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value }); | ||||||
|   | |||||||
| @@ -118,8 +118,17 @@ async function renderText(note: FNote | FAttachment, $renderedContent: JQuery<HT | |||||||
| async function renderCode(note: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>) { | async function renderCode(note: FNote | FAttachment, $renderedContent: JQuery<HTMLElement>) { | ||||||
|     const blob = await note.getBlob(); |     const blob = await note.getBlob(); | ||||||
|  |  | ||||||
|  |     let content = blob?.content || ""; | ||||||
|  |     if (note.mime === "application/json") { | ||||||
|  |         try { | ||||||
|  |             content = JSON.stringify(JSON.parse(content), null, 4); | ||||||
|  |         } catch (e) { | ||||||
|  |             // Ignore JSON parsing errors. | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const $codeBlock = $("<code>"); |     const $codeBlock = $("<code>"); | ||||||
|     $codeBlock.text(blob?.content || ""); |     $codeBlock.text(content); | ||||||
|     $renderedContent.append($("<pre>").append($codeBlock)); |     $renderedContent.append($("<pre>").append($codeBlock)); | ||||||
|     await applySingleBlockSyntaxHighlight($codeBlock, normalizeMimeTypeForCKEditor(note.mime)); |     await applySingleBlockSyntaxHighlight($codeBlock, normalizeMimeTypeForCKEditor(note.mime)); | ||||||
| } | } | ||||||
| @@ -301,7 +310,7 @@ function getRenderingType(entity: FNote | FAttachment) { | |||||||
|  |  | ||||||
|     if (type === "file" && mime === "application/pdf") { |     if (type === "file" && mime === "application/pdf") { | ||||||
|         type = "pdf"; |         type = "pdf"; | ||||||
|     } else if (type === "file" && mime && CODE_MIME_TYPES.has(mime)) { |     } else if ((type === "file" || type === "viewConfig") && mime && CODE_MIME_TYPES.has(mime)) { | ||||||
|         type = "code"; |         type = "code"; | ||||||
|     } else if (type === "file" && mime && mime.startsWith("audio/")) { |     } else if (type === "file" && mime && mime.startsWith("audio/")) { | ||||||
|         type = "audio"; |         type = "audio"; | ||||||
|   | |||||||
| @@ -384,7 +384,7 @@ function linkContextMenu(e: PointerEvent) { | |||||||
|     linkContextMenuService.openContextMenu(notePath, e, viewScope, null); |     linkContextMenuService.openContextMenu(notePath, e, viewScope, null); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function loadReferenceLinkTitle($el: JQuery<HTMLElement>, href: string | null | undefined = null) { | export async function loadReferenceLinkTitle($el: JQuery<HTMLElement>, href: string | null | undefined = null) { | ||||||
|     const $link = $el[0].tagName === "A" ? $el : $el.find("a"); |     const $link = $el[0].tagName === "A" ? $el : $el.find("a"); | ||||||
|  |  | ||||||
|     href = href || $link.attr("href"); |     href = href || $link.attr("href"); | ||||||
|   | |||||||
| @@ -1,38 +1,40 @@ | |||||||
| import type FNote from "../entities/fnote.js"; | import type FNote from "../entities/fnote.js"; | ||||||
| import CalendarView from "../widgets/view_widgets/calendar_view.js"; | import CalendarView from "../widgets/view_widgets/calendar_view.js"; | ||||||
| import ListOrGridView from "../widgets/view_widgets/list_or_grid_view.js"; | import ListOrGridView from "../widgets/view_widgets/list_or_grid_view.js"; | ||||||
|  | import TableView from "../widgets/view_widgets/table_view/index.js"; | ||||||
| import type { ViewModeArgs } from "../widgets/view_widgets/view_mode.js"; | import type { ViewModeArgs } from "../widgets/view_widgets/view_mode.js"; | ||||||
| import type ViewMode from "../widgets/view_widgets/view_mode.js"; | import type ViewMode from "../widgets/view_widgets/view_mode.js"; | ||||||
|  |  | ||||||
| export type ViewTypeOptions = "list" | "grid" | "calendar"; | export type ViewTypeOptions = "list" | "grid" | "calendar" | "table"; | ||||||
|  |  | ||||||
| export default class NoteListRenderer { | export default class NoteListRenderer { | ||||||
|  |  | ||||||
|     private viewType: ViewTypeOptions; |     private viewType: ViewTypeOptions; | ||||||
|     public viewMode: ViewMode | null; |     public viewMode: ViewMode<any> | null; | ||||||
|  |  | ||||||
|     constructor($parent: JQuery<HTMLElement>, parentNote: FNote, noteIds: string[], showNotePath: boolean = false) { |     constructor(args: ViewModeArgs) { | ||||||
|         this.viewType = this.#getViewType(parentNote); |         this.viewType = this.#getViewType(args.parentNote); | ||||||
|         const args: ViewModeArgs = { |  | ||||||
|             $parent, |  | ||||||
|             parentNote, |  | ||||||
|             noteIds, |  | ||||||
|             showNotePath |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         if (this.viewType === "list" || this.viewType === "grid") { |         switch (this.viewType) { | ||||||
|             this.viewMode = new ListOrGridView(this.viewType, args); |             case "list": | ||||||
|         } else if (this.viewType === "calendar") { |             case "grid": | ||||||
|             this.viewMode = new CalendarView(args); |                 this.viewMode = new ListOrGridView(this.viewType, args); | ||||||
|         } else { |                 break; | ||||||
|             this.viewMode = null; |             case "calendar": | ||||||
|  |                 this.viewMode = new CalendarView(args); | ||||||
|  |                 break; | ||||||
|  |             case "table": | ||||||
|  |                 this.viewMode = new TableView(args); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 this.viewMode = null; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #getViewType(parentNote: FNote): ViewTypeOptions { |     #getViewType(parentNote: FNote): ViewTypeOptions { | ||||||
|         const viewType = parentNote.getLabelValue("viewType"); |         const viewType = parentNote.getLabelValue("viewType"); | ||||||
|  |  | ||||||
|         if (!["list", "grid", "calendar"].includes(viewType || "")) { |         if (!["list", "grid", "calendar", "table"].includes(viewType || "")) { | ||||||
|             // when not explicitly set, decide based on the note type |             // when not explicitly set, decide based on the note type | ||||||
|             return parentNote.type === "search" ? "list" : "grid"; |             return parentNote.type === "search" ? "list" : "grid"; | ||||||
|         } else { |         } else { | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ let dismissTimer: ReturnType<typeof setTimeout>; | |||||||
|  |  | ||||||
| function setupGlobalTooltip() { | function setupGlobalTooltip() { | ||||||
|     $(document).on("mouseenter", "a", mouseEnterHandler); |     $(document).on("mouseenter", "a", mouseEnterHandler); | ||||||
|  |     $(document).on("mouseenter", "[data-href]", mouseEnterHandler); | ||||||
|  |  | ||||||
|     // close any note tooltip after click, this fixes the problem that sometimes tooltips remained on the screen |     // close any note tooltip after click, this fixes the problem that sometimes tooltips remained on the screen | ||||||
|     $(document).on("click", (e) => { |     $(document).on("click", (e) => { | ||||||
|   | |||||||
| @@ -8,18 +8,35 @@ const SEPARATOR = { title: "----" }; | |||||||
|  |  | ||||||
| async function getNoteTypeItems(command?: TreeCommandNames) { | async function getNoteTypeItems(command?: TreeCommandNames) { | ||||||
|     const items: MenuItem<TreeCommandNames>[] = [ |     const items: MenuItem<TreeCommandNames>[] = [ | ||||||
|  |         // The suggested note type ordering method: insert the item into the corresponding group, | ||||||
|  |         // then ensure the items within the group are ordered alphabetically. | ||||||
|  |         // Please keep the order synced with the listing found also in aps/client/src/widgets/note_types.ts. | ||||||
|  |  | ||||||
|  |         // The default note type (always the first item) | ||||||
|         { title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" }, |         { title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" }, | ||||||
|         { title: t("note_types.code"), command, type: "code", uiIcon: "bx bx-code" }, |          | ||||||
|         { title: t("note_types.saved-search"), command, type: "search", uiIcon: "bx bx-file-find" }, |         // Text notes group | ||||||
|         { title: t("note_types.relation-map"), command, type: "relationMap", uiIcon: "bx bxs-network-chart" }, |  | ||||||
|         { title: t("note_types.note-map"), command, type: "noteMap", uiIcon: "bx bxs-network-chart" }, |  | ||||||
|         { title: t("note_types.render-note"), command, type: "render", uiIcon: "bx bx-extension" }, |  | ||||||
|         { title: t("note_types.book"), command, type: "book", uiIcon: "bx bx-book" }, |         { title: t("note_types.book"), command, type: "book", uiIcon: "bx bx-book" }, | ||||||
|         { title: t("note_types.mermaid-diagram"), command, type: "mermaid", uiIcon: "bx bx-selection" }, |  | ||||||
|  |         // Graphic notes | ||||||
|         { title: t("note_types.canvas"), command, type: "canvas", uiIcon: "bx bx-pen" }, |         { title: t("note_types.canvas"), command, type: "canvas", uiIcon: "bx bx-pen" }, | ||||||
|         { title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" }, |         { title: t("note_types.mermaid-diagram"), command, type: "mermaid", uiIcon: "bx bx-selection" }, | ||||||
|         { title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" }, |          | ||||||
|  |         // Map notes | ||||||
|         { title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" }, |         { title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" }, | ||||||
|  |         { title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" }, | ||||||
|  |         { title: t("note_types.note-map"), command, type: "noteMap", uiIcon: "bx bxs-network-chart" }, | ||||||
|  |         { title: t("note_types.relation-map"), command, type: "relationMap", uiIcon: "bx bxs-network-chart" }, | ||||||
|  |  | ||||||
|  |         // Misc note types | ||||||
|  |         { title: t("note_types.render-note"), command, type: "render", uiIcon: "bx bx-extension" }, | ||||||
|  |         { title: t("note_types.saved-search"), command, type: "search", uiIcon: "bx bx-file-find" }, | ||||||
|  |         { title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" }, | ||||||
|  |  | ||||||
|  |         // Code notes | ||||||
|  |         { title: t("note_types.code"), command, type: "code", uiIcon: "bx bx-code" }, | ||||||
|  |  | ||||||
|  |         // Templates | ||||||
|         ...await getBuiltInTemplates(command), |         ...await getBuiltInTemplates(command), | ||||||
|         ...await getUserTemplates(command) |         ...await getUserTemplates(command) | ||||||
|     ]; |     ]; | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| type LabelType = "text" | "number" | "boolean" | "date" | "datetime" | "time" | "url"; | export type LabelType = "text" | "number" | "boolean" | "date" | "datetime" | "time" | "url"; | ||||||
| type Multiplicity = "single" | "multi"; | type Multiplicity = "single" | "multi"; | ||||||
|  |  | ||||||
| export interface DefinitionObject { | export interface DefinitionObject { | ||||||
|   | |||||||
| @@ -382,6 +382,10 @@ div.tn-tool-dialog { | |||||||
|  |  | ||||||
| /* DELETE NOTE PREVIEW DIALOG */ | /* DELETE NOTE PREVIEW DIALOG */ | ||||||
|  |  | ||||||
|  | .delete-notes-dialog .modal-dialog { | ||||||
|  |     --bs-modal-width: fit-content; | ||||||
|  | } | ||||||
|  |  | ||||||
| .delete-notes-list .note-path { | .delete-notes-list .note-path { | ||||||
|     padding-left: 8px; |     padding-left: 8px; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -46,6 +46,12 @@ div.promoted-attributes-container { | |||||||
| .image-properties > div:first-child > span > strong { | .image-properties > div:first-child > span > strong { | ||||||
|     opacity: 0.65; |     opacity: 0.65; | ||||||
|     font-weight: 500; |     font-weight: 500; | ||||||
|  |     vertical-align: top; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .note-info-widget-table td, | ||||||
|  | .file-properties-widget .file-table td { | ||||||
|  |     vertical-align: top; | ||||||
| } | } | ||||||
|  |  | ||||||
| .file-properties-widget { | .file-properties-widget { | ||||||
|   | |||||||
| @@ -71,7 +71,7 @@ body.background-effects.platform-win32.layout-vertical #vertical-main-container | |||||||
| /* #endregion */ | /* #endregion */ | ||||||
|  |  | ||||||
| /* Matches when the left pane is collapsed */ | /* Matches when the left pane is collapsed */ | ||||||
| :has(.layout-vertical #left-pane.hidden-int) { | #horizontal-main-container:has(#left-pane.hidden-int) { | ||||||
|     --center-pane-border-radius: 0; |     --center-pane-border-radius: 0; | ||||||
|     --tab-first-item-horiz-offset: 5px; |     --tab-first-item-horiz-offset: 5px; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -760,7 +760,8 @@ | |||||||
|     "expand": "Expand", |     "expand": "Expand", | ||||||
|     "book_properties": "Book Properties", |     "book_properties": "Book Properties", | ||||||
|     "invalid_view_type": "Invalid view type '{{type}}'", |     "invalid_view_type": "Invalid view type '{{type}}'", | ||||||
|     "calendar": "Calendar" |     "calendar": "Calendar", | ||||||
|  |     "table": "Table" | ||||||
|   }, |   }, | ||||||
|   "edited_notes": { |   "edited_notes": { | ||||||
|     "no_edited_notes_found": "No edited notes on this day yet...", |     "no_edited_notes_found": "No edited notes on this day yet...", | ||||||
| @@ -1933,5 +1934,9 @@ | |||||||
|     "title": "Features", |     "title": "Features", | ||||||
|     "emoji_completion_enabled": "Enable Emoji auto-completion", |     "emoji_completion_enabled": "Enable Emoji auto-completion", | ||||||
|     "note_completion_enabled": "Enable note auto-completion" |     "note_completion_enabled": "Enable note auto-completion" | ||||||
|  |   }, | ||||||
|  |   "table_view": { | ||||||
|  |     "new-row": "New row", | ||||||
|  |     "new-column": "New column" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -34,7 +34,8 @@ export const byNoteType: Record<Exclude<NoteType, "book">, string | null> = { | |||||||
| export const byBookType: Record<ViewTypeOptions, string | null> = { | export const byBookType: Record<ViewTypeOptions, string | null> = { | ||||||
|     list: null, |     list: null, | ||||||
|     grid: null, |     grid: null, | ||||||
|     calendar: "xWbu3jpNWapp" |     calendar: "xWbu3jpNWapp", | ||||||
|  |     table: "2FvYrpmOXm29" | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export default class ContextualHelpButton extends NoteContextAwareWidget { | export default class ContextualHelpButton extends NoteContextAwareWidget { | ||||||
|   | |||||||
| @@ -1,8 +1,10 @@ | |||||||
| import NoteContextAwareWidget from "./note_context_aware_widget.js"; | import NoteContextAwareWidget from "./note_context_aware_widget.js"; | ||||||
| import NoteListRenderer from "../services/note_list_renderer.js"; | import NoteListRenderer from "../services/note_list_renderer.js"; | ||||||
| import type FNote from "../entities/fnote.js"; | import type FNote from "../entities/fnote.js"; | ||||||
| import type { CommandListener, CommandListenerData, EventData } from "../components/app_context.js"; | import type { CommandListener, CommandListenerData, CommandMappings, CommandNames, EventData } from "../components/app_context.js"; | ||||||
| import type ViewMode from "./view_widgets/view_mode.js"; | import type ViewMode from "./view_widgets/view_mode.js"; | ||||||
|  | import AttributeDetailWidget from "./attribute_widgets/attribute_detail.js"; | ||||||
|  | import { Attribute } from "../services/attribute_parser.js"; | ||||||
|  |  | ||||||
| const TPL = /*html*/` | const TPL = /*html*/` | ||||||
| <div class="note-list-widget"> | <div class="note-list-widget"> | ||||||
| @@ -36,7 +38,15 @@ export default class NoteListWidget extends NoteContextAwareWidget { | |||||||
|     private isIntersecting?: boolean; |     private isIntersecting?: boolean; | ||||||
|     private noteIdRefreshed?: string; |     private noteIdRefreshed?: string; | ||||||
|     private shownNoteId?: string | null; |     private shownNoteId?: string | null; | ||||||
|     private viewMode?: ViewMode | null; |     private viewMode?: ViewMode<any> | null; | ||||||
|  |     private attributeDetailWidget: AttributeDetailWidget; | ||||||
|  |  | ||||||
|  |     constructor() { | ||||||
|  |         super(); | ||||||
|  |         this.attributeDetailWidget = new AttributeDetailWidget() | ||||||
|  |                 .contentSized() | ||||||
|  |                 .setParent(this); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     isEnabled() { |     isEnabled() { | ||||||
|         return super.isEnabled() && this.noteContext?.hasNoteList(); |         return super.isEnabled() && this.noteContext?.hasNoteList(); | ||||||
| @@ -46,6 +56,7 @@ export default class NoteListWidget extends NoteContextAwareWidget { | |||||||
|         this.$widget = $(TPL); |         this.$widget = $(TPL); | ||||||
|         this.contentSized(); |         this.contentSized(); | ||||||
|         this.$content = this.$widget.find(".note-list-widget-content"); |         this.$content = this.$widget.find(".note-list-widget-content"); | ||||||
|  |         this.$widget.append(this.attributeDetailWidget.render()); | ||||||
|  |  | ||||||
|         const observer = new IntersectionObserver( |         const observer = new IntersectionObserver( | ||||||
|             (entries) => { |             (entries) => { | ||||||
| @@ -64,6 +75,23 @@ export default class NoteListWidget extends NoteContextAwareWidget { | |||||||
|         setTimeout(() => observer.observe(this.$widget[0]), 10); |         setTimeout(() => observer.observe(this.$widget[0]), 10); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     addNoteListItemEvent() { | ||||||
|  |         const attr: Attribute = { | ||||||
|  |             type: "label", | ||||||
|  |             name: "label:myLabel", | ||||||
|  |             value: "promoted,single,text" | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         this.attributeDetailWidget!.showAttributeDetail({ | ||||||
|  |             attribute: attr, | ||||||
|  |             allAttributes: [ attr ], | ||||||
|  |             isOwned: true, | ||||||
|  |             x: 100, | ||||||
|  |             y: 200, | ||||||
|  |             focus: "name" | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     checkRenderStatus() { |     checkRenderStatus() { | ||||||
|         // console.log("this.isIntersecting", this.isIntersecting); |         // console.log("this.isIntersecting", this.isIntersecting); | ||||||
|         // console.log(`${this.noteIdRefreshed} === ${this.noteId}`, this.noteIdRefreshed === this.noteId); |         // console.log(`${this.noteIdRefreshed} === ${this.noteId}`, this.noteIdRefreshed === this.noteId); | ||||||
| @@ -76,7 +104,12 @@ export default class NoteListWidget extends NoteContextAwareWidget { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async renderNoteList(note: FNote) { |     async renderNoteList(note: FNote) { | ||||||
|         const noteListRenderer = new NoteListRenderer(this.$content, note, note.getChildNoteIds()); |         const noteListRenderer = new NoteListRenderer({ | ||||||
|  |             $parent: this.$content, | ||||||
|  |             parentNote: note, | ||||||
|  |             parentNotePath: this.notePath, | ||||||
|  |             noteIds: note.getChildNoteIds() | ||||||
|  |         }); | ||||||
|         this.$widget.toggleClass("full-height", noteListRenderer.isFullHeight); |         this.$widget.toggleClass("full-height", noteListRenderer.isFullHeight); | ||||||
|         await noteListRenderer.renderList(); |         await noteListRenderer.renderList(); | ||||||
|         this.viewMode = noteListRenderer.viewMode; |         this.viewMode = noteListRenderer.viewMode; | ||||||
| @@ -134,4 +167,13 @@ export default class NoteListWidget extends NoteContextAwareWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     triggerCommand<K extends CommandNames>(name: K, data?: CommandMappings[K]): Promise<unknown> | undefined | null { | ||||||
|  |         // Pass the commands to the view mode, which is not actually attached to the hierarchy. | ||||||
|  |         if (this.viewMode?.triggerCommand(name, data)) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return super.triggerCommand(name, data); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -324,7 +324,13 @@ export default class NoteMapWidget extends NoteContextAwareWidget { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         const mapRootNoteId = this.getMapRootNoteId(); |         const mapRootNoteId = this.getMapRootNoteId(); | ||||||
|         const data = await this.loadNotesAndRelations(mapRootNoteId); |  | ||||||
|  |         const labelValues = (name: string) => this.note?.getLabels(name).map(l => l.value) ?? []; | ||||||
|  |  | ||||||
|  |         const excludeRelations = labelValues("mapExcludeRelation"); | ||||||
|  |         const includeRelations = labelValues("mapIncludeRelation"); | ||||||
|  |  | ||||||
|  |         const data = await this.loadNotesAndRelations(mapRootNoteId, excludeRelations, includeRelations); | ||||||
|  |  | ||||||
|         const nodeLinkRatio = data.nodes.length / data.links.length; |         const nodeLinkRatio = data.nodes.length / data.links.length; | ||||||
|         const magnifiedRatio = Math.pow(nodeLinkRatio, 1.5); |         const magnifiedRatio = Math.pow(nodeLinkRatio, 1.5); | ||||||
| @@ -473,8 +479,10 @@ export default class NoteMapWidget extends NoteContextAwareWidget { | |||||||
|         ctx.restore(); |         ctx.restore(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async loadNotesAndRelations(mapRootNoteId: string): Promise<NotesAndRelationsData> { |     async loadNotesAndRelations(mapRootNoteId: string, excludeRelations: string[], includeRelations: string[]): Promise<NotesAndRelationsData> { | ||||||
|         const resp = await server.post<PostNotesMapResponse>(`note-map/${mapRootNoteId}/${this.mapType}`); |         const resp = await server.post<PostNotesMapResponse>(`note-map/${mapRootNoteId}/${this.mapType}`, { | ||||||
|  |             excludeRelations, includeRelations | ||||||
|  |         }); | ||||||
|  |  | ||||||
|         this.calculateNodeSizes(resp); |         this.calculateNodeSizes(resp); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ interface NoteTypeMapping { | |||||||
| const NOTE_TYPES: NoteTypeMapping[] = [ | const NOTE_TYPES: NoteTypeMapping[] = [ | ||||||
|     // The suggested note type ordering method: insert the item into the corresponding group, |     // The suggested note type ordering method: insert the item into the corresponding group, | ||||||
|     // then ensure the items within the group are ordered alphabetically. |     // then ensure the items within the group are ordered alphabetically. | ||||||
|  |     // Please keep the order synced with the listing found also in apps/client/src/services/note_types.ts. | ||||||
|  |  | ||||||
|     // The default note type (always the first item) |     // The default note type (always the first item) | ||||||
|     { type: "text", mime: "text/html", title: t("note_types.text"), selectable: true }, |     { type: "text", mime: "text/html", title: t("note_types.text"), selectable: true }, | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ const TPL = /*html*/` | |||||||
|             <option value="grid">${t("book_properties.grid")}</option> |             <option value="grid">${t("book_properties.grid")}</option> | ||||||
|             <option value="list">${t("book_properties.list")}</option> |             <option value="list">${t("book_properties.list")}</option> | ||||||
|             <option value="calendar">${t("book_properties.calendar")}</option> |             <option value="calendar">${t("book_properties.calendar")}</option> | ||||||
|  |             <option value="table">${t("book_properties.table")}</option> | ||||||
|         </select> |         </select> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
| @@ -67,7 +68,6 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget { | |||||||
|     getTitle() { |     getTitle() { | ||||||
|         return { |         return { | ||||||
|             show: this.isEnabled(), |             show: this.isEnabled(), | ||||||
|             activate: true, |  | ||||||
|             title: t("book_properties.book_properties"), |             title: t("book_properties.book_properties"), | ||||||
|             icon: "bx bx-book" |             icon: "bx bx-book" | ||||||
|         }; |         }; | ||||||
| @@ -126,7 +126,7 @@ export default class BookPropertiesWidget extends NoteContextAwareWidget { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!["list", "grid", "calendar"].includes(type)) { |         if (!["list", "grid", "calendar", "table"].includes(type)) { | ||||||
|             throw new Error(t("book_properties.invalid_view_type", { type })); |             throw new Error(t("book_properties.invalid_view_type", { type })); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget { | |||||||
|         // the order of attributes is important as well |         // the order of attributes is important as well | ||||||
|         ownedAttributes.sort((a, b) => a.position - b.position); |         ownedAttributes.sort((a, b) => a.position - b.position); | ||||||
|  |  | ||||||
|         if (promotedDefAttrs.length === 0) { |         if (promotedDefAttrs.length === 0 || note.getLabelValue("viewType") === "table") { | ||||||
|             this.toggleInt(false); |             this.toggleInt(false); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -65,7 +65,13 @@ export default class SearchResultWidget extends NoteContextAwareWidget { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const noteListRenderer = new NoteListRenderer(this.$content, note, note.getChildNoteIds(), true); |         // this.$content, note, note.getChildNoteIds(), true | ||||||
|  |         const noteListRenderer = new NoteListRenderer({ | ||||||
|  |             $parent: this.$content, | ||||||
|  |             parentNote: note, | ||||||
|  |             noteIds: note.getChildNoteIds(), | ||||||
|  |             showNotePath: true | ||||||
|  |         }); | ||||||
|         await noteListRenderer.renderList(); |         await noteListRenderer.renderList(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -36,7 +36,21 @@ export default class BookTypeWidget extends TypeWidget { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async doRefresh(note: FNote) { |     async doRefresh(note: FNote) { | ||||||
|         this.$helpNoChildren.toggle(!this.note?.hasChildren() && this.note?.getAttributeValue("label", "viewType") !== "calendar"); |         this.$helpNoChildren.toggle(this.shouldDisplayNoChildrenWarning()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     shouldDisplayNoChildrenWarning() { | ||||||
|  |         if (this.note?.hasChildren()) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         switch (this.note?.getAttributeValue("label", "viewType")) { | ||||||
|  |             case "calendar": | ||||||
|  |             case "table": | ||||||
|  |                 return false; | ||||||
|  |             default: | ||||||
|  |                 return true; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { |     entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { | ||||||
|   | |||||||
| @@ -109,24 +109,22 @@ const CALENDAR_VIEWS = [ | |||||||
|     "listMonth" |     "listMonth" | ||||||
| ] | ] | ||||||
|  |  | ||||||
| export default class CalendarView extends ViewMode { | export default class CalendarView extends ViewMode<{}> { | ||||||
|  |  | ||||||
|     private $root: JQuery<HTMLElement>; |     private $root: JQuery<HTMLElement>; | ||||||
|     private $calendarContainer: JQuery<HTMLElement>; |     private $calendarContainer: JQuery<HTMLElement>; | ||||||
|     private noteIds: string[]; |     private noteIds: string[]; | ||||||
|     private parentNote: FNote; |  | ||||||
|     private calendar?: Calendar; |     private calendar?: Calendar; | ||||||
|     private isCalendarRoot: boolean; |     private isCalendarRoot: boolean; | ||||||
|     private lastView?: string; |     private lastView?: string; | ||||||
|     private debouncedSaveView?: DebouncedFunction<() => void>; |     private debouncedSaveView?: DebouncedFunction<() => void>; | ||||||
|  |  | ||||||
|     constructor(args: ViewModeArgs) { |     constructor(args: ViewModeArgs) { | ||||||
|         super(args); |         super(args, "calendar"); | ||||||
|  |  | ||||||
|         this.$root = $(TPL); |         this.$root = $(TPL); | ||||||
|         this.$calendarContainer = this.$root.find(".calendar-container"); |         this.$calendarContainer = this.$root.find(".calendar-container"); | ||||||
|         this.noteIds = args.noteIds; |         this.noteIds = args.noteIds; | ||||||
|         this.parentNote = args.parentNote; |  | ||||||
|         this.isCalendarRoot = false; |         this.isCalendarRoot = false; | ||||||
|         args.$parent.append(this.$root); |         args.$parent.append(this.$root); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import treeService from "../../services/tree.js"; | |||||||
| import utils from "../../services/utils.js"; | import utils from "../../services/utils.js"; | ||||||
| import type FNote from "../../entities/fnote.js"; | import type FNote from "../../entities/fnote.js"; | ||||||
| import ViewMode, { type ViewModeArgs } from "./view_mode.js"; | import ViewMode, { type ViewModeArgs } from "./view_mode.js"; | ||||||
|  | import type { ViewTypeOptions } from "../../services/note_list_renderer.js"; | ||||||
|  |  | ||||||
| const TPL = /*html*/` | const TPL = /*html*/` | ||||||
| <div class="note-list"> | <div class="note-list"> | ||||||
| @@ -157,26 +158,22 @@ const TPL = /*html*/` | |||||||
|     </div> |     </div> | ||||||
| </div>`; | </div>`; | ||||||
|  |  | ||||||
| class ListOrGridView extends ViewMode { | class ListOrGridView extends ViewMode<{}> { | ||||||
|     private $noteList: JQuery<HTMLElement>; |     private $noteList: JQuery<HTMLElement>; | ||||||
|  |  | ||||||
|     private parentNote: FNote; |  | ||||||
|     private noteIds: string[]; |     private noteIds: string[]; | ||||||
|     private page?: number; |     private page?: number; | ||||||
|     private pageSize?: number; |     private pageSize?: number; | ||||||
|     private viewType?: string | null; |  | ||||||
|     private showNotePath?: boolean; |     private showNotePath?: boolean; | ||||||
|     private highlightRegex?: RegExp | null; |     private highlightRegex?: RegExp | null; | ||||||
|  |  | ||||||
|     /* |     /* | ||||||
|      * We're using noteIds so that it's not necessary to load all notes at once when paging |      * We're using noteIds so that it's not necessary to load all notes at once when paging | ||||||
|      */ |      */ | ||||||
|     constructor(viewType: string, args: ViewModeArgs) { |     constructor(viewType: ViewTypeOptions, args: ViewModeArgs) { | ||||||
|         super(args); |         super(args, viewType); | ||||||
|         this.$noteList = $(TPL); |         this.$noteList = $(TPL); | ||||||
|         this.viewType = viewType; |  | ||||||
|  |  | ||||||
|         this.parentNote = args.parentNote; |  | ||||||
|         const includedNoteIds = this.getIncludedNoteIds(); |         const includedNoteIds = this.getIncludedNoteIds(); | ||||||
|  |  | ||||||
|         this.noteIds = args.noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden"); |         this.noteIds = args.noteIds.filter((noteId) => !includedNoteIds.has(noteId) && noteId !== "_hidden"); | ||||||
|   | |||||||
							
								
								
									
										110
									
								
								apps/client/src/widgets/view_widgets/table_view/columns.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								apps/client/src/widgets/view_widgets/table_view/columns.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | import { RelationEditor } from "./relation_editor.js"; | ||||||
|  | import { NoteFormatter, NoteTitleFormatter } from "./formatters.js"; | ||||||
|  | import { applyHeaderMenu } from "./header-menu.js"; | ||||||
|  | import type { ColumnDefinition } from "tabulator-tables"; | ||||||
|  | import { LabelType } from "../../../services/promoted_attribute_definition_parser.js"; | ||||||
|  |  | ||||||
|  | type ColumnType = LabelType | "relation"; | ||||||
|  |  | ||||||
|  | export interface PromotedAttributeInformation { | ||||||
|  |     name: string; | ||||||
|  |     title?: string; | ||||||
|  |     type?: ColumnType; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const labelTypeMappings: Record<ColumnType, Partial<ColumnDefinition>> = { | ||||||
|  |     text: { | ||||||
|  |         editor: "input" | ||||||
|  |     }, | ||||||
|  |     boolean: { | ||||||
|  |         formatter: "tickCross", | ||||||
|  |         editor: "tickCross" | ||||||
|  |     }, | ||||||
|  |     date: { | ||||||
|  |         editor: "date", | ||||||
|  |     }, | ||||||
|  |     datetime: { | ||||||
|  |         editor: "datetime" | ||||||
|  |     }, | ||||||
|  |     number: { | ||||||
|  |         editor: "number" | ||||||
|  |     }, | ||||||
|  |     time: { | ||||||
|  |         editor: "input" | ||||||
|  |     }, | ||||||
|  |     url: { | ||||||
|  |         formatter: "link", | ||||||
|  |         editor: "input" | ||||||
|  |     }, | ||||||
|  |     relation: { | ||||||
|  |         editor: RelationEditor, | ||||||
|  |         formatter: NoteFormatter | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export function buildColumnDefinitions(info: PromotedAttributeInformation[], existingColumnData?: ColumnDefinition[]) { | ||||||
|  |     const columnDefs: ColumnDefinition[] = [ | ||||||
|  |         { | ||||||
|  |             title: "#", | ||||||
|  |             formatter: "rownum", | ||||||
|  |             headerSort: false, | ||||||
|  |             hozAlign: "center", | ||||||
|  |             resizable: false, | ||||||
|  |             frozen: true | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             field: "noteId", | ||||||
|  |             title: "Note ID", | ||||||
|  |             visible: false | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             field: "title", | ||||||
|  |             title: "Title", | ||||||
|  |             editor: "input", | ||||||
|  |             formatter: NoteTitleFormatter, | ||||||
|  |             width: 400 | ||||||
|  |         } | ||||||
|  |     ]; | ||||||
|  |  | ||||||
|  |     const seenFields = new Set<string>(); | ||||||
|  |     for (const { name, title, type } of info) { | ||||||
|  |         const prefix = (type === "relation" ? "relations" : "labels"); | ||||||
|  |         const field = `${prefix}.${name}`; | ||||||
|  |  | ||||||
|  |         if (seenFields.has(field)) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         columnDefs.push({ | ||||||
|  |             field, | ||||||
|  |             title: title ?? name, | ||||||
|  |             editor: "input", | ||||||
|  |             ...labelTypeMappings[type ?? "text"], | ||||||
|  |         }); | ||||||
|  |         seenFields.add(field); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     applyHeaderMenu(columnDefs); | ||||||
|  |     if (existingColumnData) { | ||||||
|  |         restoreExistingData(columnDefs, existingColumnData); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return columnDefs; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function restoreExistingData(newDefs: ColumnDefinition[], oldDefs: ColumnDefinition[]) { | ||||||
|  |     const byField = new Map<string, ColumnDefinition>; | ||||||
|  |     for (const def of oldDefs) { | ||||||
|  |         byField.set(def.field ?? "", def); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (const newDef of newDefs) { | ||||||
|  |         const oldDef = byField.get(newDef.field ?? ""); | ||||||
|  |         if (!oldDef) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         newDef.width = oldDef.width; | ||||||
|  |         newDef.visible = oldDef.visible; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								apps/client/src/widgets/view_widgets/table_view/dragging.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								apps/client/src/widgets/view_widgets/table_view/dragging.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | import type { Tabulator } from "tabulator-tables"; | ||||||
|  | import type FNote from "../../../entities/fnote.js"; | ||||||
|  | import branches from "../../../services/branches.js"; | ||||||
|  |  | ||||||
|  | export function canReorderRows(parentNote: FNote) { | ||||||
|  |     return !parentNote.hasLabel("sorted") | ||||||
|  |         && parentNote.type !== "search"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function configureReorderingRows(tabulator: Tabulator) { | ||||||
|  |     tabulator.on("rowMoved", (row) => { | ||||||
|  |         const branchIdsToMove = [ row.getData().branchId ]; | ||||||
|  |  | ||||||
|  |         const prevRow = row.getPrevRow(); | ||||||
|  |         if (prevRow) { | ||||||
|  |             branches.moveAfterBranch(branchIdsToMove, prevRow.getData().branchId); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const nextRow = row.getNextRow(); | ||||||
|  |         if (nextRow) { | ||||||
|  |             branches.moveBeforeBranch(branchIdsToMove, nextRow.getData().branchId); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								apps/client/src/widgets/view_widgets/table_view/footer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								apps/client/src/widgets/view_widgets/table_view/footer.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | import FNote from "../../../entities/fnote.js"; | ||||||
|  | import { t } from "../../../services/i18n.js"; | ||||||
|  |  | ||||||
|  | function shouldDisplayFooter(parentNote: FNote) { | ||||||
|  |     return (parentNote.type !== "search"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default function buildFooter(parentNote: FNote) { | ||||||
|  |     if (!shouldDisplayFooter(parentNote)) { | ||||||
|  |         return undefined; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return /*html*/`\ | ||||||
|  |         <button class="btn btn-sm" style="padding: 0px 10px 0px 10px;" data-trigger-command="addNewRow"> | ||||||
|  |             <span class="bx bx-plus"></span> ${t("table_view.new-row")} | ||||||
|  |         </button> | ||||||
|  |  | ||||||
|  |         <button class="btn btn-sm" style="padding: 0px 10px 0px 10px;" data-trigger-command="addNoteListItem"> | ||||||
|  |             <span class="bx bx-columns"></span> ${t("table_view.new-column")} | ||||||
|  |         </button> | ||||||
|  |     `.trimStart(); | ||||||
|  | } | ||||||
| @@ -0,0 +1,45 @@ | |||||||
|  | import { CellComponent } from "tabulator-tables"; | ||||||
|  | import { loadReferenceLinkTitle } from "../../../services/link.js"; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Custom formatter to represent a note, with the icon and note title being rendered. | ||||||
|  |  * | ||||||
|  |  * The value of the cell must be the note ID. | ||||||
|  |  */ | ||||||
|  | export function NoteFormatter(cell: CellComponent, _formatterParams, onRendered) { | ||||||
|  |     let noteId = cell.getValue(); | ||||||
|  |     if (!noteId) { | ||||||
|  |         return ""; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     onRendered(async () => { | ||||||
|  |         const { $noteRef, href } = buildNoteLink(noteId); | ||||||
|  |         await loadReferenceLinkTitle($noteRef, href); | ||||||
|  |         cell.getElement().appendChild($noteRef[0]); | ||||||
|  |     }); | ||||||
|  |     return ""; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Custom formatter for the note title that is quite similar to {@link NoteFormatter}, but where the title and icons are read from separate fields. | ||||||
|  |  */ | ||||||
|  | export function NoteTitleFormatter(cell: CellComponent) { | ||||||
|  |     const { noteId, iconClass } = cell.getRow().getData(); | ||||||
|  |     if (!noteId) { | ||||||
|  |         return ""; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const { $noteRef } = buildNoteLink(noteId); | ||||||
|  |     $noteRef.text(cell.getValue()); | ||||||
|  |     $noteRef.prepend($("<span>").addClass(iconClass)); | ||||||
|  |  | ||||||
|  |     return $noteRef[0].outerHTML; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function buildNoteLink(noteId: string) { | ||||||
|  |     const $noteRef = $("<span>"); | ||||||
|  |     const href = `#root/${noteId}`; | ||||||
|  |     $noteRef.addClass("reference-link"); | ||||||
|  |     $noteRef.attr("data-href", href); | ||||||
|  |     return { $noteRef, href }; | ||||||
|  | } | ||||||
| @@ -0,0 +1,53 @@ | |||||||
|  | import type { ColumnComponent, ColumnDefinition, MenuObject, Tabulator } from "tabulator-tables"; | ||||||
|  |  | ||||||
|  | export function applyHeaderMenu(columns: ColumnDefinition[]) { | ||||||
|  |     for (let column of columns) { | ||||||
|  |         if (column.headerSort !== false) { | ||||||
|  |             column.headerMenu = headerMenu; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function headerMenu(this: Tabulator) { | ||||||
|  |     const menu: MenuObject<ColumnComponent>[] = []; | ||||||
|  |     const columns = this.getColumns(); | ||||||
|  |  | ||||||
|  |     for (let column of columns) { | ||||||
|  |         //create checkbox element using font awesome icons | ||||||
|  |         let icon = document.createElement("i"); | ||||||
|  |         icon.classList.add("bx"); | ||||||
|  |         icon.classList.add(column.isVisible() ? "bx-check" : "bx-empty"); | ||||||
|  |  | ||||||
|  |         //build label | ||||||
|  |         let label = document.createElement("span"); | ||||||
|  |         let title = document.createElement("span"); | ||||||
|  |  | ||||||
|  |         title.textContent = " " + column.getDefinition().title; | ||||||
|  |  | ||||||
|  |         label.appendChild(icon); | ||||||
|  |         label.appendChild(title); | ||||||
|  |  | ||||||
|  |         //create menu item | ||||||
|  |         menu.push({ | ||||||
|  |             label: label, | ||||||
|  |             action: function (e) { | ||||||
|  |                 //prevent menu closing | ||||||
|  |                 e.stopPropagation(); | ||||||
|  |  | ||||||
|  |                 //toggle current column visibility | ||||||
|  |                 column.toggle(); | ||||||
|  |  | ||||||
|  |                 //change menu item icon | ||||||
|  |                 if (column.isVisible()) { | ||||||
|  |                     icon.classList.remove("bx-empty"); | ||||||
|  |                     icon.classList.add("bx-check"); | ||||||
|  |                 } else { | ||||||
|  |                     icon.classList.remove("bx-check"); | ||||||
|  |                     icon.classList.add("bx-empty"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return menu; | ||||||
|  | }; | ||||||
							
								
								
									
										265
									
								
								apps/client/src/widgets/view_widgets/table_view/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								apps/client/src/widgets/view_widgets/table_view/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,265 @@ | |||||||
|  | import froca from "../../../services/froca.js"; | ||||||
|  | import ViewMode, { type ViewModeArgs } from "../view_mode.js"; | ||||||
|  | import attributes, { setAttribute, setLabel } from "../../../services/attributes.js"; | ||||||
|  | import server from "../../../services/server.js"; | ||||||
|  | import SpacedUpdate from "../../../services/spaced_update.js"; | ||||||
|  | import type { CommandListenerData, EventData } from "../../../components/app_context.js"; | ||||||
|  | import type { Attribute } from "../../../services/attribute_parser.js"; | ||||||
|  | import note_create from "../../../services/note_create.js"; | ||||||
|  | import {Tabulator, SortModule, FormatModule, InteractionModule, EditModule, ResizeColumnsModule, FrozenColumnsModule, PersistenceModule, MoveColumnsModule, MenuModule, MoveRowsModule, ColumnDefinition} from 'tabulator-tables'; | ||||||
|  | import "tabulator-tables/dist/css/tabulator_bootstrap5.min.css"; | ||||||
|  | import { canReorderRows, configureReorderingRows } from "./dragging.js"; | ||||||
|  | import buildFooter from "./footer.js"; | ||||||
|  | import getPromotedAttributeInformation, { buildRowDefinitions } from "./rows.js"; | ||||||
|  | import { buildColumnDefinitions } from "./columns.js"; | ||||||
|  |  | ||||||
|  | const TPL = /*html*/` | ||||||
|  | <div class="table-view"> | ||||||
|  |     <style> | ||||||
|  |     .table-view { | ||||||
|  |         overflow: hidden; | ||||||
|  |         position: relative; | ||||||
|  |         height: 100%; | ||||||
|  |         user-select: none; | ||||||
|  |         padding: 0 5px 0 10px; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     .table-view-container { | ||||||
|  |         height: 100%; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     .search-result-widget-content .table-view { | ||||||
|  |         position: absolute; | ||||||
|  |         top: 0; | ||||||
|  |         left: 0; | ||||||
|  |         right: 0; | ||||||
|  |         bottom: 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     .tabulator-cell .autocomplete { | ||||||
|  |         position: absolute; | ||||||
|  |         top: 50%; | ||||||
|  |         transform: translateY(-50%); | ||||||
|  |         background: transparent; | ||||||
|  |         outline: none !important; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     .tabulator .tabulator-header { | ||||||
|  |         border-top: unset; | ||||||
|  |         border-bottom-width: 1px; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     .tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left, | ||||||
|  |     .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left { | ||||||
|  |         border-right-width: 1px; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     .tabulator .tabulator-footer { | ||||||
|  |         background-color: unset; | ||||||
|  |         padding: 5px 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     .tabulator .tabulator-footer .tabulator-footer-contents { | ||||||
|  |         justify-content: left; | ||||||
|  |         gap: 0.5em; | ||||||
|  |     } | ||||||
|  |     </style> | ||||||
|  |  | ||||||
|  |     <div class="table-view-container"></div> | ||||||
|  | </div> | ||||||
|  | `; | ||||||
|  |  | ||||||
|  | export interface StateInfo { | ||||||
|  |     tableData?: { | ||||||
|  |         columns?: ColumnDefinition[]; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default class TableView extends ViewMode<StateInfo> { | ||||||
|  |  | ||||||
|  |     private $root: JQuery<HTMLElement>; | ||||||
|  |     private $container: JQuery<HTMLElement>; | ||||||
|  |     private args: ViewModeArgs; | ||||||
|  |     private spacedUpdate: SpacedUpdate; | ||||||
|  |     private api?: Tabulator; | ||||||
|  |     private newAttribute?: Attribute; | ||||||
|  |     private persistentData: StateInfo["tableData"]; | ||||||
|  |     /** If set to a note ID, whenever the rows will be updated, the title of the note will be automatically focused for editing. */ | ||||||
|  |     private noteIdToEdit?: string; | ||||||
|  |  | ||||||
|  |     constructor(args: ViewModeArgs) { | ||||||
|  |         super(args, "table"); | ||||||
|  |  | ||||||
|  |         this.$root = $(TPL); | ||||||
|  |         this.$container = this.$root.find(".table-view-container"); | ||||||
|  |         this.args = args; | ||||||
|  |         this.spacedUpdate = new SpacedUpdate(() => this.onSave(), 5_000); | ||||||
|  |         this.persistentData = {}; | ||||||
|  |         args.$parent.append(this.$root); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     get isFullHeight(): boolean { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async renderList() { | ||||||
|  |         this.$container.empty(); | ||||||
|  |         this.renderTable(this.$container[0]); | ||||||
|  |         return this.$root; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async renderTable(el: HTMLElement) { | ||||||
|  |         const modules = [SortModule, FormatModule, InteractionModule, EditModule, ResizeColumnsModule, FrozenColumnsModule, PersistenceModule, MoveColumnsModule, MoveRowsModule, MenuModule]; | ||||||
|  |         for (const module of modules) { | ||||||
|  |             Tabulator.registerModule(module); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this.initialize(el); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async initialize(el: HTMLElement) { | ||||||
|  |         const notes = await froca.getNotes(this.args.noteIds); | ||||||
|  |         const info = getPromotedAttributeInformation(this.parentNote); | ||||||
|  |  | ||||||
|  |         const viewStorage = await this.viewStorage.restore(); | ||||||
|  |         this.persistentData = viewStorage?.tableData || {}; | ||||||
|  |  | ||||||
|  |         const columnDefs = buildColumnDefinitions(info); | ||||||
|  |         const movableRows = canReorderRows(this.parentNote); | ||||||
|  |  | ||||||
|  |         this.api = new Tabulator(el, { | ||||||
|  |             layout: "fitDataFill", | ||||||
|  |             index: "noteId", | ||||||
|  |             columns: columnDefs, | ||||||
|  |             data: await buildRowDefinitions(this.parentNote, notes, info), | ||||||
|  |             persistence: true, | ||||||
|  |             movableColumns: true, | ||||||
|  |             movableRows, | ||||||
|  |             footerElement: buildFooter(this.parentNote), | ||||||
|  |             persistenceWriterFunc: (_id, type: string, data: object) => { | ||||||
|  |                 (this.persistentData as Record<string, {}>)[type] = data; | ||||||
|  |                 this.spacedUpdate.scheduleUpdate(); | ||||||
|  |             }, | ||||||
|  |             persistenceReaderFunc: (_id, type: string) => this.persistentData?.[type], | ||||||
|  |         }); | ||||||
|  |         configureReorderingRows(this.api); | ||||||
|  |         this.setupEditing(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private onSave() { | ||||||
|  |         this.viewStorage.store({ | ||||||
|  |             tableData: this.persistentData, | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private setupEditing() { | ||||||
|  |         this.api!.on("cellEdited", async (cell) => { | ||||||
|  |             const noteId = cell.getRow().getData().noteId; | ||||||
|  |             const field = cell.getField(); | ||||||
|  |             const newValue = cell.getValue(); | ||||||
|  |  | ||||||
|  |             if (field === "title") { | ||||||
|  |                 server.put(`notes/${noteId}/title`, { title: newValue }); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (field.includes(".")) { | ||||||
|  |                 const [ type, name ] = field.split(".", 2); | ||||||
|  |                 if (type === "labels") { | ||||||
|  |                     setLabel(noteId, name, newValue); | ||||||
|  |                 } else if (type === "relations") { | ||||||
|  |                     const note = await froca.getNote(noteId); | ||||||
|  |                     if (note) { | ||||||
|  |                         setAttribute(note, "relation", name, newValue); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async reloadAttributesCommand() { | ||||||
|  |         console.log("Reload attributes"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async updateAttributeListCommand({ attributes }: CommandListenerData<"updateAttributeList">) { | ||||||
|  |         this.newAttribute = attributes[0]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async saveAttributesCommand() { | ||||||
|  |         if (!this.newAttribute) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const { name, value } = this.newAttribute; | ||||||
|  |         attributes.addLabel(this.parentNote.noteId, name, value, true); | ||||||
|  |         console.log("Save attributes", this.newAttribute); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     addNewRowCommand() { | ||||||
|  |         const parentNotePath = this.args.parentNotePath; | ||||||
|  |         if (parentNotePath) { | ||||||
|  |             note_create.createNote(parentNotePath, { | ||||||
|  |                 activate: false | ||||||
|  |             }).then(({ note }) => { | ||||||
|  |                 if (!note) { | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 this.noteIdToEdit = note.noteId; | ||||||
|  |             }) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     onEntitiesReloaded({ loadResults }: EventData<"entitiesReloaded">): boolean | void { | ||||||
|  |         if (!this.api) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Refresh if promoted attributes get changed. | ||||||
|  |         if (loadResults.getAttributeRows().find(attr => | ||||||
|  |             attr.type === "label" && | ||||||
|  |             (attr.name?.startsWith("label:") || attr.name?.startsWith("relation:")) && | ||||||
|  |             attributes.isAffecting(attr, this.parentNote))) { | ||||||
|  |             this.#manageColumnUpdate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (loadResults.getBranchRows().some(branch => branch.parentNoteId === this.parentNote.noteId)) { | ||||||
|  |             this.#manageRowsUpdate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (loadResults.getAttributeRows().some(attr => this.args.noteIds.includes(attr.noteId!))) { | ||||||
|  |             this.#manageRowsUpdate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #manageColumnUpdate() { | ||||||
|  |         if (!this.api) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const info = getPromotedAttributeInformation(this.parentNote); | ||||||
|  |         const columnDefs = buildColumnDefinitions(info, this.persistentData?.columns); | ||||||
|  |         this.api.setColumns(columnDefs); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async #manageRowsUpdate() { | ||||||
|  |         if (!this.api) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const notes = await froca.getNotes(this.args.noteIds); | ||||||
|  |         const info = getPromotedAttributeInformation(this.parentNote); | ||||||
|  |         this.api.replaceData(await buildRowDefinitions(this.parentNote, notes, info)); | ||||||
|  |  | ||||||
|  |         if (this.noteIdToEdit) { | ||||||
|  |             const row = this.api?.getRows().find(r => r.getData().noteId === this.noteIdToEdit); | ||||||
|  |             if (row) { | ||||||
|  |                 row.getCell("title").edit(); | ||||||
|  |             } | ||||||
|  |             this.noteIdToEdit = undefined; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
| @@ -0,0 +1,51 @@ | |||||||
|  | import { CellComponent } from "tabulator-tables"; | ||||||
|  | import note_autocomplete from "../../../services/note_autocomplete"; | ||||||
|  | import froca from "../../../services/froca"; | ||||||
|  |  | ||||||
|  | export function RelationEditor(cell: CellComponent, onRendered, success, cancel, editorParams){ | ||||||
|  |     //cell - the cell component for the editable cell | ||||||
|  |     //onRendered - function to call when the editor has been rendered | ||||||
|  |     //success - function to call to pass thesuccessfully updated value to Tabulator | ||||||
|  |     //cancel - function to call to abort the edit and return to a normal cell | ||||||
|  |     //editorParams - params object passed into the editorParams column definition property | ||||||
|  |  | ||||||
|  |     //create and style editor | ||||||
|  |     const editor = document.createElement("input"); | ||||||
|  |  | ||||||
|  |     const $editor = $(editor); | ||||||
|  |     editor.classList.add("form-control"); | ||||||
|  |  | ||||||
|  |     //create and style input | ||||||
|  |     editor.style.padding = "3px"; | ||||||
|  |     editor.style.width = "100%"; | ||||||
|  |     editor.style.boxSizing = "border-box"; | ||||||
|  |  | ||||||
|  |     //Set value of editor to the current value of the cell | ||||||
|  |     const noteId = cell.getValue(); | ||||||
|  |     if (noteId) { | ||||||
|  |         const note = froca.getNoteFromCache(noteId); | ||||||
|  |         editor.value = note.title; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     //set focus on the select box when the editor is selected | ||||||
|  |     onRendered(function(){ | ||||||
|  |         note_autocomplete.initNoteAutocomplete($editor, { | ||||||
|  |             allowCreatingNotes: true | ||||||
|  |         }).on("autocomplete:noteselected", (event, suggestion, dataset) => { | ||||||
|  |             const notePath = suggestion.notePath; | ||||||
|  |             if (!notePath) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             const noteId = notePath.split("/").at(-1); | ||||||
|  |             success(noteId); | ||||||
|  |         }); | ||||||
|  |         editor.focus(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const container = document.createElement("div"); | ||||||
|  |     container.classList.add("input-group"); | ||||||
|  |     container.classList.add("autocomplete"); | ||||||
|  |     container.appendChild(editor); | ||||||
|  |     return container; | ||||||
|  | }; | ||||||
							
								
								
									
										74
									
								
								apps/client/src/widgets/view_widgets/table_view/rows.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								apps/client/src/widgets/view_widgets/table_view/rows.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | import FNote from "../../../entities/fnote.js"; | ||||||
|  | import type { LabelType } from "../../../services/promoted_attribute_definition_parser.js"; | ||||||
|  | import type { PromotedAttributeInformation } from "./columns.js"; | ||||||
|  |  | ||||||
|  | export type TableData = { | ||||||
|  |     iconClass: string; | ||||||
|  |     noteId: string; | ||||||
|  |     title: string; | ||||||
|  |     labels: Record<string, boolean | string | null>; | ||||||
|  |     relations: Record<string, boolean | string | null>; | ||||||
|  |     branchId: string; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export async function buildRowDefinitions(parentNote: FNote, notes: FNote[], infos: PromotedAttributeInformation[]) { | ||||||
|  |     const definitions: TableData[] = []; | ||||||
|  |     for (const branch of parentNote.getChildBranches()) { | ||||||
|  |         const note = await branch.getNote(); | ||||||
|  |         if (!note) { | ||||||
|  |             continue; // Skip if the note is not found | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const labels: typeof definitions[0]["labels"] = {}; | ||||||
|  |         const relations: typeof definitions[0]["relations"] = {}; | ||||||
|  |         for (const { name, type } of infos) { | ||||||
|  |             if (type === "relation") { | ||||||
|  |                 relations[name] = note.getRelationValue(name); | ||||||
|  |             } else if (type === "boolean") { | ||||||
|  |                 labels[name] = note.hasLabel(name); | ||||||
|  |             } else { | ||||||
|  |                 labels[name] = note.getLabelValue(name); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         definitions.push({ | ||||||
|  |             iconClass: note.getIcon(), | ||||||
|  |             noteId: note.noteId, | ||||||
|  |             title: note.title, | ||||||
|  |             labels, | ||||||
|  |             relations, | ||||||
|  |             branchId: branch.branchId | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return definitions; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default function getPromotedAttributeInformation(parentNote: FNote) { | ||||||
|  |     const info: PromotedAttributeInformation[] = []; | ||||||
|  |     for (const promotedAttribute of parentNote.getPromotedDefinitionAttributes()) { | ||||||
|  |         const def = promotedAttribute.getDefinition(); | ||||||
|  |         if (def.multiplicity !== "single") { | ||||||
|  |             console.warn("Multiple values are not supported for now"); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const [ labelType, name ] = promotedAttribute.name.split(":", 2); | ||||||
|  |         if (promotedAttribute.type !== "label") { | ||||||
|  |             console.warn("Relations are not supported for now"); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let type: LabelType | "relation" = def.labelType || "text"; | ||||||
|  |         if (labelType === "relation") { | ||||||
|  |             type = "relation"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         info.push({ | ||||||
|  |             name, | ||||||
|  |             title: def.promotedAlias, | ||||||
|  |             type | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     console.log("Promoted attribute information", info); | ||||||
|  |     return info; | ||||||
|  | } | ||||||
| @@ -1,18 +1,30 @@ | |||||||
| import type { EventData } from "../../components/app_context.js"; | import type { EventData } from "../../components/app_context.js"; | ||||||
|  | import Component from "../../components/component.js"; | ||||||
| import type FNote from "../../entities/fnote.js"; | import type FNote from "../../entities/fnote.js"; | ||||||
|  | import type { ViewTypeOptions } from "../../services/note_list_renderer.js"; | ||||||
|  | import ViewModeStorage from "./view_mode_storage.js"; | ||||||
|  |  | ||||||
| export interface ViewModeArgs { | export interface ViewModeArgs { | ||||||
|     $parent: JQuery<HTMLElement>; |     $parent: JQuery<HTMLElement>; | ||||||
|     parentNote: FNote; |     parentNote: FNote; | ||||||
|  |     parentNotePath?: string | null; | ||||||
|     noteIds: string[]; |     noteIds: string[]; | ||||||
|     showNotePath?: boolean; |     showNotePath?: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
| export default abstract class ViewMode { | export default abstract class ViewMode<T extends object> extends Component { | ||||||
|  |  | ||||||
|     constructor(args: ViewModeArgs) { |     private _viewStorage: ViewModeStorage<T> | null; | ||||||
|  |     protected parentNote: FNote; | ||||||
|  |     protected viewType: ViewTypeOptions; | ||||||
|  |  | ||||||
|  |     constructor(args: ViewModeArgs, viewType: ViewTypeOptions) { | ||||||
|  |         super(); | ||||||
|  |         this.parentNote = args.parentNote; | ||||||
|  |         this._viewStorage = null; | ||||||
|         // note list must be added to the DOM immediately, otherwise some functionality scripting (canvas) won't work |         // note list must be added to the DOM immediately, otherwise some functionality scripting (canvas) won't work | ||||||
|         args.$parent.empty(); |         args.$parent.empty(); | ||||||
|  |         this.viewType = viewType; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     abstract renderList(): Promise<JQuery<HTMLElement> | undefined>; |     abstract renderList(): Promise<JQuery<HTMLElement> | undefined>; | ||||||
| @@ -32,4 +44,13 @@ export default abstract class ViewMode { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     get viewStorage() { | ||||||
|  |         if (this._viewStorage) { | ||||||
|  |             return this._viewStorage; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this._viewStorage = new ViewModeStorage(this.parentNote, this.viewType); | ||||||
|  |         return this._viewStorage; | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								apps/client/src/widgets/view_widgets/view_mode_storage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								apps/client/src/widgets/view_widgets/view_mode_storage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | import type FNote from "../../entities/fnote"; | ||||||
|  | import type { ViewTypeOptions } from "../../services/note_list_renderer"; | ||||||
|  | import server from "../../services/server"; | ||||||
|  |  | ||||||
|  | const ATTACHMENT_ROLE = "viewConfig"; | ||||||
|  |  | ||||||
|  | export default class ViewModeStorage<T extends object> { | ||||||
|  |  | ||||||
|  |     private note: FNote; | ||||||
|  |     private attachmentName: string; | ||||||
|  |  | ||||||
|  |     constructor(note: FNote, viewType: ViewTypeOptions) { | ||||||
|  |         this.note = note; | ||||||
|  |         this.attachmentName = viewType + ".json"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async store(data: T) { | ||||||
|  |         const payload = { | ||||||
|  |             role: ATTACHMENT_ROLE, | ||||||
|  |             title: this.attachmentName, | ||||||
|  |             mime: "application/json", | ||||||
|  |             content: JSON.stringify(data), | ||||||
|  |             position: 0 | ||||||
|  |         }; | ||||||
|  |         await server.post(`notes/${this.note.noteId}/attachments?matchBy=title`, payload); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async restore() { | ||||||
|  |         const existingAttachments = await this.note.getAttachmentsByRole(ATTACHMENT_ROLE); | ||||||
|  |         if (existingAttachments.length === 0) { | ||||||
|  |             return undefined; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const attachment = existingAttachments | ||||||
|  |             .find(a => a.title === this.attachmentName); | ||||||
|  |         if (!attachment) { | ||||||
|  |             return undefined; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const attachmentData = await server.get<{ content: string } | null>(`attachments/${attachment.attachmentId}/blob`); | ||||||
|  |         return JSON.parse(attachmentData?.content ?? "{}"); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| { | { | ||||||
|   "extends": "../../tsconfig.base.json", |   "extends": "../../tsconfig.base.json", | ||||||
|   "compilerOptions": { |   "compilerOptions": { | ||||||
|  |     "lib": [ "ESNext" ], | ||||||
|     "outDir": "dist", |     "outDir": "dist", | ||||||
|     "types": [ |     "types": [ | ||||||
|       "node" |       "node" | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "dotenv": "16.6.0", |     "dotenv": "17.0.1", | ||||||
|     "electron": "36.6.0" |     "electron": "37.2.0" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@triliumnext/desktop", |   "name": "@triliumnext/desktop", | ||||||
|   "version": "0.95.0", |   "version": "0.96.0", | ||||||
|   "description": "Build your personal knowledge base with Trilium Notes", |   "description": "Build your personal knowledge base with Trilium Notes", | ||||||
|   "private": true, |   "private": true, | ||||||
|   "main": "main.cjs", |   "main": "main.cjs", | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|     "@types/electron-squirrel-startup": "1.0.2", |     "@types/electron-squirrel-startup": "1.0.2", | ||||||
|     "@triliumnext/server": "workspace:*", |     "@triliumnext/server": "workspace:*", | ||||||
|     "copy-webpack-plugin": "13.0.0", |     "copy-webpack-plugin": "13.0.0", | ||||||
|     "electron": "36.6.0", |     "electron": "37.2.0", | ||||||
|     "@electron-forge/cli": "7.8.1", |     "@electron-forge/cli": "7.8.1", | ||||||
|     "@electron-forge/maker-deb": "7.8.1", |     "@electron-forge/maker-deb": "7.8.1", | ||||||
|     "@electron-forge/maker-dmg": "7.8.1", |     "@electron-forge/maker-dmg": "7.8.1", | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
|     "@triliumnext/desktop": "workspace:*", |     "@triliumnext/desktop": "workspace:*", | ||||||
|     "@types/fs-extra": "11.0.4", |     "@types/fs-extra": "11.0.4", | ||||||
|     "copy-webpack-plugin": "13.0.0", |     "copy-webpack-plugin": "13.0.0", | ||||||
|     "electron": "36.6.0", |     "electron": "37.2.0", | ||||||
|     "fs-extra": "11.3.0" |     "fs-extra": "11.3.0" | ||||||
|   }, |   }, | ||||||
|   "nx": { |   "nx": { | ||||||
|   | |||||||
| @@ -17,6 +17,6 @@ | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "dotenv": "16.6.0" |     "dotenv": "17.0.1" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| { | { | ||||||
|     "dependencies": { |     "dependencies": { | ||||||
|         "better-sqlite3": "12.1.1" |         "better-sqlite3": "12.2.0" | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| { | { | ||||||
|   "name": "@triliumnext/server", |   "name": "@triliumnext/server", | ||||||
|   "version": "0.95.0", |   "version": "0.96.0", | ||||||
|   "description": "The server-side component of TriliumNext, which exposes the client via the web, allows for sync and provides a REST API for both internal and external use.", |   "description": "The server-side component of TriliumNext, which exposes the client via the web, allows for sync and provides a REST API for both internal and external use.", | ||||||
|   "private": true, |   "private": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "better-sqlite3": "12.1.1" |     "better-sqlite3": "12.2.0" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@electron/remote": "2.1.2", |     "@electron/remote": "2.1.2", | ||||||
| @@ -24,7 +24,7 @@ | |||||||
|     "@types/js-yaml": "4.0.9", |     "@types/js-yaml": "4.0.9", | ||||||
|     "@types/jsdom": "21.1.7", |     "@types/jsdom": "21.1.7", | ||||||
|     "@types/mime-types": "3.0.1", |     "@types/mime-types": "3.0.1", | ||||||
|     "@types/multer": "1.4.13", |     "@types/multer": "2.0.0", | ||||||
|     "@types/safe-compare": "1.1.2", |     "@types/safe-compare": "1.1.2", | ||||||
|     "@types/sanitize-html": "2.16.0", |     "@types/sanitize-html": "2.16.0", | ||||||
|     "@types/sax": "1.2.7", |     "@types/sax": "1.2.7", | ||||||
| @@ -39,7 +39,7 @@ | |||||||
|     "@types/ws": "8.18.1", |     "@types/ws": "8.18.1", | ||||||
|     "@types/xml2js": "0.4.14", |     "@types/xml2js": "0.4.14", | ||||||
|     "express-http-proxy": "2.1.1", |     "express-http-proxy": "2.1.1", | ||||||
|     "@anthropic-ai/sdk": "0.55.0", |     "@anthropic-ai/sdk": "0.55.1", | ||||||
|     "@braintree/sanitize-url": "7.1.1", |     "@braintree/sanitize-url": "7.1.1", | ||||||
|     "@triliumnext/commons": "workspace:*", |     "@triliumnext/commons": "workspace:*", | ||||||
|     "@triliumnext/express-partial-content": "workspace:*", |     "@triliumnext/express-partial-content": "workspace:*", | ||||||
| @@ -59,7 +59,7 @@ | |||||||
|     "debounce": "2.2.0", |     "debounce": "2.2.0", | ||||||
|     "debug": "4.4.1", |     "debug": "4.4.1", | ||||||
|     "ejs": "3.1.10", |     "ejs": "3.1.10", | ||||||
|     "electron": "36.6.0", |     "electron": "37.2.0", | ||||||
|     "electron-debug": "4.1.0", |     "electron-debug": "4.1.0", | ||||||
|     "electron-window-state": "5.0.3", |     "electron-window-state": "5.0.3", | ||||||
|     "escape-html": "1.0.3", |     "escape-html": "1.0.3", | ||||||
| @@ -74,7 +74,7 @@ | |||||||
|     "html2plaintext": "2.1.4", |     "html2plaintext": "2.1.4", | ||||||
|     "http-proxy-agent": "7.0.2", |     "http-proxy-agent": "7.0.2", | ||||||
|     "https-proxy-agent": "7.0.6", |     "https-proxy-agent": "7.0.6", | ||||||
|     "i18next": "25.2.1", |     "i18next": "25.3.0", | ||||||
|     "i18next-fs-backend": "2.6.0", |     "i18next-fs-backend": "2.6.0", | ||||||
|     "image-type": "6.0.0", |     "image-type": "6.0.0", | ||||||
|     "ini": "5.0.0", |     "ini": "5.0.0", | ||||||
| @@ -83,12 +83,12 @@ | |||||||
|     "jimp": "1.6.0", |     "jimp": "1.6.0", | ||||||
|     "js-yaml": "4.1.0", |     "js-yaml": "4.1.0", | ||||||
|     "jsdom": "26.1.0", |     "jsdom": "26.1.0", | ||||||
|     "marked": "15.0.12", |     "marked": "16.0.0", | ||||||
|     "mime-types": "3.0.1", |     "mime-types": "3.0.1", | ||||||
|     "multer": "2.0.1", |     "multer": "2.0.1", | ||||||
|     "normalize-strings": "1.1.1", |     "normalize-strings": "1.1.1", | ||||||
|     "ollama": "0.5.16", |     "ollama": "0.5.16", | ||||||
|     "openai": "5.8.1", |     "openai": "5.8.2", | ||||||
|     "rand-token": "1.0.1", |     "rand-token": "1.0.1", | ||||||
|     "safe-compare": "1.1.4", |     "safe-compare": "1.1.4", | ||||||
|     "sanitize-filename": "1.6.3", |     "sanitize-filename": "1.6.3", | ||||||
| @@ -105,7 +105,7 @@ | |||||||
|     "tmp": "0.2.3", |     "tmp": "0.2.3", | ||||||
|     "turndown": "7.2.0", |     "turndown": "7.2.0", | ||||||
|     "unescape": "1.0.1", |     "unescape": "1.0.1", | ||||||
|     "ws": "8.18.2", |     "ws": "8.18.3", | ||||||
|     "xml2js": "0.6.2", |     "xml2js": "0.6.2", | ||||||
|     "yauzl": "3.2.0" |     "yauzl": "3.2.0" | ||||||
|   }, |   }, | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -42,5 +42,5 @@ | |||||||
|   This will export the notes in an unencrypted form, so if you reimport into |   This will export the notes in an unencrypted form, so if you reimport into | ||||||
|   Trilium, make sure to re-protect these notes.</p> |   Trilium, make sure to re-protect these notes.</p> | ||||||
| <h2>Supported syntax</h2> | <h2>Supported syntax</h2> | ||||||
| <p>See the dedicated page: <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/mHbBMPDPkVV5/Oau6X9rCuegd/_help_rJ9grSgoExl9">Supported syntax</a> | <p>See the dedicated page: <a class="reference-link" href="#root/_help_rJ9grSgoExl9">Supported syntax</a> | ||||||
| </p> | </p> | ||||||
| @@ -41,7 +41,7 @@ | |||||||
|   Trilium-compatible syntax, but it will not export Trilium Notes into Markdown |   Trilium-compatible syntax, but it will not export Trilium Notes into Markdown | ||||||
|   files with this syntax.</p> |   files with this syntax.</p> | ||||||
| <aside class="admonition important"> | <aside class="admonition important"> | ||||||
|   <p>The path to pages in wikilinks is resolved relatively to the <em>import root </em>and |   <p>The path to pages in wikilinks is resolved relatively to the <em>import root</em> and | ||||||
|     not the current directory of the note. This is to be inline with other |     not the current directory of the note. This is to be inline with other | ||||||
|     platforms that use wikilinks such as SilverBullet.</p> |     platforms that use wikilinks such as SilverBullet.</p> | ||||||
|   <p>The root path of the import is determined as follows:</p> |   <p>The root path of the import is determined as follows:</p> | ||||||
|   | |||||||
							
								
								
									
										98
									
								
								apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note List/Table.html
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note List/Table.html
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | |||||||
|  | <figure class="image"> | ||||||
|  |   <img style="aspect-ratio:1050/259;" src="Table_image.png" width="1050" | ||||||
|  |   height="259"> | ||||||
|  | </figure> | ||||||
|  | <p>The table view displays information in a grid, where the rows are individual | ||||||
|  |   notes and the columns are <a class="reference-link" href="#root/pOsGYCXsbNQG/tC7s2alapj8V/zEY4DaJG4YT5/_help_OFXdgB2nNk1F">Promoted Attributes</a>. | ||||||
|  |   In addition, values are editable.</p> | ||||||
|  | <h2>Interaction</h2> | ||||||
|  | <h3>Creating a new table</h3> | ||||||
|  | <p>Right click the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a> and | ||||||
|  |   select <em>Insert child note</em> and look for the <em>Table item</em>.</p> | ||||||
|  | <h3>Adding columns</h3> | ||||||
|  | <p>Each column is a <a href="#root/pOsGYCXsbNQG/tC7s2alapj8V/zEY4DaJG4YT5/_help_OFXdgB2nNk1F">promoted attribute</a> that | ||||||
|  |   is defined on the Book note. Ideally, the promoted attributes need to be | ||||||
|  |   inheritable in order to show up in the child notes.</p> | ||||||
|  | <p>To create a new column, simply press <em>Add new column </em>at the bottom | ||||||
|  |   of the table.</p> | ||||||
|  | <p>There are also a few predefined columns:</p> | ||||||
|  | <ul> | ||||||
|  |   <li>The current item number, identified by the <code>#</code> symbol. This simply | ||||||
|  |     counts the note and is affected by sorting.</li> | ||||||
|  |   <li><a class="reference-link" href="#root/pOsGYCXsbNQG/tC7s2alapj8V/_help_m1lbrzyKDaRB">Note ID</a>, | ||||||
|  |     representing the unique ID used internally by Trilium</li> | ||||||
|  |   <li>The title of the note.</li> | ||||||
|  | </ul> | ||||||
|  | <h3>Adding new rows</h3> | ||||||
|  | <p>Each row is actually a note that is a child of the book note.</p> | ||||||
|  | <p>To create a new note, press <em>Add new row</em> at the bottom of the table. | ||||||
|  |   By default it will try to edit the title of the newly created note.</p> | ||||||
|  | <p>Alternatively, the note can be created from the<a class="reference-link" | ||||||
|  |   href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a> or | ||||||
|  |   <a | ||||||
|  |   href="#root/pOsGYCXsbNQG/_help_CdNpE2pqjmI6">scripting</a>.</p> | ||||||
|  | <h3>Editing data</h3> | ||||||
|  | <p>Simply click on a cell within a row to change its value. The change will | ||||||
|  |   not only reflect in the table, but also as an attribute of the corresponding | ||||||
|  |   note.</p> | ||||||
|  | <ul> | ||||||
|  |   <li>The editing will respect the type of the promoted attribute, by presenting | ||||||
|  |     a normal text box, a number selector or a date selector for example.</li> | ||||||
|  |   <li>It also possible to change the title of a note.</li> | ||||||
|  |   <li>Editing relations is also possible, by using the note autocomplete.</li> | ||||||
|  | </ul> | ||||||
|  | <h2>Working with the data</h2> | ||||||
|  | <h3>Sorting</h3> | ||||||
|  | <p>It is possible to sort the data by the values of a column:</p> | ||||||
|  | <ul> | ||||||
|  |   <li>To do so, simply click on a column.</li> | ||||||
|  |   <li>To switch between ascending or descending sort, simply click again on | ||||||
|  |     the same column. The arrow next to the column will indicate the direction | ||||||
|  |     of the sort.</li> | ||||||
|  | </ul> | ||||||
|  | <h3>Reordering and hiding columns</h3> | ||||||
|  | <ul> | ||||||
|  |   <li>Columns can be reordered by dragging the header of the columns.</li> | ||||||
|  |   <li>Columns can be hidden or shown by right clicking on a column and clicking | ||||||
|  |     the item corresponding to the column.</li> | ||||||
|  | </ul> | ||||||
|  | <h3>Reordering rows</h3> | ||||||
|  | <p>Notes can be dragged around to change their order. This will also change | ||||||
|  |   the order of the note in the <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a>.</p> | ||||||
|  | <p>Currently, it's possible to reorder notes even if sorting is used, but | ||||||
|  |   the result might be inconsistent.</p> | ||||||
|  | <h2>Limitations</h2> | ||||||
|  | <p>The table functionality is still in its early stages, as such it faces | ||||||
|  |   quite a few important limitations:</p> | ||||||
|  | <ol> | ||||||
|  |   <li>As mentioned previously, the columns of the table are defined as  | ||||||
|  |     <a | ||||||
|  |     class="reference-link" href="#root/pOsGYCXsbNQG/tC7s2alapj8V/zEY4DaJG4YT5/_help_OFXdgB2nNk1F">Promoted Attributes</a>. | ||||||
|  |       <ol> | ||||||
|  |         <li>But only the promoted attributes that are defined at the level of the | ||||||
|  |           Book note are actually taken into consideration.</li> | ||||||
|  |         <li>There are plans to recursively look for columns across the sub-hierarchy.</li> | ||||||
|  |       </ol> | ||||||
|  |   </li> | ||||||
|  |   <li>Hierarchy is not yet supported, so the table will only show the items | ||||||
|  |     that are direct children of the <em>Book</em> note.</li> | ||||||
|  |   <li>Multiple labels and relations are not supported. If a <a class="reference-link" | ||||||
|  |     href="#root/pOsGYCXsbNQG/tC7s2alapj8V/zEY4DaJG4YT5/_help_OFXdgB2nNk1F">Promoted Attributes</a> is | ||||||
|  |     defined with a <em>Multi value</em> specificity, they will be ignored.</li> | ||||||
|  | </ol> | ||||||
|  | <h2>Use in search</h2> | ||||||
|  | <p>The table view can be used in a <a class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_m523cpzocqaD">Saved Search</a> by | ||||||
|  |   adding the <code>#viewType=table</code> attribute.</p> | ||||||
|  | <p>Unlike when used in a book, saved searches are not limited to the sub-hierarchy | ||||||
|  |   of a note and allows for advanced queries thanks to the power of the  | ||||||
|  |   <a | ||||||
|  |   class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/wArbEsdSae6g/_help_eIg8jdvaoNNd">Search</a>.</p> | ||||||
|  | <p>However, there are also some limitations:</p> | ||||||
|  | <ul> | ||||||
|  |   <li>It's not possible to reorder notes.</li> | ||||||
|  |   <li>It's not possible to add a new row.</li> | ||||||
|  | </ul> | ||||||
|  | <p>Columns are supported, by being defined as <a class="reference-link" | ||||||
|  |   href="#root/pOsGYCXsbNQG/tC7s2alapj8V/zEY4DaJG4YT5/_help_OFXdgB2nNk1F">Promoted Attributes</a> to | ||||||
|  |   the <a class="reference-link" href="#root/pOsGYCXsbNQG/KSZ04uQ2D1St/_help_m523cpzocqaD">Saved Search</a> note.</p> | ||||||
|  | <p>Editing is also supported.</p> | ||||||
							
								
								
									
										
											BIN
										
									
								
								apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note List/Table_image.png
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Note List/Table_image.png
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 34 KiB | 
							
								
								
									
										5
									
								
								apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Note Map.html
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Note Map.html
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -6,4 +6,7 @@ | |||||||
|   of the same name: <a href="#root/_help_BCkXAVs63Ttv">Note Map (Link map, Tree map)</a>.</p> |   of the same name: <a href="#root/_help_BCkXAVs63Ttv">Note Map (Link map, Tree map)</a>.</p> | ||||||
| <p>Once created, the note map will display the relations between notes. Only | <p>Once created, the note map will display the relations between notes. Only | ||||||
|   the notes that are part of the parent of the note map will be displayed |   the notes that are part of the parent of the note map will be displayed | ||||||
|   (including their children).</p> |   (including their children).</p> | ||||||
|  | <p>The labels <code>mapIncludeRelation</code> and <code>mapExcludeRelation</code>, | ||||||
|  |   if set, filter the note map to include only the specified relations or | ||||||
|  |   to exclude the specified relations, respectively.</p> | ||||||
| @@ -62,4 +62,4 @@ class="image image-style-align-center"> | |||||||
|     are currently no plans for adjusting it or allowing the user to customize |     are currently no plans for adjusting it or allowing the user to customize | ||||||
|     them.</p> |     them.</p> | ||||||
|   <h3>Markdown support</h3> |   <h3>Markdown support</h3> | ||||||
|   <p>See <a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/mHbBMPDPkVV5/Oau6X9rCuegd/_help_rJ9grSgoExl9">Supported syntax</a>.</p> |   <p>See <a class="reference-link" href="#root/_help_rJ9grSgoExl9">Supported syntax</a>.</p> | ||||||
| @@ -48,7 +48,8 @@ function updateNoteAttribute(req: Request) { | |||||||
|         attribute = new BAttribute({ |         attribute = new BAttribute({ | ||||||
|             noteId: noteId, |             noteId: noteId, | ||||||
|             name: body.name, |             name: body.name, | ||||||
|             type: body.type |             type: body.type, | ||||||
|  |             isInheritable: body.isInheritable | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -110,6 +110,11 @@ function getLinkMap(req: Request) { | |||||||
|     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy("excludeFromNoteMap"); |     const ignoreExcludeFromNoteMap = mapRootNote.isLabelTruthy("excludeFromNoteMap"); | ||||||
|     let unfilteredNotes; |     let unfilteredNotes; | ||||||
|  |  | ||||||
|  |     const toSet = (data: unknown) => new Set<string>(data instanceof Array ? data : []); | ||||||
|  |  | ||||||
|  |     const excludeRelations = toSet(req.body.excludeRelations); | ||||||
|  |     const includeRelations = toSet(req.body.includeRelations); | ||||||
|  |  | ||||||
|     if (mapRootNote.type === "search") { |     if (mapRootNote.type === "search") { | ||||||
|         // for search notes, we want to consider the direct search results only without the descendants |         // for search notes, we want to consider the direct search results only without the descendants | ||||||
|         unfilteredNotes = mapRootNote.getSearchResultNotes(); |         unfilteredNotes = mapRootNote.getSearchResultNotes(); | ||||||
| @@ -152,6 +157,10 @@ function getLinkMap(req: Request) { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 return !parentNote.getChildNotes().find((childNote) => childNote.noteId === rel.value); |                 return !parentNote.getChildNotes().find((childNote) => childNote.noteId === rel.value); | ||||||
|  |             } else if (includeRelations.size != 0 && !includeRelations.has(rel.name)) { | ||||||
|  |                 return false; | ||||||
|  |             } else if (excludeRelations.has(rel.name)) { | ||||||
|  |                 return false; | ||||||
|             } else { |             } else { | ||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -255,8 +255,12 @@ export interface Api { | |||||||
|     /** |     /** | ||||||
|      * Returns week note for given date. If such a note doesn't exist, it is created. |      * Returns week note for given date. If such a note doesn't exist, it is created. | ||||||
|      * |      * | ||||||
|  |      * <p> | ||||||
|  |      * If the calendar does not support week notes, this method will return `null`. | ||||||
|  |      * | ||||||
|      * @param date in YYYY-MM-DD format |      * @param date in YYYY-MM-DD format | ||||||
|      * @param rootNote - specify calendar root note, normally leave empty to use the default calendar |      * @param rootNote - specify calendar root note, normally leave empty to use the default calendar | ||||||
|  |      * @return an existing or newly created week note, or `null` if the calendar does not support week notes. | ||||||
|      */ |      */ | ||||||
|     getWeekNote(date: string, rootNote: BNote): BNote | null; |     getWeekNote(date: string, rootNote: BNote): BNote | null; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -45,6 +45,8 @@ export default [ | |||||||
|     { type: "label", name: "pageSize" }, |     { type: "label", name: "pageSize" }, | ||||||
|     { type: "label", name: "viewType" }, |     { type: "label", name: "viewType" }, | ||||||
|     { type: "label", name: "mapRootNoteId" }, |     { type: "label", name: "mapRootNoteId" }, | ||||||
|  |     { type: "label", name: "mapExcludeRelation" }, | ||||||
|  |     { type: "label", name: "mapIncludeRelation" }, | ||||||
|     { type: "label", name: "bookmarkFolder" }, |     { type: "label", name: "bookmarkFolder" }, | ||||||
|     { type: "label", name: "sorted" }, |     { type: "label", name: "sorted" }, | ||||||
|     { type: "label", name: "sortDirection" }, |     { type: "label", name: "sortDirection" }, | ||||||
|   | |||||||
| @@ -75,6 +75,9 @@ async function exportToZip(taskContext: TaskContext, branch: BBranch, format: "h | |||||||
|  |  | ||||||
|     function getDataFileName(type: string | null, mime: string, baseFileName: string, existingFileNames: Record<string, number>): string { |     function getDataFileName(type: string | null, mime: string, baseFileName: string, existingFileNames: Record<string, number>): string { | ||||||
|         let fileName = baseFileName.trim(); |         let fileName = baseFileName.trim(); | ||||||
|  |         if (!fileName) { | ||||||
|  |             fileName = "note"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         // Crop fileName to avoid its length exceeding 30 and prevent cutting into the extension. |         // Crop fileName to avoid its length exceeding 30 and prevent cutting into the extension. | ||||||
|         if (fileName.length > 30) { |         if (fileName.length > 30) { | ||||||
| @@ -366,7 +369,7 @@ ${markdownContent}`; | |||||||
|     function saveNote(noteMeta: NoteMeta, filePathPrefix: string) { |     function saveNote(noteMeta: NoteMeta, filePathPrefix: string) { | ||||||
|         log.info(`Exporting note '${noteMeta.noteId}'`); |         log.info(`Exporting note '${noteMeta.noteId}'`); | ||||||
|  |  | ||||||
|         if (!noteMeta.noteId || !noteMeta.title) { |         if (!noteMeta.noteId || noteMeta.title === undefined) { | ||||||
|             throw new Error("Missing note meta."); |             throw new Error("Missing note meta."); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -515,97 +518,108 @@ ${markdownContent}`; | |||||||
|         archive.append(cssContent, { name: cssMeta.dataFileName }); |         archive.append(cssContent, { name: cssMeta.dataFileName }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const existingFileNames: Record<string, number> = format === "html" ? { navigation: 0, index: 1 } : {}; |     try { | ||||||
|     const rootMeta = createNoteMeta(branch, { notePath: [] }, existingFileNames); |         const existingFileNames: Record<string, number> = format === "html" ? { navigation: 0, index: 1 } : {}; | ||||||
|     if (!rootMeta) { |         const rootMeta = createNoteMeta(branch, { notePath: [] }, existingFileNames); | ||||||
|         throw new Error("Unable to create root meta."); |         if (!rootMeta) { | ||||||
|     } |             throw new Error("Unable to create root meta."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|     const metaFile: NoteMetaFile = { |         const metaFile: NoteMetaFile = { | ||||||
|         formatVersion: 2, |             formatVersion: 2, | ||||||
|         appVersion: packageInfo.version, |             appVersion: packageInfo.version, | ||||||
|         files: [rootMeta] |             files: [rootMeta] | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let navigationMeta: NoteMeta | null = null; |  | ||||||
|     let indexMeta: NoteMeta | null = null; |  | ||||||
|     let cssMeta: NoteMeta | null = null; |  | ||||||
|  |  | ||||||
|     if (format === "html") { |  | ||||||
|         navigationMeta = { |  | ||||||
|             noImport: true, |  | ||||||
|             dataFileName: "navigation.html" |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         metaFile.files.push(navigationMeta); |         let navigationMeta: NoteMeta | null = null; | ||||||
|  |         let indexMeta: NoteMeta | null = null; | ||||||
|  |         let cssMeta: NoteMeta | null = null; | ||||||
|  |  | ||||||
|         indexMeta = { |         if (format === "html") { | ||||||
|             noImport: true, |             navigationMeta = { | ||||||
|             dataFileName: "index.html" |                 noImport: true, | ||||||
|         }; |                 dataFileName: "navigation.html" | ||||||
|  |             }; | ||||||
|  |  | ||||||
|         metaFile.files.push(indexMeta); |             metaFile.files.push(navigationMeta); | ||||||
|  |  | ||||||
|         cssMeta = { |             indexMeta = { | ||||||
|             noImport: true, |                 noImport: true, | ||||||
|             dataFileName: "style.css" |                 dataFileName: "index.html" | ||||||
|         }; |             }; | ||||||
|  |  | ||||||
|         metaFile.files.push(cssMeta); |             metaFile.files.push(indexMeta); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (const noteMeta of Object.values(noteIdToMeta)) { |             cssMeta = { | ||||||
|         // filter out relations which are not inside this export |                 noImport: true, | ||||||
|         noteMeta.attributes = (noteMeta.attributes || []).filter((attr) => { |                 dataFileName: "style.css" | ||||||
|             if (attr.type !== "relation") { |             }; | ||||||
|                 return true; |  | ||||||
|             } else if (attr.value in noteIdToMeta) { |             metaFile.files.push(cssMeta); | ||||||
|                 return true; |         } | ||||||
|             } else if (attr.value === "root" || attr.value?.startsWith("_")) { |  | ||||||
|                 // relations to "named" noteIds can be preserved |         for (const noteMeta of Object.values(noteIdToMeta)) { | ||||||
|                 return true; |             // filter out relations which are not inside this export | ||||||
|             } else { |             noteMeta.attributes = (noteMeta.attributes || []).filter((attr) => { | ||||||
|                 return false; |                 if (attr.type !== "relation") { | ||||||
|  |                     return true; | ||||||
|  |                 } else if (attr.value in noteIdToMeta) { | ||||||
|  |                     return true; | ||||||
|  |                 } else if (attr.value === "root" || attr.value?.startsWith("_")) { | ||||||
|  |                     // relations to "named" noteIds can be preserved | ||||||
|  |                     return true; | ||||||
|  |                 } else { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!rootMeta) { | ||||||
|  |             // corner case of disabled export for exported note | ||||||
|  |             if ("sendStatus" in res) { | ||||||
|  |                 res.sendStatus(400); | ||||||
|             } |             } | ||||||
|         }); |             return; | ||||||
|     } |         } | ||||||
|  |  | ||||||
|  |         const metaFileJson = JSON.stringify(metaFile, null, "\t"); | ||||||
|  |  | ||||||
|  |         archive.append(metaFileJson, { name: "!!!meta.json" }); | ||||||
|  |  | ||||||
|  |         saveNote(rootMeta, ""); | ||||||
|  |  | ||||||
|  |         if (format === "html") { | ||||||
|  |             if (!navigationMeta || !indexMeta || !cssMeta) { | ||||||
|  |                 throw new Error("Missing meta."); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             saveNavigation(rootMeta, navigationMeta); | ||||||
|  |             saveIndex(rootMeta, indexMeta); | ||||||
|  |             saveCss(rootMeta, cssMeta); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const note = branch.getNote(); | ||||||
|  |         const zipFileName = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.getTitleOrProtected() || "note"}.zip`; | ||||||
|  |  | ||||||
|  |         if (setHeaders && "setHeader" in res) { | ||||||
|  |             res.setHeader("Content-Disposition", getContentDisposition(zipFileName)); | ||||||
|  |             res.setHeader("Content-Type", "application/zip"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         archive.pipe(res); | ||||||
|  |         await archive.finalize(); | ||||||
|  |         taskContext.taskSucceeded(); | ||||||
|  |     } catch (e: unknown) { | ||||||
|  |         const message = `Export failed with error: ${e instanceof Error ? e.message : String(e)}`; | ||||||
|  |         log.error(message); | ||||||
|  |         taskContext.reportError(message); | ||||||
|  |  | ||||||
|     if (!rootMeta) { |  | ||||||
|         // corner case of disabled export for exported note |  | ||||||
|         if ("sendStatus" in res) { |         if ("sendStatus" in res) { | ||||||
|             res.sendStatus(400); |             res.removeHeader("Content-Disposition"); | ||||||
|  |             res.removeHeader("Content-Type"); | ||||||
|  |             res.status(500).send(message); | ||||||
|         } |         } | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const metaFileJson = JSON.stringify(metaFile, null, "\t"); |  | ||||||
|  |  | ||||||
|     archive.append(metaFileJson, { name: "!!!meta.json" }); |  | ||||||
|  |  | ||||||
|     saveNote(rootMeta, ""); |  | ||||||
|  |  | ||||||
|     if (format === "html") { |  | ||||||
|         if (!navigationMeta || !indexMeta || !cssMeta) { |  | ||||||
|             throw new Error("Missing meta."); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         saveNavigation(rootMeta, navigationMeta); |  | ||||||
|         saveIndex(rootMeta, indexMeta); |  | ||||||
|         saveCss(rootMeta, cssMeta); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const note = branch.getNote(); |  | ||||||
|     const zipFileName = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.getTitleOrProtected()}.zip`; |  | ||||||
|  |  | ||||||
|     if (setHeaders && "setHeader" in res) { |  | ||||||
|         res.setHeader("Content-Disposition", getContentDisposition(zipFileName)); |  | ||||||
|         res.setHeader("Content-Type", "application/zip"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     archive.pipe(res); |  | ||||||
|     await archive.finalize(); |  | ||||||
|  |  | ||||||
|     taskContext.taskSucceeded(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| async function exportToZipFile(noteId: string, format: "markdown" | "html", zipFilePath: string, zipExportOptions?: AdvancedExportOptions) { | async function exportToZipFile(noteId: string, format: "markdown" | "html", zipFilePath: string, zipExportOptions?: AdvancedExportOptions) { | ||||||
|   | |||||||
| @@ -26,6 +26,23 @@ export default function buildHiddenSubtreeTemplates() { | |||||||
|                         value: "promoted,alias=Description,single,text" |                         value: "promoted,alias=Description,single,text" | ||||||
|                     } |                     } | ||||||
|                 ] |                 ] | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |                 id: "_template_table", | ||||||
|  |                 type: "book", | ||||||
|  |                 title: "Table", | ||||||
|  |                 icon: "bx bx-table", | ||||||
|  |                 attributes: [ | ||||||
|  |                     { | ||||||
|  |                         name: "template", | ||||||
|  |                         type: "label" | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: "viewType", | ||||||
|  |                         type: "label", | ||||||
|  |                         value: "table" | ||||||
|  |                     } | ||||||
|  |                 ] | ||||||
|             } |             } | ||||||
|         ] |         ] | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -29,16 +29,18 @@ | |||||||
| 		"tailwindcss": "^4.0.0", | 		"tailwindcss": "^4.0.0", | ||||||
| 		"typescript": "^5.0.0", | 		"typescript": "^5.0.0", | ||||||
| 		"typescript-eslint": "^8.20.0", | 		"typescript-eslint": "^8.20.0", | ||||||
| 		"vite": "^6.2.6" | 		"vite": "^7.0.0" | ||||||
| 	}, | 	}, | ||||||
| 	"dependencies": { | 	"dependencies": { | ||||||
| 		"@inlang/paraglide-js": "^2.0.0" | 		"@inlang/paraglide-js": "^2.0.0" | ||||||
| 	}, | 	}, | ||||||
| 	"nx": { | 	"nx": { | ||||||
| 		"typecheck": { | 		"targets": { | ||||||
| 			"dependsOn": [ | 			"typecheck": { | ||||||
| 				"build" | 				"dependsOn": [ | ||||||
| 			] | 					"build" | ||||||
|  | 				] | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								docs/Developer Guide/!!!meta.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								docs/Developer Guide/!!!meta.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|     "formatVersion": 2, |     "formatVersion": 2, | ||||||
|     "appVersion": "0.95.0", |     "appVersion": "0.96.0", | ||||||
|     "files": [ |     "files": [ | ||||||
|         { |         { | ||||||
|             "isClone": false, |             "isClone": false, | ||||||
|   | |||||||
							
								
								
									
										92
									
								
								docs/Release Notes/!!!meta.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										92
									
								
								docs/Release Notes/!!!meta.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|     "formatVersion": 2, |     "formatVersion": 2, | ||||||
|     "appVersion": "0.95.0", |     "appVersion": "0.96.0", | ||||||
|     "files": [ |     "files": [ | ||||||
|         { |         { | ||||||
|             "isClone": false, |             "isClone": false, | ||||||
| @@ -61,6 +61,32 @@ | |||||||
|             "attachments": [], |             "attachments": [], | ||||||
|             "dirFileName": "Release Notes", |             "dirFileName": "Release Notes", | ||||||
|             "children": [ |             "children": [ | ||||||
|  |                 { | ||||||
|  |                     "isClone": false, | ||||||
|  |                     "noteId": "mYXFde3LuNR7", | ||||||
|  |                     "notePath": [ | ||||||
|  |                         "hD3V4hiu2VW4", | ||||||
|  |                         "mYXFde3LuNR7" | ||||||
|  |                     ], | ||||||
|  |                     "title": "v0.96.0", | ||||||
|  |                     "notePosition": 10, | ||||||
|  |                     "prefix": null, | ||||||
|  |                     "isExpanded": false, | ||||||
|  |                     "type": "text", | ||||||
|  |                     "mime": "text/html", | ||||||
|  |                     "attributes": [ | ||||||
|  |                         { | ||||||
|  |                             "type": "relation", | ||||||
|  |                             "name": "template", | ||||||
|  |                             "value": "wyurrlcDl416", | ||||||
|  |                             "isInheritable": false, | ||||||
|  |                             "position": 60 | ||||||
|  |                         } | ||||||
|  |                     ], | ||||||
|  |                     "format": "markdown", | ||||||
|  |                     "dataFileName": "v0.96.0.md", | ||||||
|  |                     "attachments": [] | ||||||
|  |                 }, | ||||||
|                 { |                 { | ||||||
|                     "isClone": false, |                     "isClone": false, | ||||||
|                     "noteId": "jthwbL0FdaeU", |                     "noteId": "jthwbL0FdaeU", | ||||||
| @@ -69,7 +95,7 @@ | |||||||
|                         "jthwbL0FdaeU" |                         "jthwbL0FdaeU" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.95.0", |                     "title": "v0.95.0", | ||||||
|                     "notePosition": 10, |                     "notePosition": 20, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -95,7 +121,7 @@ | |||||||
|                         "7HGYsJbLuhnv" |                         "7HGYsJbLuhnv" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.94.1", |                     "title": "v0.94.1", | ||||||
|                     "notePosition": 20, |                     "notePosition": 30, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -121,7 +147,7 @@ | |||||||
|                         "Neq53ujRGBqv" |                         "Neq53ujRGBqv" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.94.0", |                     "title": "v0.94.0", | ||||||
|                     "notePosition": 30, |                     "notePosition": 40, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -147,7 +173,7 @@ | |||||||
|                         "VN3xnce1vLkX" |                         "VN3xnce1vLkX" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.93.0", |                     "title": "v0.93.0", | ||||||
|                     "notePosition": 40, |                     "notePosition": 50, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -165,7 +191,7 @@ | |||||||
|                         "WRaBfQqPr6qo" |                         "WRaBfQqPr6qo" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.92.7", |                     "title": "v0.92.7", | ||||||
|                     "notePosition": 50, |                     "notePosition": 60, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -191,7 +217,7 @@ | |||||||
|                         "a2rwfKNmUFU1" |                         "a2rwfKNmUFU1" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.92.6", |                     "title": "v0.92.6", | ||||||
|                     "notePosition": 60, |                     "notePosition": 70, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -209,7 +235,7 @@ | |||||||
|                         "fEJ8qErr0BKL" |                         "fEJ8qErr0BKL" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.92.5-beta", |                     "title": "v0.92.5-beta", | ||||||
|                     "notePosition": 70, |                     "notePosition": 80, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -227,7 +253,7 @@ | |||||||
|                         "kkkZQQGSXjwy" |                         "kkkZQQGSXjwy" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.92.4", |                     "title": "v0.92.4", | ||||||
|                     "notePosition": 80, |                     "notePosition": 90, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -245,7 +271,7 @@ | |||||||
|                         "vAroNixiezaH" |                         "vAroNixiezaH" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.92.3-beta", |                     "title": "v0.92.3-beta", | ||||||
|                     "notePosition": 90, |                     "notePosition": 100, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -263,7 +289,7 @@ | |||||||
|                         "mHEq1wxAKNZd" |                         "mHEq1wxAKNZd" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.92.2-beta", |                     "title": "v0.92.2-beta", | ||||||
|                     "notePosition": 100, |                     "notePosition": 110, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -281,7 +307,7 @@ | |||||||
|                         "IykjoAmBpc61" |                         "IykjoAmBpc61" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.92.1-beta", |                     "title": "v0.92.1-beta", | ||||||
|                     "notePosition": 110, |                     "notePosition": 120, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -299,7 +325,7 @@ | |||||||
|                         "dq2AJ9vSBX4Y" |                         "dq2AJ9vSBX4Y" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.92.0-beta", |                     "title": "v0.92.0-beta", | ||||||
|                     "notePosition": 120, |                     "notePosition": 130, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -317,7 +343,7 @@ | |||||||
|                         "3a8aMe4jz4yM" |                         "3a8aMe4jz4yM" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.91.6", |                     "title": "v0.91.6", | ||||||
|                     "notePosition": 130, |                     "notePosition": 140, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -335,7 +361,7 @@ | |||||||
|                         "8djQjkiDGESe" |                         "8djQjkiDGESe" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.91.5", |                     "title": "v0.91.5", | ||||||
|                     "notePosition": 140, |                     "notePosition": 150, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -353,7 +379,7 @@ | |||||||
|                         "OylxVoVJqNmr" |                         "OylxVoVJqNmr" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.91.4-beta", |                     "title": "v0.91.4-beta", | ||||||
|                     "notePosition": 150, |                     "notePosition": 160, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -371,7 +397,7 @@ | |||||||
|                         "tANGQDvnyhrj" |                         "tANGQDvnyhrj" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.91.3-beta", |                     "title": "v0.91.3-beta", | ||||||
|                     "notePosition": 160, |                     "notePosition": 170, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -389,7 +415,7 @@ | |||||||
|                         "hMoBfwSoj1SC" |                         "hMoBfwSoj1SC" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.91.2-beta", |                     "title": "v0.91.2-beta", | ||||||
|                     "notePosition": 170, |                     "notePosition": 180, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -407,7 +433,7 @@ | |||||||
|                         "a2XMSKROCl9z" |                         "a2XMSKROCl9z" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.91.1-beta", |                     "title": "v0.91.1-beta", | ||||||
|                     "notePosition": 180, |                     "notePosition": 190, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -425,7 +451,7 @@ | |||||||
|                         "yqXFvWbLkuMD" |                         "yqXFvWbLkuMD" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.12", |                     "title": "v0.90.12", | ||||||
|                     "notePosition": 190, |                     "notePosition": 200, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -443,7 +469,7 @@ | |||||||
|                         "veS7pg311yJP" |                         "veS7pg311yJP" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.11-beta", |                     "title": "v0.90.11-beta", | ||||||
|                     "notePosition": 200, |                     "notePosition": 210, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -461,7 +487,7 @@ | |||||||
|                         "sq5W9TQxRqMq" |                         "sq5W9TQxRqMq" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.10-beta", |                     "title": "v0.90.10-beta", | ||||||
|                     "notePosition": 210, |                     "notePosition": 220, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -479,7 +505,7 @@ | |||||||
|                         "yFEGVCUM9tPx" |                         "yFEGVCUM9tPx" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.9-beta", |                     "title": "v0.90.9-beta", | ||||||
|                     "notePosition": 220, |                     "notePosition": 230, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -497,7 +523,7 @@ | |||||||
|                         "o4wAGqOQuJtV" |                         "o4wAGqOQuJtV" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.8", |                     "title": "v0.90.8", | ||||||
|                     "notePosition": 230, |                     "notePosition": 240, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -530,7 +556,7 @@ | |||||||
|                         "i4A5g9iOg9I0" |                         "i4A5g9iOg9I0" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.7-beta", |                     "title": "v0.90.7-beta", | ||||||
|                     "notePosition": 240, |                     "notePosition": 250, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -548,7 +574,7 @@ | |||||||
|                         "ThNf2GaKgXUs" |                         "ThNf2GaKgXUs" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.6-beta", |                     "title": "v0.90.6-beta", | ||||||
|                     "notePosition": 250, |                     "notePosition": 260, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -566,7 +592,7 @@ | |||||||
|                         "G4PAi554kQUr" |                         "G4PAi554kQUr" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.5-beta", |                     "title": "v0.90.5-beta", | ||||||
|                     "notePosition": 260, |                     "notePosition": 270, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -593,7 +619,7 @@ | |||||||
|                         "zATRobGRCmBn" |                         "zATRobGRCmBn" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.4", |                     "title": "v0.90.4", | ||||||
|                     "notePosition": 270, |                     "notePosition": 280, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -611,7 +637,7 @@ | |||||||
|                         "sCDLf8IKn3Iz" |                         "sCDLf8IKn3Iz" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.3", |                     "title": "v0.90.3", | ||||||
|                     "notePosition": 280, |                     "notePosition": 290, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -629,7 +655,7 @@ | |||||||
|                         "VqqyBu4AuTjC" |                         "VqqyBu4AuTjC" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.2-beta", |                     "title": "v0.90.2-beta", | ||||||
|                     "notePosition": 290, |                     "notePosition": 300, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -647,7 +673,7 @@ | |||||||
|                         "RX3Nl7wInLsA" |                         "RX3Nl7wInLsA" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.1-beta", |                     "title": "v0.90.1-beta", | ||||||
|                     "notePosition": 300, |                     "notePosition": 310, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -665,7 +691,7 @@ | |||||||
|                         "GyueACukPWjk" |                         "GyueACukPWjk" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "v0.90.0-beta", |                     "title": "v0.90.0-beta", | ||||||
|                     "notePosition": 310, |                     "notePosition": 320, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
| @@ -683,7 +709,7 @@ | |||||||
|                         "wyurrlcDl416" |                         "wyurrlcDl416" | ||||||
|                     ], |                     ], | ||||||
|                     "title": "Release Template", |                     "title": "Release Template", | ||||||
|                     "notePosition": 320, |                     "notePosition": 330, | ||||||
|                     "prefix": null, |                     "prefix": null, | ||||||
|                     "isExpanded": false, |                     "isExpanded": false, | ||||||
|                     "type": "text", |                     "type": "text", | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								docs/Release Notes/Release Notes/v0.94.1.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								docs/Release Notes/Release Notes/v0.94.1.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| # v0.94.1 | # v0.94.1 | ||||||
| > [!NOTE] | > [!NOTE] | ||||||
| > _Trilium Notes_ will rebrand itself back to Trilium Notes since @zadam was kind enough to give us the original name. See [#2190](https://github.com/orgs/TriliumNext/discussions/2190) for more info. This will probably be the "last" version branded as Trilium Notes_. | > _Trilium Notes_ will rebrand itself back to Trilium Notes since @zadam was kind enough to give us the original name. See [#2190](https://github.com/orgs/TriliumNext/discussions/2190) for more info. This will probably be the "last" version branded as Trilium Notes\_. | ||||||
|  |  | ||||||
| > [!IMPORTANT] | > [!IMPORTANT] | ||||||
| > If you enjoyed this release, consider showing a token of appreciation by: | > If you enjoyed this release, consider showing a token of appreciation by: | ||||||
|   | |||||||
							
								
								
									
										50
									
								
								docs/Release Notes/Release Notes/v0.96.0.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								docs/Release Notes/Release Notes/v0.96.0.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | # v0.96.0 | ||||||
|  | > [!NOTE] | ||||||
|  | > The Docker image has been relocated to `triliumnext/trilium`. Please update your configuration accordingly. | ||||||
|  |  | ||||||
|  | > [!IMPORTANT] | ||||||
|  | > If you enjoyed this release, consider showing a token of appreciation by: | ||||||
|  | >  | ||||||
|  | > *   Pressing the “Star” button on [GitHub](https://github.com/TriliumNext/Trilium) (top-right). | ||||||
|  | > *   Considering a one-time or recurrent donation to the [lead developer](https://github.com/eliandoran) via [GitHub Sponsors](https://github.com/sponsors/eliandoran) or [PayPal](https://paypal.me/eliandoran). | ||||||
|  |  | ||||||
|  | ## 💡 Key highlights | ||||||
|  |  | ||||||
|  | *   Thanks to a partnership with CKEditor, we now have a set of features that would otherwise be available on a commercial license only. | ||||||
|  |     *   Slash commands for easy commands via the keyboard. | ||||||
|  |     *   Text snippets to insert reusable pieces of text (similar to templates, but for blocks of text content). | ||||||
|  |     *   For more information, see the user guide → Note Types → Text → Premium features. | ||||||
|  |  | ||||||
|  | ## 🐞 Bugfixes | ||||||
|  |  | ||||||
|  | *   [“Insert note after” long-press dialog doesn’t create Note](https://github.com/TriliumNext/Notes/issues/2246) | ||||||
|  | *   Code notes: user's font selection not respected. | ||||||
|  | *   [Windows V0.95.0 Client Failed to sync with server (use of double-slashes)](https://github.com/TriliumNext/Notes/issues/2339) by @perfectra1n | ||||||
|  | *   Desktop client not working on older Linux distros | ||||||
|  | *   [NOT NULL constraint failed: revisions.title when saving an empty note](https://github.com/TriliumNext/Trilium/issues/6103) | ||||||
|  |  | ||||||
|  | ## ✨ Improvements | ||||||
|  |  | ||||||
|  | *   [Elixir language syntax highlighting for text notes](https://github.com/TriliumNext/Notes/pull/2327) (by @jshprentz) and code notes. | ||||||
|  | *   [Autocomplete: support specifying path when creating a new note](https://github.com/TriliumNext/Notes/pull/2342) by @SiriusXT | ||||||
|  | *   Markdown import: basic support for importing wikilinks | ||||||
|  | *   Text notes: | ||||||
|  |     *   Allow disabling emoji auto-completion from settings. | ||||||
|  |     *   Allow disabling note auto-completion from settings. | ||||||
|  | *   [Backend scripts: re-enable dayjs plugins by default](https://github.com/TriliumNext/Trilium/issues/6080) | ||||||
|  |  | ||||||
|  | ## 📖 Documentation | ||||||
|  |  | ||||||
|  | *   [regex search / Nix flake / restore dev docs](https://github.com/TriliumNext/Notes/pull/2341) by @FliegendeWurst | ||||||
|  | *   New premium features in text note type. | ||||||
|  |  | ||||||
|  | ## 🌍 Internationalization | ||||||
|  |  | ||||||
|  | *   Spanish improvements by @hasecilu | ||||||
|  |  | ||||||
|  | ## 🛠️ Technical updates | ||||||
|  |  | ||||||
|  | *   flake: fix Electron version, fix Wayland support, fix source filter by @FliegendeWurst | ||||||
|  | *   Improvements to the landing page (under development) by @FliegendeWurst | ||||||
|  | *   Updated Node.js to v22.17.0 | ||||||
|  | *   Updated CKEditor to v45.2.1 | ||||||
							
								
								
									
										112
									
								
								docs/User Guide/!!!meta.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										112
									
								
								docs/User Guide/!!!meta.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|     "formatVersion": 2, |     "formatVersion": 2, | ||||||
|     "appVersion": "0.95.0", |     "appVersion": "0.96.0", | ||||||
|     "files": [ |     "files": [ | ||||||
|         { |         { | ||||||
|             "isClone": false, |             "isClone": false, | ||||||
| @@ -3420,6 +3420,86 @@ | |||||||
|                                                     "dataFileName": "11_Calendar View_image.png" |                                                     "dataFileName": "11_Calendar View_image.png" | ||||||
|                                                 } |                                                 } | ||||||
|                                             ] |                                             ] | ||||||
|  |                                         }, | ||||||
|  |                                         { | ||||||
|  |                                             "isClone": false, | ||||||
|  |                                             "noteId": "2FvYrpmOXm29", | ||||||
|  |                                             "notePath": [ | ||||||
|  |                                                 "pOsGYCXsbNQG", | ||||||
|  |                                                 "gh7bpGYxajRS", | ||||||
|  |                                                 "BFs8mudNFgCS", | ||||||
|  |                                                 "0ESUbbAxVnoK", | ||||||
|  |                                                 "2FvYrpmOXm29" | ||||||
|  |                                             ], | ||||||
|  |                                             "title": "Table", | ||||||
|  |                                             "notePosition": 20, | ||||||
|  |                                             "prefix": null, | ||||||
|  |                                             "isExpanded": false, | ||||||
|  |                                             "type": "text", | ||||||
|  |                                             "mime": "text/html", | ||||||
|  |                                             "attributes": [ | ||||||
|  |                                                 { | ||||||
|  |                                                     "type": "label", | ||||||
|  |                                                     "name": "iconClass", | ||||||
|  |                                                     "value": "bx bx-table", | ||||||
|  |                                                     "isInheritable": false, | ||||||
|  |                                                     "position": 10 | ||||||
|  |                                                 }, | ||||||
|  |                                                 { | ||||||
|  |                                                     "type": "relation", | ||||||
|  |                                                     "name": "internalLink", | ||||||
|  |                                                     "value": "OFXdgB2nNk1F", | ||||||
|  |                                                     "isInheritable": false, | ||||||
|  |                                                     "position": 20 | ||||||
|  |                                                 }, | ||||||
|  |                                                 { | ||||||
|  |                                                     "type": "relation", | ||||||
|  |                                                     "name": "internalLink", | ||||||
|  |                                                     "value": "oPVyFC7WL2Lp", | ||||||
|  |                                                     "isInheritable": false, | ||||||
|  |                                                     "position": 30 | ||||||
|  |                                                 }, | ||||||
|  |                                                 { | ||||||
|  |                                                     "type": "relation", | ||||||
|  |                                                     "name": "internalLink", | ||||||
|  |                                                     "value": "m1lbrzyKDaRB", | ||||||
|  |                                                     "isInheritable": false, | ||||||
|  |                                                     "position": 40 | ||||||
|  |                                                 }, | ||||||
|  |                                                 { | ||||||
|  |                                                     "type": "relation", | ||||||
|  |                                                     "name": "internalLink", | ||||||
|  |                                                     "value": "CdNpE2pqjmI6", | ||||||
|  |                                                     "isInheritable": false, | ||||||
|  |                                                     "position": 50 | ||||||
|  |                                                 }, | ||||||
|  |                                                 { | ||||||
|  |                                                     "type": "relation", | ||||||
|  |                                                     "name": "internalLink", | ||||||
|  |                                                     "value": "m523cpzocqaD", | ||||||
|  |                                                     "isInheritable": false, | ||||||
|  |                                                     "position": 60 | ||||||
|  |                                                 }, | ||||||
|  |                                                 { | ||||||
|  |                                                     "type": "relation", | ||||||
|  |                                                     "name": "internalLink", | ||||||
|  |                                                     "value": "eIg8jdvaoNNd", | ||||||
|  |                                                     "isInheritable": false, | ||||||
|  |                                                     "position": 70 | ||||||
|  |                                                 } | ||||||
|  |                                             ], | ||||||
|  |                                             "format": "markdown", | ||||||
|  |                                             "dataFileName": "Table.md", | ||||||
|  |                                             "attachments": [ | ||||||
|  |                                                 { | ||||||
|  |                                                     "attachmentId": "vJYUG9fLQ2Pd", | ||||||
|  |                                                     "title": "image.png", | ||||||
|  |                                                     "role": "image", | ||||||
|  |                                                     "mime": "image/png", | ||||||
|  |                                                     "position": 10, | ||||||
|  |                                                     "dataFileName": "Table_image.png" | ||||||
|  |                                                 } | ||||||
|  |                                             ] | ||||||
|                                         } |                                         } | ||||||
|                                     ] |                                     ] | ||||||
|                                 } |                                 } | ||||||
| @@ -4350,6 +4430,13 @@ | |||||||
|                                     "type": "text", |                                     "type": "text", | ||||||
|                                     "mime": "text/markdown", |                                     "mime": "text/markdown", | ||||||
|                                     "attributes": [ |                                     "attributes": [ | ||||||
|  |                                         { | ||||||
|  |                                             "type": "relation", | ||||||
|  |                                             "name": "internalLink", | ||||||
|  |                                             "value": "rJ9grSgoExl9", | ||||||
|  |                                             "isInheritable": false, | ||||||
|  |                                             "position": 10 | ||||||
|  |                                         }, | ||||||
|                                         { |                                         { | ||||||
|                                             "type": "label", |                                             "type": "label", | ||||||
|                                             "name": "shareAlias", |                                             "name": "shareAlias", | ||||||
| @@ -4363,13 +4450,6 @@ | |||||||
|                                             "value": "bx bxl-markdown", |                                             "value": "bx bxl-markdown", | ||||||
|                                             "isInheritable": false, |                                             "isInheritable": false, | ||||||
|                                             "position": 50 |                                             "position": 50 | ||||||
|                                         }, |  | ||||||
|                                         { |  | ||||||
|                                             "type": "relation", |  | ||||||
|                                             "name": "internalLink", |  | ||||||
|                                             "value": "rJ9grSgoExl9", |  | ||||||
|                                             "isInheritable": false, |  | ||||||
|                                             "position": 60 |  | ||||||
|                                         } |                                         } | ||||||
|                                     ], |                                     ], | ||||||
|                                     "format": "markdown", |                                     "format": "markdown", | ||||||
| @@ -5113,23 +5193,23 @@ | |||||||
|                                         { |                                         { | ||||||
|                                             "type": "relation", |                                             "type": "relation", | ||||||
|                                             "name": "internalLink", |                                             "name": "internalLink", | ||||||
|                                             "value": "nRhnJkTT8cPs", |                                             "value": "rJ9grSgoExl9", | ||||||
|                                             "isInheritable": false, |                                             "isInheritable": false, | ||||||
|                                             "position": 10 |                                             "position": 10 | ||||||
|                                         }, |                                         }, | ||||||
|  |                                         { | ||||||
|  |                                             "type": "relation", | ||||||
|  |                                             "name": "internalLink", | ||||||
|  |                                             "value": "nRhnJkTT8cPs", | ||||||
|  |                                             "isInheritable": false, | ||||||
|  |                                             "position": 20 | ||||||
|  |                                         }, | ||||||
|                                         { |                                         { | ||||||
|                                             "type": "label", |                                             "type": "label", | ||||||
|                                             "name": "iconClass", |                                             "name": "iconClass", | ||||||
|                                             "value": "bx bx-info-circle", |                                             "value": "bx bx-info-circle", | ||||||
|                                             "isInheritable": false, |                                             "isInheritable": false, | ||||||
|                                             "position": 10 |                                             "position": 10 | ||||||
|                                         }, |  | ||||||
|                                         { |  | ||||||
|                                             "type": "relation", |  | ||||||
|                                             "name": "internalLink", |  | ||||||
|                                             "value": "rJ9grSgoExl9", |  | ||||||
|                                             "isInheritable": false, |  | ||||||
|                                             "position": 20 |  | ||||||
|                                         } |                                         } | ||||||
|                                     ], |                                     ], | ||||||
|                                     "format": "markdown", |                                     "format": "markdown", | ||||||
|   | |||||||
							
								
								
									
										83
									
								
								docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note List/Table.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note List/Table.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | # Table | ||||||
|  | <figure class="image"><img style="aspect-ratio:1050/259;" src="Table_image.png" width="1050" height="259"></figure> | ||||||
|  |  | ||||||
|  | The table view displays information in a grid, where the rows are individual notes and the columns are <a class="reference-link" href="../../../Advanced%20Usage/Attributes/Promoted%20Attributes.md">Promoted Attributes</a>. In addition, values are editable. | ||||||
|  |  | ||||||
|  | ## Interaction | ||||||
|  |  | ||||||
|  | ### Creating a new table | ||||||
|  |  | ||||||
|  | Right click the <a class="reference-link" href="../../UI%20Elements/Note%20Tree.md">Note Tree</a> and select _Insert child note_ and look for the _Table item_. | ||||||
|  |  | ||||||
|  | ### Adding columns | ||||||
|  |  | ||||||
|  | Each column is a [promoted attribute](../../../Advanced%20Usage/Attributes/Promoted%20Attributes.md) that is defined on the Book note. Ideally, the promoted attributes need to be inheritable in order to show up in the child notes. | ||||||
|  |  | ||||||
|  | To create a new column, simply press _Add new column_ at the bottom of the table. | ||||||
|  |  | ||||||
|  | There are also a few predefined columns: | ||||||
|  |  | ||||||
|  | *   The current item number, identified by the `#` symbol. This simply counts the note and is affected by sorting. | ||||||
|  | *   <a class="reference-link" href="../../../Advanced%20Usage/Note%20ID.md">Note ID</a>, representing the unique ID used internally by Trilium | ||||||
|  | *   The title of the note. | ||||||
|  |  | ||||||
|  | ### Adding new rows | ||||||
|  |  | ||||||
|  | Each row is actually a note that is a child of the book note. | ||||||
|  |  | ||||||
|  | To create a new note, press _Add new row_ at the bottom of the table. By default it will try to edit the title of the newly created note. | ||||||
|  |  | ||||||
|  | Alternatively, the note can be created from the<a class="reference-link" href="../../UI%20Elements/Note%20Tree.md">Note Tree</a> or [scripting](../../../Scripting.md). | ||||||
|  |  | ||||||
|  | ### Editing data | ||||||
|  |  | ||||||
|  | Simply click on a cell within a row to change its value. The change will not only reflect in the table, but also as an attribute of the corresponding note. | ||||||
|  |  | ||||||
|  | *   The editing will respect the type of the promoted attribute, by presenting a normal text box, a number selector or a date selector for example. | ||||||
|  | *   It also possible to change the title of a note. | ||||||
|  | *   Editing relations is also possible, by using the note autocomplete. | ||||||
|  |  | ||||||
|  | ## Working with the data | ||||||
|  |  | ||||||
|  | ### Sorting | ||||||
|  |  | ||||||
|  | It is possible to sort the data by the values of a column: | ||||||
|  |  | ||||||
|  | *   To do so, simply click on a column. | ||||||
|  | *   To switch between ascending or descending sort, simply click again on the same column. The arrow next to the column will indicate the direction of the sort. | ||||||
|  |  | ||||||
|  | ### Reordering and hiding columns | ||||||
|  |  | ||||||
|  | *   Columns can be reordered by dragging the header of the columns. | ||||||
|  | *   Columns can be hidden or shown by right clicking on a column and clicking the item corresponding to the column. | ||||||
|  |  | ||||||
|  | ### Reordering rows | ||||||
|  |  | ||||||
|  | Notes can be dragged around to change their order. This will also change the order of the note in the <a class="reference-link" href="../../UI%20Elements/Note%20Tree.md">Note Tree</a>. | ||||||
|  |  | ||||||
|  | Currently, it's possible to reorder notes even if sorting is used, but the result might be inconsistent. | ||||||
|  |  | ||||||
|  | ## Limitations | ||||||
|  |  | ||||||
|  | The table functionality is still in its early stages, as such it faces quite a few important limitations: | ||||||
|  |  | ||||||
|  | 1.  As mentioned previously, the columns of the table are defined as <a class="reference-link" href="../../../Advanced%20Usage/Attributes/Promoted%20Attributes.md">Promoted Attributes</a>. | ||||||
|  |     1.  But only the promoted attributes that are defined at the level of the Book note are actually taken into consideration. | ||||||
|  |     2.  There are plans to recursively look for columns across the sub-hierarchy. | ||||||
|  | 2.  Hierarchy is not yet supported, so the table will only show the items that are direct children of the _Book_ note. | ||||||
|  | 3.  Multiple labels and relations are not supported. If a <a class="reference-link" href="../../../Advanced%20Usage/Attributes/Promoted%20Attributes.md">Promoted Attributes</a> is defined with a _Multi value_ specificity, they will be ignored. | ||||||
|  |  | ||||||
|  | ## Use in search | ||||||
|  |  | ||||||
|  | The table view can be used in a <a class="reference-link" href="../../../Note%20Types/Saved%20Search.md">Saved Search</a> by adding the `#viewType=table` attribute. | ||||||
|  |  | ||||||
|  | Unlike when used in a book, saved searches are not limited to the sub-hierarchy of a note and allows for advanced queries thanks to the power of the <a class="reference-link" href="../../Navigation/Search.md">Search</a>. | ||||||
|  |  | ||||||
|  | However, there are also some limitations: | ||||||
|  |  | ||||||
|  | *   It's not possible to reorder notes. | ||||||
|  | *   It's not possible to add a new row. | ||||||
|  |  | ||||||
|  | Columns are supported, by being defined as <a class="reference-link" href="../../../Advanced%20Usage/Attributes/Promoted%20Attributes.md">Promoted Attributes</a> to the <a class="reference-link" href="../../../Note%20Types/Saved%20Search.md">Saved Search</a> note. | ||||||
|  |  | ||||||
|  | Editing is also supported. | ||||||
							
								
								
									
										
											BIN
										
									
								
								docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note List/Table_image.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/User Guide/User Guide/Basic Concepts and Features/Notes/Note List/Table_image.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 34 KiB | 
| @@ -3,4 +3,6 @@ | |||||||
|  |  | ||||||
| A Note map is a note type which displays a standalone version of the feature of the same name: [Note Map (Link map, Tree map)](../Advanced%20Usage/Note%20Map%20\(Link%20map%2C%20Tree%20map\).md). | A Note map is a note type which displays a standalone version of the feature of the same name: [Note Map (Link map, Tree map)](../Advanced%20Usage/Note%20Map%20\(Link%20map%2C%20Tree%20map\).md). | ||||||
|  |  | ||||||
| Once created, the note map will display the relations between notes. Only the notes that are part of the parent of the note map will be displayed (including their children). | Once created, the note map will display the relations between notes. Only the notes that are part of the parent of the note map will be displayed (including their children). | ||||||
|  |  | ||||||
|  | The labels `mapIncludeRelation` and `mapExcludeRelation`, if set, filter the note map to include only the specified relations or to exclude the specified relations, respectively. | ||||||
							
								
								
									
										32
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@triliumnext/source", |   "name": "@triliumnext/source", | ||||||
|   "version": "0.95.0", |   "version": "0.96.0", | ||||||
|   "description": "Build your personal knowledge base with Trilium Notes", |   "description": "Build your personal knowledge base with Trilium Notes", | ||||||
|   "directories": { |   "directories": { | ||||||
|     "doc": "docs" |     "doc": "docs" | ||||||
| @@ -27,20 +27,20 @@ | |||||||
|   "private": true, |   "private": true, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@electron/rebuild": "4.0.1", |     "@electron/rebuild": "4.0.1", | ||||||
|     "@nx/devkit": "21.2.1", |     "@nx/devkit": "21.2.2", | ||||||
|     "@nx/esbuild": "21.2.1", |     "@nx/esbuild": "21.2.2", | ||||||
|     "@nx/eslint": "21.2.1", |     "@nx/eslint": "21.2.2", | ||||||
|     "@nx/eslint-plugin": "21.2.1", |     "@nx/eslint-plugin": "21.2.2", | ||||||
|     "@nx/express": "21.2.1", |     "@nx/express": "21.2.2", | ||||||
|     "@nx/js": "21.2.1", |     "@nx/js": "21.2.2", | ||||||
|     "@nx/node": "21.2.1", |     "@nx/node": "21.2.2", | ||||||
|     "@nx/playwright": "21.2.1", |     "@nx/playwright": "21.2.2", | ||||||
|     "@nx/vite": "21.2.1", |     "@nx/vite": "21.2.2", | ||||||
|     "@nx/web": "21.2.1", |     "@nx/web": "21.2.2", | ||||||
|     "@playwright/test": "^1.36.0", |     "@playwright/test": "^1.36.0", | ||||||
|     "@triliumnext/server": "workspace:*", |     "@triliumnext/server": "workspace:*", | ||||||
|     "@types/express": "^5.0.0", |     "@types/express": "^5.0.0", | ||||||
|     "@types/node": "22.15.33", |     "@types/node": "22.16.0", | ||||||
|     "@vitest/coverage-v8": "^3.0.5", |     "@vitest/coverage-v8": "^3.0.5", | ||||||
|     "@vitest/ui": "^3.0.0", |     "@vitest/ui": "^3.0.0", | ||||||
|     "chalk": "5.4.1", |     "chalk": "5.4.1", | ||||||
| @@ -54,15 +54,15 @@ | |||||||
|     "jiti": "2.4.2", |     "jiti": "2.4.2", | ||||||
|     "jsdom": "~26.1.0", |     "jsdom": "~26.1.0", | ||||||
|     "jsonc-eslint-parser": "^2.1.0", |     "jsonc-eslint-parser": "^2.1.0", | ||||||
|     "nx": "21.2.1", |     "nx": "21.2.2", | ||||||
|     "react-refresh": "^0.17.0", |     "react-refresh": "^0.17.0", | ||||||
|     "rollup-plugin-webpack-stats": "2.0.7", |     "rollup-plugin-webpack-stats": "2.1.0", | ||||||
|     "tslib": "^2.3.0", |     "tslib": "^2.3.0", | ||||||
|     "tsx": "4.20.3", |     "tsx": "4.20.3", | ||||||
|     "typescript": "~5.8.0", |     "typescript": "~5.8.0", | ||||||
|     "typescript-eslint": "^8.19.0", |     "typescript-eslint": "^8.19.0", | ||||||
|     "upath": "2.0.1", |     "upath": "2.0.1", | ||||||
|     "vite": "^6.0.0", |     "vite": "^7.0.0", | ||||||
|     "vite-plugin-dts": "~4.5.0", |     "vite-plugin-dts": "~4.5.0", | ||||||
|     "vitest": "^3.0.0" |     "vitest": "^3.0.0" | ||||||
|   }, |   }, | ||||||
| @@ -89,7 +89,7 @@ | |||||||
|       "@nx/js": "patches/@nx__js.patch" |       "@nx/js": "patches/@nx__js.patch" | ||||||
|     }, |     }, | ||||||
|     "overrides": { |     "overrides": { | ||||||
|       "mermaid": "11.7.0", |       "mermaid": "11.8.0", | ||||||
|       "preact": "10.26.9", |       "preact": "10.26.9", | ||||||
|       "roughjs": "4.6.6", |       "roughjs": "4.6.6", | ||||||
|       "@types/express-serve-static-core": "5.0.6", |       "@types/express-serve-static-core": "5.0.6", | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ | |||||||
|     "@codemirror/lang-xml": "6.1.0", |     "@codemirror/lang-xml": "6.1.0", | ||||||
|     "@codemirror/legacy-modes": "6.5.1", |     "@codemirror/legacy-modes": "6.5.1", | ||||||
|     "@codemirror/search": "6.5.11", |     "@codemirror/search": "6.5.11", | ||||||
|     "@codemirror/view": "6.37.2", |     "@codemirror/view": "6.38.0", | ||||||
|     "@fsegurai/codemirror-theme-abcdef": "6.2.0", |     "@fsegurai/codemirror-theme-abcdef": "6.2.0", | ||||||
|     "@fsegurai/codemirror-theme-abyss": "6.2.0", |     "@fsegurai/codemirror-theme-abyss": "6.2.0", | ||||||
|     "@fsegurai/codemirror-theme-android-studio": "6.2.0", |     "@fsegurai/codemirror-theme-android-studio": "6.2.0", | ||||||
| @@ -62,6 +62,6 @@ | |||||||
|     "codemirror-lang-elixir": "4.0.0", |     "codemirror-lang-elixir": "4.0.0", | ||||||
|     "codemirror-lang-hcl": "0.1.0", |     "codemirror-lang-hcl": "0.1.0", | ||||||
|     "codemirror-lang-mermaid": "0.5.0", |     "codemirror-lang-mermaid": "0.5.0", | ||||||
|     "eslint-linter-browserify": "9.29.0" |     "eslint-linter-browserify": "9.30.1" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "@triliumnext/commons", |   "name": "@triliumnext/commons", | ||||||
|   "version": "0.95.0", |   "version": "0.96.0", | ||||||
|   "description": "Shared library between the clients (e.g. browser, Electron) and the server, mostly for type definitions and utility methods.", |   "description": "Shared library between the clients (e.g. browser, Electron) and the server, mostly for type definitions and utility methods.", | ||||||
|   "private": true, |   "private": true, | ||||||
|   "type": "module", |   "type": "module", | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ | |||||||
|     "@types/swagger-ui": "^5.0.0", |     "@types/swagger-ui": "^5.0.0", | ||||||
|     "@typescript-eslint/eslint-plugin": "^8.0.0", |     "@typescript-eslint/eslint-plugin": "^8.0.0", | ||||||
|     "@typescript-eslint/parser": "^8.0.0", |     "@typescript-eslint/parser": "^8.0.0", | ||||||
|     "dotenv": "^16.3.1", |     "dotenv": "^17.0.0", | ||||||
|     "esbuild": "^0.25.0", |     "esbuild": "^0.25.0", | ||||||
|     "eslint": "^9.0.0", |     "eslint": "^9.0.0", | ||||||
|     "highlight.js": "^11.8.0", |     "highlight.js": "^11.8.0", | ||||||
|   | |||||||
							
								
								
									
										3776
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3776
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user