mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	getting rid of attributes like data-note-path in favor of the whole nav state in URLs
This commit is contained in:
		| @@ -9,6 +9,7 @@ import TabManager from "./tab_manager.js"; | |||||||
| import treeService from "../services/tree.js"; | import treeService from "../services/tree.js"; | ||||||
| import Component from "./component.js"; | import Component from "./component.js"; | ||||||
| import keyboardActionsService from "../services/keyboard_actions.js"; | import keyboardActionsService from "../services/keyboard_actions.js"; | ||||||
|  | import linkService from "../services/link.js"; | ||||||
| import MobileScreenSwitcherExecutor from "./mobile_screen_switcher.js"; | import MobileScreenSwitcherExecutor from "./mobile_screen_switcher.js"; | ||||||
| import MainTreeExecutors from "./main_tree_executors.js"; | import MainTreeExecutors from "./main_tree_executors.js"; | ||||||
| import toast from "../services/toast.js"; | import toast from "../services/toast.js"; | ||||||
| @@ -158,14 +159,9 @@ $(window).on('beforeunload', () => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| $(window).on('hashchange', function() { | $(window).on('hashchange', function() { | ||||||
|     if (treeService.isNotePathInAddress()) { |     const {notePath, ntxId, viewScope} = linkService.parseNavigationStateFromUrl(window.location.href); | ||||||
|         const {notePath, ntxId, viewScope} = treeService.parseNavigationStateFromAddress(); |  | ||||||
|  |  | ||||||
|         if (!notePath && !ntxId) { |  | ||||||
|             console.log(`Invalid hash value "${document.location.hash}", ignoring.`); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|  |     if (notePath || ntxId) { | ||||||
|         appContext.tabManager.switchToNoteContext(ntxId, notePath, viewScope); |         appContext.tabManager.switchToNoteContext(ntxId, notePath, viewScope); | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -52,14 +52,13 @@ export default class TabManager extends Component { | |||||||
|  |  | ||||||
|     async loadTabs() { |     async loadTabs() { | ||||||
|         try { |         try { | ||||||
|             const noteContextsToOpen = appContext.isMainWindow |             const noteContextsToOpen = (appContext.isMainWindow && options.getJson('openNoteContexts')) || []; | ||||||
|                 ? (options.getJson('openNoteContexts') || []) |  | ||||||
|                 : []; |  | ||||||
|  |  | ||||||
|             // preload all notes at once |             // preload all notes at once | ||||||
|             await froca.getNotes([ |             await froca.getNotes([ | ||||||
|                     ...noteContextsToOpen.map(tab => treeService.getNoteIdFromNotePath(tab.notePath)), |                     ...noteContextsToOpen.flatMap(tab => | ||||||
|                     ...noteContextsToOpen.map(tab => tab.hoistedNoteId), |                         [ treeService.getNoteIdFromNotePath(tab.notePath), tab.hoistedNoteId] | ||||||
|  |                     ), | ||||||
|             ], true); |             ], true); | ||||||
|  |  | ||||||
|             const filteredNoteContexts = noteContextsToOpen.filter(openTab => { |             const filteredNoteContexts = noteContextsToOpen.filter(openTab => { | ||||||
| @@ -81,7 +80,7 @@ export default class TabManager extends Component { | |||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             // resolve before opened tabs can change this |             // resolve before opened tabs can change this | ||||||
|             const parsedFromUrl = treeService.parseNavigationStateFromAddress(); |             const parsedFromUrl = linkService.parseNavigationStateFromUrl(window.location.href); | ||||||
|  |  | ||||||
|             if (filteredNoteContexts.length === 0) { |             if (filteredNoteContexts.length === 0) { | ||||||
|                 parsedFromUrl.ntxId = parsedFromUrl.ntxId || NoteContext.generateNtxId(); // generate already here, so that we later know which one to activate |                 parsedFromUrl.ntxId = parsedFromUrl.ntxId || NoteContext.generateNtxId(); // generate already here, so that we later know which one to activate | ||||||
| @@ -109,8 +108,8 @@ export default class TabManager extends Component { | |||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             // if there's notePath in the URL, make sure it's open and active |             // if there's a notePath in the URL, make sure it's open and active | ||||||
|             // (useful, for e.g. opening clipped notes from clipper or opening link in an extra window) |             // (useful, for e.g., opening clipped notes from clipper or opening link in an extra window) | ||||||
|             if (parsedFromUrl.notePath) { |             if (parsedFromUrl.notePath) { | ||||||
|                 await appContext.tabManager.switchToNoteContext( |                 await appContext.tabManager.switchToNoteContext( | ||||||
|                     parsedFromUrl.ntxId, |                     parsedFromUrl.ntxId, | ||||||
|   | |||||||
| @@ -56,8 +56,7 @@ async function createNoteLink(noteId) { | |||||||
|  |  | ||||||
|     return $("<a>", { |     return $("<a>", { | ||||||
|         href: `#root/${noteId}`, |         href: `#root/${noteId}`, | ||||||
|         class: 'reference-link', |         class: 'reference-link' | ||||||
|         'data-note-path': noteId |  | ||||||
|     }) |     }) | ||||||
|         .text(note.title); |         .text(note.title); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,21 +19,17 @@ async function createNoteLink(notePath, options = {}) { | |||||||
|  |  | ||||||
|     if (!notePath.startsWith("root")) { |     if (!notePath.startsWith("root")) { | ||||||
|         // all note paths should start with "root/" (except for "root" itself) |         // all note paths should start with "root/" (except for "root" itself) | ||||||
|         // used e.g., to find internal links |         // used, e.g., to find internal links | ||||||
|         notePath = `root/${notePath}`; |         notePath = `root/${notePath}`; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let noteTitle = options.title; |  | ||||||
|     const showTooltip = options.showTooltip === undefined ? true : options.showTooltip; |     const showTooltip = options.showTooltip === undefined ? true : options.showTooltip; | ||||||
|     const showNotePath = options.showNotePath === undefined ? false : options.showNotePath; |     const showNotePath = options.showNotePath === undefined ? false : options.showNotePath; | ||||||
|     const showNoteIcon = options.showNoteIcon === undefined ? false : options.showNoteIcon; |     const showNoteIcon = options.showNoteIcon === undefined ? false : options.showNoteIcon; | ||||||
|     const referenceLink = options.referenceLink === undefined ? false : options.referenceLink; |     const referenceLink = options.referenceLink === undefined ? false : options.referenceLink; | ||||||
|  |  | ||||||
|     const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath); |     const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromNotePath(notePath); | ||||||
|  |     const noteTitle = options.title || await treeService.getNoteTitle(noteId, parentNoteId); | ||||||
|     if (!noteTitle) { |  | ||||||
|         noteTitle = await treeService.getNoteTitle(noteId, parentNoteId); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const $container = $("<span>"); |     const $container = $("<span>"); | ||||||
|  |  | ||||||
| @@ -45,11 +41,15 @@ async function createNoteLink(notePath, options = {}) { | |||||||
|             .append(" "); |             .append(" "); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     const hash = calculateHash({ | ||||||
|  |         notePath, | ||||||
|  |         viewScope: options.viewScope | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     const $noteLink = $("<a>", { |     const $noteLink = $("<a>", { | ||||||
|         href: `#${notePath}`, |         href: hash, | ||||||
|         text: noteTitle |         text: noteTitle | ||||||
|     }).attr('data-action', 'note') |     }); | ||||||
|         .attr('data-note-path', notePath); |  | ||||||
|  |  | ||||||
|     if (!showTooltip) { |     if (!showTooltip) { | ||||||
|         $noteLink.addClass("no-tooltip-preview"); |         $noteLink.addClass("no-tooltip-preview"); | ||||||
| @@ -78,27 +78,6 @@ async function createNoteLink(notePath, options = {}) { | |||||||
|     return $container; |     return $container; | ||||||
| } | } | ||||||
|  |  | ||||||
| function parseNotePathAndScope($link) { |  | ||||||
|     let notePath = $link.attr("data-note-path"); |  | ||||||
|  |  | ||||||
|     if (!notePath) { |  | ||||||
|         const url = $link.attr('href'); |  | ||||||
|  |  | ||||||
|         notePath = url ? getNotePathFromUrl(url) : null; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const viewScope = { |  | ||||||
|         viewMode: $link.attr('data-view-mode') || 'default', |  | ||||||
|         attachmentId: $link.attr('data-attachment-id'), |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return { |  | ||||||
|         notePath, |  | ||||||
|         noteId: treeService.getNoteIdFromNotePath(notePath), |  | ||||||
|         viewScope |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function calculateHash({notePath, ntxId, hoistedNoteId, viewScope = {}}) { | function calculateHash({notePath, ntxId, hoistedNoteId, viewScope = {}}) { | ||||||
|     notePath = notePath || ""; |     notePath = notePath || ""; | ||||||
|     const params = [ |     const params = [ | ||||||
| @@ -128,9 +107,50 @@ function calculateHash({notePath, ntxId, hoistedNoteId, viewScope = {}}) { | |||||||
|     return hash; |     return hash; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function parseNavigationStateFromUrl(url) { | ||||||
|  |     const hashIdx = url?.indexOf('#'); | ||||||
|  |     if (hashIdx === -1) { | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const hash = url?.substr(hashIdx + 1); // strip also the initial '#' | ||||||
|  |     const [notePath, paramString] = hash.split("?"); | ||||||
|  |     const viewScope = { | ||||||
|  |         viewMode: 'default' | ||||||
|  |     }; | ||||||
|  |     let ntxId = null; | ||||||
|  |     let hoistedNoteId = null; | ||||||
|  |  | ||||||
|  |     if (paramString) { | ||||||
|  |         for (const pair of paramString.split("&")) { | ||||||
|  |             let [name, value] = pair.split("="); | ||||||
|  |             name = decodeURIComponent(name); | ||||||
|  |             value = decodeURIComponent(value); | ||||||
|  |  | ||||||
|  |             if (name === 'ntxId') { | ||||||
|  |                 ntxId = value; | ||||||
|  |             } else if (name === 'hoistedNoteId') { | ||||||
|  |                 hoistedNoteId = value; | ||||||
|  |             } else if (['viewMode', 'attachmentId'].includes(name)) { | ||||||
|  |                 viewScope[name] = value; | ||||||
|  |             } else { | ||||||
|  |                 console.warn(`Unrecognized hash parameter '${name}'.`); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         notePath, | ||||||
|  |         noteId: treeService.getNoteIdFromNotePath(notePath), | ||||||
|  |         ntxId, | ||||||
|  |         hoistedNoteId, | ||||||
|  |         viewScope | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
| function goToLink(evt) { | function goToLink(evt) { | ||||||
|     const $link = $(evt.target).closest("a,.block-link"); |     const $link = $(evt.target).closest("a,.block-link"); | ||||||
|     const hrefLink = $link.attr('href'); |     const hrefLink = $link.attr('href') || $link.attr('data-href'); | ||||||
|  |  | ||||||
|     if (hrefLink?.startsWith("data:")) { |     if (hrefLink?.startsWith("data:")) { | ||||||
|         return true; |         return true; | ||||||
| @@ -139,7 +159,7 @@ function goToLink(evt) { | |||||||
|     evt.preventDefault(); |     evt.preventDefault(); | ||||||
|     evt.stopPropagation(); |     evt.stopPropagation(); | ||||||
|  |  | ||||||
|     const { notePath, viewScope } = parseNotePathAndScope($link); |     const { notePath, viewScope } = parseNavigationStateFromUrl(hrefLink); | ||||||
|  |  | ||||||
|     const ctrlKey = utils.isCtrlKey(evt); |     const ctrlKey = utils.isCtrlKey(evt); | ||||||
|     const isLeftClick = evt.which === 1; |     const isLeftClick = evt.which === 1; | ||||||
| @@ -186,8 +206,9 @@ function goToLink(evt) { | |||||||
|  |  | ||||||
| function linkContextMenu(e) { | function linkContextMenu(e) { | ||||||
|     const $link = $(e.target).closest("a"); |     const $link = $(e.target).closest("a"); | ||||||
|  |     const url = $link.attr("href") || $link.attr("data-href"); | ||||||
|  |  | ||||||
|     const { notePath, viewScope } = parseNotePathAndScope($link); |     const { notePath, viewScope } = parseNavigationStateFromUrl(url); | ||||||
|  |  | ||||||
|     if (!notePath) { |     if (!notePath) { | ||||||
|         return; |         return; | ||||||
| @@ -252,6 +273,6 @@ export default { | |||||||
|     createNoteLink, |     createNoteLink, | ||||||
|     goToLink, |     goToLink, | ||||||
|     loadReferenceLinkTitle, |     loadReferenceLinkTitle, | ||||||
|     parseNotePathAndScope, |     calculateHash, | ||||||
|     calculateHash |     parseNavigationStateFromUrl | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -114,8 +114,7 @@ function initNoteAutocomplete($el, options) { | |||||||
|             .prop("title", "Show recent notes"); |             .prop("title", "Show recent notes"); | ||||||
|  |  | ||||||
|     const $goToSelectedNoteButton = $("<a>") |     const $goToSelectedNoteButton = $("<a>") | ||||||
|         .addClass("input-group-text go-to-selected-note-button bx bx-arrow-to-right") |         .addClass("input-group-text go-to-selected-note-button bx bx-arrow-to-right"); | ||||||
|         .attr("data-action", "note"); |  | ||||||
|  |  | ||||||
|     const $sideButtons = $("<div>") |     const $sideButtons = $("<div>") | ||||||
|         .addClass("input-group-append") |         .addClass("input-group-append") | ||||||
|   | |||||||
| @@ -54,9 +54,9 @@ async function getRenderedContent(note, options = {}) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else if (type === 'code') { |     else if (type === 'code') { | ||||||
|         const fullNote = await server.get(`notes/${note.noteId}`); |         const blob = await note.getBlob({ preview: options.trim }); | ||||||
|  |  | ||||||
|         $renderedContent.append($("<pre>").text(trim(fullNote.content, options.trim))); |         $renderedContent.append($("<pre>").text(trim(blob.content, options.trim))); | ||||||
|     } |     } | ||||||
|     else if (type === 'image') { |     else if (type === 'image') { | ||||||
|         const sanitizedTitle = note.title.replace(/[^a-z0-9-.]/gi, ""); |         const sanitizedTitle = note.title.replace(/[^a-z0-9-.]/gi, ""); | ||||||
|   | |||||||
| @@ -268,7 +268,7 @@ class NoteListRenderer { | |||||||
|  |  | ||||||
|         const {$renderedAttributes} = await attributeRenderer.renderNormalAttributes(note); |         const {$renderedAttributes} = await attributeRenderer.renderNormalAttributes(note); | ||||||
|         const notePath = this.parentNote.type === 'search' |         const notePath = this.parentNote.type === 'search' | ||||||
|             ? note.noteId // for search note parent we want to display non-search path |             ? note.noteId // for search note parent, we want to display a non-search path | ||||||
|             : `${this.parentNote.noteId}/${note.noteId}`; |             : `${this.parentNote.noteId}/${note.noteId}`; | ||||||
|  |  | ||||||
|         const $card = $('<div class="note-book-card">') |         const $card = $('<div class="note-book-card">') | ||||||
| @@ -288,7 +288,7 @@ class NoteListRenderer { | |||||||
|         if (this.viewType === 'grid') { |         if (this.viewType === 'grid') { | ||||||
|             $card |             $card | ||||||
|                 .addClass("block-link") |                 .addClass("block-link") | ||||||
|                 .attr("data-note-path", notePath) |                 .attr("data-href", `#${notePath}`) | ||||||
|                 .on('click', e => linkService.goToLink(e)); |                 .on('click', e => linkService.goToLink(e)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -32,7 +32,8 @@ async function mouseEnterHandler() { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const { notePath, noteId, viewScope } = linkService.parseNotePathAndScope($link); |     const url = $link.attr("href") || $link.attr("data-href"); | ||||||
|  |     const { notePath, noteId, viewScope } = linkService.parseNavigationStateFromUrl(url); | ||||||
|  |  | ||||||
|     if (!notePath || viewScope.viewMode !== 'default') { |     if (!notePath || viewScope.viewMode !== 'default') { | ||||||
|         return; |         return; | ||||||
|   | |||||||
| @@ -279,50 +279,6 @@ async function getNoteTitleWithPathAsSuffix(notePath) { | |||||||
|     return $titleWithPath; |     return $titleWithPath; | ||||||
| } | } | ||||||
|  |  | ||||||
| function parseNavigationStateFromAddress() { |  | ||||||
|     const str = document.location.hash?.substr(1) || ""; // strip initial # |  | ||||||
|  |  | ||||||
|     const [notePath, paramString] = str.split("?"); |  | ||||||
|     const viewScope = { |  | ||||||
|         viewMode: 'default' |  | ||||||
|     }; |  | ||||||
|     let ntxId = null; |  | ||||||
|     let hoistedNoteId = null; |  | ||||||
|  |  | ||||||
|     if (paramString) { |  | ||||||
|         for (const pair of paramString.split("&")) { |  | ||||||
|             let [name, value] = pair.split("="); |  | ||||||
|             name = decodeURIComponent(name); |  | ||||||
|             value = decodeURIComponent(value); |  | ||||||
|  |  | ||||||
|             if (name === 'ntxId') { |  | ||||||
|                 ntxId = value; |  | ||||||
|             } else if (name === 'hoistedNoteId') { |  | ||||||
|                 hoistedNoteId = value; |  | ||||||
|             } else if (['viewMode', 'attachmentId'].includes(name)) { |  | ||||||
|                 viewScope[name] = value; |  | ||||||
|             } else { |  | ||||||
|                 console.warn(`Unrecognized hash parameter '${name}'.`); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return { |  | ||||||
|         notePath, |  | ||||||
|         ntxId, |  | ||||||
|         hoistedNoteId, |  | ||||||
|         viewScope |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function isNotePathInAddress() { |  | ||||||
|     const {notePath, ntxId} = parseNavigationStateFromAddress(); |  | ||||||
|  |  | ||||||
|     return notePath.startsWith("root") |  | ||||||
|         // empty string is for empty/uninitialized tab |  | ||||||
|         || (notePath === '' && !!ntxId); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function isNotePathInHiddenSubtree(notePath) { | function isNotePathInHiddenSubtree(notePath) { | ||||||
|     return notePath?.includes("root/_hidden"); |     return notePath?.includes("root/_hidden"); | ||||||
| } | } | ||||||
| @@ -338,7 +294,5 @@ export default { | |||||||
|     getNoteTitle, |     getNoteTitle, | ||||||
|     getNotePathTitle, |     getNotePathTitle, | ||||||
|     getNoteTitleWithPathAsSuffix, |     getNoteTitleWithPathAsSuffix, | ||||||
|     parseNavigationStateFromAddress, |  | ||||||
|     isNotePathInAddress, |  | ||||||
|     isNotePathInHiddenSubtree |     isNotePathInHiddenSubtree | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import BasicWidget from "./basic_widget.js"; | |||||||
| import server from "../services/server.js"; | import server from "../services/server.js"; | ||||||
| import options from "../services/options.js"; | import options from "../services/options.js"; | ||||||
| import imageService from "../services/image.js"; | import imageService from "../services/image.js"; | ||||||
|  | import linkService from "../services/link.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="attachment-detail"> | <div class="attachment-detail"> | ||||||
| @@ -15,6 +16,7 @@ const TPL = ` | |||||||
|         .attachment-title-line { |         .attachment-title-line { | ||||||
|             display: flex; |             display: flex; | ||||||
|             align-items: baseline; |             align-items: baseline; | ||||||
|  |             gap: 1em; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         .attachment-details { |         .attachment-details { | ||||||
| @@ -54,10 +56,10 @@ const TPL = ` | |||||||
|  |  | ||||||
|     <div class="attachment-detail-wrapper"> |     <div class="attachment-detail-wrapper"> | ||||||
|         <div class="attachment-title-line"> |         <div class="attachment-title-line"> | ||||||
|  |             <div class="attachment-actions-container"></div> | ||||||
|             <h4 class="attachment-title"></h4>                 |             <h4 class="attachment-title"></h4>                 | ||||||
|             <div class="attachment-details"></div> |             <div class="attachment-details"></div> | ||||||
|             <div style="flex: 1 1;"></div> |             <div style="flex: 1 1;"></div> | ||||||
|             <div class="attachment-actions-container"></div> |  | ||||||
|         </div> |         </div> | ||||||
|          |          | ||||||
|         <div class="attachment-deletion-warning alert alert-info"></div> |         <div class="attachment-deletion-warning alert alert-info"></div> | ||||||
| @@ -84,7 +86,7 @@ export default class AttachmentDetailWidget extends BasicWidget { | |||||||
|         super.doRender(); |         super.doRender(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     refresh() { |     async refresh() { | ||||||
|         this.$widget.find('.attachment-detail-wrapper') |         this.$widget.find('.attachment-detail-wrapper') | ||||||
|             .empty() |             .empty() | ||||||
|             .append( |             .append( | ||||||
| @@ -97,11 +99,13 @@ export default class AttachmentDetailWidget extends BasicWidget { | |||||||
|  |  | ||||||
|         if (!this.isFullDetail) { |         if (!this.isFullDetail) { | ||||||
|             this.$wrapper.find('.attachment-title').append( |             this.$wrapper.find('.attachment-title').append( | ||||||
|                 $('<a href="javascript:">') |                 await linkService.createNoteLink(this.attachment.parentId, { | ||||||
|                     .attr("data-note-path", this.attachment.parentId) |                     title: this.attachment.title, | ||||||
|                     .attr("data-view-mode", "attachments") |                     viewScope: { | ||||||
|                     .attr("data-attachment-id", this.attachment.attachmentId) |                         viewMode: 'attachments', | ||||||
|                     .text(this.attachment.title) |                         attachmentId: this.attachment.attachmentId | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|             ); |             ); | ||||||
|         } else { |         } else { | ||||||
|             this.$wrapper.find('.attachment-title') |             this.$wrapper.find('.attachment-title') | ||||||
|   | |||||||
| @@ -701,9 +701,8 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget { | |||||||
|  |  | ||||||
|     createNoteLink(noteId) { |     createNoteLink(noteId) { | ||||||
|         return $("<a>", { |         return $("<a>", { | ||||||
|             href: `#${noteId}`, |             href: `#root/${noteId}`, | ||||||
|             class: 'reference-link', |             class: 'reference-link' | ||||||
|             'data-note-path': noteId |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -105,7 +105,7 @@ export default class CalendarWidget extends RightDropdownButtonWidget { | |||||||
|  |  | ||||||
|         if (dateNoteId) { |         if (dateNoteId) { | ||||||
|             $newDay.addClass('calendar-date-exists'); |             $newDay.addClass('calendar-date-exists'); | ||||||
|             $newDay.attr("data-note-path", dateNoteId); |             $newDay.attr("href", `#root/dateNoteId`); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (this.isEqual(this.date, this.activeDate)) { |         if (this.isEqual(this.date, this.activeDate)) { | ||||||
|   | |||||||
| @@ -55,7 +55,7 @@ export default class HistoryNavigationButton extends ButtonFromNoteWidget { | |||||||
|         for (const idx in this.webContents.history) { |         for (const idx in this.webContents.history) { | ||||||
|             const url = this.webContents.history[idx]; |             const url = this.webContents.history[idx]; | ||||||
|             const [_, notePathWithTab] = url.split('#'); |             const [_, notePathWithTab] = url.split('#'); | ||||||
|             // broken: use treeService.parseNavigationStateFromAddress(); |             // broken: use linkService.parseNavigationStateFromUrl(); | ||||||
|             const [notePath, ntxId] = notePathWithTab.split('-'); |             const [notePath, ntxId] = notePathWithTab.split('-'); | ||||||
|  |  | ||||||
|             const title = await treeService.getNotePathTitle(notePath); |             const title = await treeService.getNotePathTitle(notePath); | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import TypeWidget from "./type_widget.js"; | import TypeWidget from "./type_widget.js"; | ||||||
| import server from "../../services/server.js"; | import server from "../../services/server.js"; | ||||||
| import AttachmentDetailWidget from "../attachment_detail.js"; | import AttachmentDetailWidget from "../attachment_detail.js"; | ||||||
|  | import linkService from "../../services/link.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="attachment-detail note-detail-printable"> | <div class="attachment-detail note-detail-printable"> | ||||||
| @@ -10,6 +11,8 @@ const TPL = ` | |||||||
|         } |         } | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|  |     <div class="links-wrapper"></div> | ||||||
|  |  | ||||||
|     <div class="attachment-wrapper"></div> |     <div class="attachment-wrapper"></div> | ||||||
| </div>`; | </div>`; | ||||||
|  |  | ||||||
| @@ -29,6 +32,8 @@ export default class AttachmentDetailTypeWidget extends TypeWidget { | |||||||
|         this.$wrapper.empty(); |         this.$wrapper.empty(); | ||||||
|         this.children = []; |         this.children = []; | ||||||
|  |  | ||||||
|  |         linkService.createNoteLink(this.noteId, {}); | ||||||
|  |  | ||||||
|         const attachment = await server.get(`attachments/${this.attachmentId}/?includeContent=true`); |         const attachment = await server.get(`attachments/${this.attachmentId}/?includeContent=true`); | ||||||
|  |  | ||||||
|         if (!attachment) { |         if (!attachment) { | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ function sanitize(dirtyHtml) { | |||||||
|             'en-media' // for ENEX import |             'en-media' // for ENEX import | ||||||
|         ], |         ], | ||||||
|         allowedAttributes: { |         allowedAttributes: { | ||||||
|             'a': [ 'href', 'class', 'data-note-path' ], |             'a': [ 'href', 'class' ], | ||||||
|             'img': [ 'src' ], |             'img': [ 'src' ], | ||||||
|             'section': [ 'class', 'data-note-id' ], |             'section': [ 'class', 'data-note-id' ], | ||||||
|             'figure': [ 'class' ], |             'figure': [ 'class' ], | ||||||
|   | |||||||
| @@ -376,20 +376,6 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | |||||||
|             return `href="#root/${target.noteId}"`; |             return `href="#root/${target.noteId}"`; | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         content = content.replace(/data-note-path="([^"]*)"/g, (match, notePath) => { |  | ||||||
|             const noteId = notePath.split("/").pop(); |  | ||||||
|  |  | ||||||
|             let targetNoteId; |  | ||||||
|  |  | ||||||
|             if (noteId === 'root' || noteId.startsWith("_")) { // named noteIds stay identical across instances |  | ||||||
|                 targetNoteId = noteId; |  | ||||||
|             } else { |  | ||||||
|                 targetNoteId = noteIdMap[noteId]; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             return `data-note-path="root/${targetNoteId}"`; |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         if (noteMeta) { |         if (noteMeta) { | ||||||
|             const includeNoteLinks = (noteMeta.attributes || []) |             const includeNoteLinks = (noteMeta.attributes || []) | ||||||
|                 .filter(attr => attr.type === 'relation' && attr.name === 'includeNoteLink'); |                 .filter(attr => attr.type === 'relation' && attr.name === 'includeNoteLink'); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user