mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 13:01:31 +08:00 
			
		
		
		
	backlinks improvements, #2349
This commit is contained in:
		
							parent
							
								
									bbceb6251a
								
							
						
					
					
						commit
						630d9f2e45
					
				| @ -2,7 +2,7 @@ image: | |||||||
|   file: .gitpod.dockerfile |   file: .gitpod.dockerfile | ||||||
| 
 | 
 | ||||||
| tasks: | tasks: | ||||||
|     - before: nvm install 16.13.0 && nvm use 16.13.0 |     - before: nvm install 16.13.1 && nvm use 16.13.1 | ||||||
|       init: npm install |       init: npm install | ||||||
|       command: npm run start-server |       command: npm run start-server | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| FROM node:16.13.0-alpine | FROM node:16.13.1-alpine | ||||||
| 
 | 
 | ||||||
| # Create app directory | # Create app directory | ||||||
| WORKDIR /usr/src/app | WORKDIR /usr/src/app | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #!/usr/bin/env bash | #!/usr/bin/env bash | ||||||
| 
 | 
 | ||||||
| PKG_DIR=dist/trilium-linux-x64-server | PKG_DIR=dist/trilium-linux-x64-server | ||||||
| NODE_VERSION=16.13.0 | NODE_VERSION=16.13.1 | ||||||
| 
 | 
 | ||||||
| if [ "$1" != "DONTCOPY" ] | if [ "$1" != "DONTCOPY" ] | ||||||
| then | then | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ if [[ $# -eq 0 ]] ; then | |||||||
|     exit 1 |     exit 1 | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| n exec 16.13.0 npm run webpack | n exec 16.13.1 npm run webpack | ||||||
| 
 | 
 | ||||||
| DIR=$1 | DIR=$1 | ||||||
| 
 | 
 | ||||||
| @ -27,7 +27,7 @@ cp -r electron.js $DIR/ | |||||||
| cp webpack-* $DIR/ | cp webpack-* $DIR/ | ||||||
| 
 | 
 | ||||||
| # run in subshell (so we return to original dir) | # run in subshell (so we return to original dir) | ||||||
| (cd $DIR && n exec 16.13.0 npm install --only=prod) | (cd $DIR && n exec 16.13.1 npm install --only=prod) | ||||||
| 
 | 
 | ||||||
| # cleanup of useless files in dependencies | # cleanup of useless files in dependencies | ||||||
| rm -r $DIR/node_modules/image-q/demo | rm -r $DIR/node_modules/image-q/demo | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ const TPL = ` | |||||||
|             display: flex; |             display: flex; | ||||||
|             justify-content: space-between; |             justify-content: space-between; | ||||||
|             align-items: center; |             align-items: center; | ||||||
|  |             z-index: 10; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         .backlinks-count { |         .backlinks-count { | ||||||
| @ -56,6 +57,15 @@ const TPL = ` | |||||||
|             opacity: 80%; |             opacity: 80%; | ||||||
|             font-size: 90%; |             font-size: 90%; | ||||||
|         } |         } | ||||||
|  |          | ||||||
|  |         .backlink-excerpt .backlink-link { /* the actual backlink */ | ||||||
|  |             font-weight: bold; | ||||||
|  |             background-color: yellow; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /* relation map has already buttons in that position */ | ||||||
|  |         .type-relation-map .backlinks-ticker { top: 50px; } | ||||||
|  |         .type-relation-map .backlinks-items { top: 100px; } | ||||||
|     </style> |     </style> | ||||||
|      |      | ||||||
|     <div class="backlinks-ticker"> |     <div class="backlinks-ticker"> | ||||||
| @ -136,8 +146,12 @@ export default class BacklinksWidget extends NoteContextAwareWidget { | |||||||
|                 showTooltip: false |                 showTooltip: false | ||||||
|             })); |             })); | ||||||
| 
 | 
 | ||||||
|             this.$items.append("<br/>"); |             if (backlink.relationName) { | ||||||
|             this.$items.append(...backlink.excerpts); |                 this.$items.append($("<p>").text("relation: " + backlink.relationName)); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 this.$items.append(...backlink.excerpts); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -182,6 +182,97 @@ function removeImages(document) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const EXCERPT_CHAR_LIMIT = 200; | ||||||
|  | 
 | ||||||
|  | function findExcerpts(sourceNote, referencedNoteId) { | ||||||
|  |     const html = sourceNote.getContent(); | ||||||
|  |     const document = new JSDOM(html).window.document; | ||||||
|  | 
 | ||||||
|  |     const excerpts = []; | ||||||
|  | 
 | ||||||
|  |     removeImages(document); | ||||||
|  | 
 | ||||||
|  |     for (const linkEl of document.querySelectorAll("a")) { | ||||||
|  |         const href = linkEl.getAttribute("href"); | ||||||
|  | 
 | ||||||
|  |         if (!href || !href.endsWith(referencedNoteId)) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         linkEl.classList.add("backlink-link"); | ||||||
|  | 
 | ||||||
|  |         let centerEl = linkEl; | ||||||
|  | 
 | ||||||
|  |         while (centerEl.tagName !== 'BODY' && centerEl.parentElement.textContent.length <= EXCERPT_CHAR_LIMIT) { | ||||||
|  |             centerEl = centerEl.parentElement; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const excerptEls = [centerEl]; | ||||||
|  |         let excerptLength = centerEl.textContent.length; | ||||||
|  |         let left = centerEl; | ||||||
|  |         let right = centerEl; | ||||||
|  | 
 | ||||||
|  |         while (excerptLength < EXCERPT_CHAR_LIMIT) { | ||||||
|  |             let added = false; | ||||||
|  | 
 | ||||||
|  |             const prev = left.previousElementSibling; | ||||||
|  | 
 | ||||||
|  |             if (prev) { | ||||||
|  |                 const prevText = prev.textContent; | ||||||
|  | 
 | ||||||
|  |                 if (prevText.length + excerptLength > EXCERPT_CHAR_LIMIT) { | ||||||
|  |                     const prefix = prevText.substr(prevText.length - (EXCERPT_CHAR_LIMIT - excerptLength)); | ||||||
|  | 
 | ||||||
|  |                     const textNode = document.createTextNode("…" + prefix); | ||||||
|  |                     excerptEls.unshift(textNode); | ||||||
|  | 
 | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 left = prev; | ||||||
|  |                 excerptEls.unshift(left); | ||||||
|  |                 excerptLength += prevText.length; | ||||||
|  |                 added = true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const next = right.nextElementSibling; | ||||||
|  | 
 | ||||||
|  |             if (next) { | ||||||
|  |                 const nextText = next.textContent; | ||||||
|  | 
 | ||||||
|  |                 if (nextText.length + excerptLength > EXCERPT_CHAR_LIMIT) { | ||||||
|  |                     const suffix = nextText.substr(nextText.length - (EXCERPT_CHAR_LIMIT - excerptLength)); | ||||||
|  | 
 | ||||||
|  |                     const textNode = document.createTextNode(suffix + "…"); | ||||||
|  |                     excerptEls.push(textNode); | ||||||
|  | 
 | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 right = next; | ||||||
|  |                 excerptEls.push(right); | ||||||
|  |                 excerptLength += nextText.length; | ||||||
|  |                 added = true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (!added) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const excerptWrapper = document.createElement('div'); | ||||||
|  |         excerptWrapper.classList.add("ck-content"); | ||||||
|  |         excerptWrapper.classList.add("backlink-excerpt"); | ||||||
|  | 
 | ||||||
|  |         for (const childEl of excerptEls) { | ||||||
|  |             excerptWrapper.appendChild(childEl); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         excerpts.push(excerptWrapper.outerHTML); | ||||||
|  |     } | ||||||
|  |     return excerpts; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| function getBacklinks(req) { | function getBacklinks(req) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| @ -192,105 +283,22 @@ function getBacklinks(req) { | |||||||
| 
 | 
 | ||||||
|     let backlinks = note.getTargetRelations(); |     let backlinks = note.getTargetRelations(); | ||||||
| 
 | 
 | ||||||
|     if (backlinks.length > 50) { |     let backlinksWithExcerptCount = 0; | ||||||
|         backlinks = backlinks.slice(0, 50); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     return backlinks.map(backlink => { |     return backlinks.map(backlink => { | ||||||
|         const sourceNote = backlink.note; |         const sourceNote = backlink.note; | ||||||
| 
 | 
 | ||||||
|         const html = sourceNote.getContent(); |         if (sourceNote.type !== 'text' || backlinksWithExcerptCount > 50) { | ||||||
|         const dom = new JSDOM(html); |             return { | ||||||
| 
 |                 noteId: sourceNote.noteId, | ||||||
|         const excerpts = []; |                 relationName: backlink.name | ||||||
| 
 |             }; | ||||||
|         const document = dom.window.document; |  | ||||||
| 
 |  | ||||||
|         removeImages(document); |  | ||||||
| 
 |  | ||||||
|         for (const linkEl of document.querySelectorAll("a")) { |  | ||||||
|             const href = linkEl.getAttribute("href"); |  | ||||||
| 
 |  | ||||||
|             if (!href || !href.includes(noteId)) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             linkEl.style.fontWeight = "bold"; |  | ||||||
|             linkEl.style.backgroundColor = "yellow"; |  | ||||||
| 
 |  | ||||||
|             const LIMIT = 200; |  | ||||||
|             let centerEl = linkEl; |  | ||||||
| 
 |  | ||||||
|             while (centerEl.tagName !== 'BODY' && centerEl.parentElement.textContent.length < LIMIT) { |  | ||||||
|                 centerEl = centerEl.parentElement; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             const sub = [centerEl]; |  | ||||||
|             let counter = centerEl.textContent.length; |  | ||||||
|             let left = centerEl; |  | ||||||
|             let right = centerEl; |  | ||||||
| 
 |  | ||||||
|             while (true) { |  | ||||||
|                 let added = false; |  | ||||||
| 
 |  | ||||||
|                 const prev = left.previousElementSibling; |  | ||||||
| 
 |  | ||||||
|                 if (prev) { |  | ||||||
|                     const prevText = prev.textContent; |  | ||||||
| 
 |  | ||||||
|                     if (prevText.length + counter > LIMIT) { |  | ||||||
|                         const prefix = prevText.substr(prevText.length - (LIMIT - counter)); |  | ||||||
| 
 |  | ||||||
|                         const textNode = document.createTextNode("…" + prefix); |  | ||||||
|                         sub.unshift(textNode); |  | ||||||
| 
 |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     left = prev; |  | ||||||
|                     sub.unshift(left); |  | ||||||
|                     counter += prevText.length; |  | ||||||
|                     added = true; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 const next = right.nextElementSibling; |  | ||||||
| 
 |  | ||||||
|                 if (next) { |  | ||||||
|                     const nextText = next.textContent; |  | ||||||
| 
 |  | ||||||
|                     if (nextText.length + counter > LIMIT) { |  | ||||||
|                         const suffix = nextText.substr(nextText.length - (LIMIT - counter)); |  | ||||||
| 
 |  | ||||||
|                         const textNode = document.createTextNode(suffix + "…"); |  | ||||||
|                         sub.push(textNode); |  | ||||||
| 
 |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     right = next; |  | ||||||
|                     sub.push(right); |  | ||||||
|                     counter += nextText.length; |  | ||||||
|                     added = true; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (!added) { |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             const div = document.createElement('div'); |  | ||||||
|             div.classList.add("ck-content"); |  | ||||||
|             div.classList.add("backlink-excerpt"); |  | ||||||
| 
 |  | ||||||
|             for (const childEl of sub) { |  | ||||||
|                 div.appendChild(childEl); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             const subHtml = div.outerHTML; |  | ||||||
| 
 |  | ||||||
|             excerpts.push(subHtml); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         backlinksWithExcerptCount++; | ||||||
|  | 
 | ||||||
|  |         const excerpts = findExcerpts(sourceNote, noteId); | ||||||
|  | 
 | ||||||
|         return { |         return { | ||||||
|             noteId: sourceNote.noteId, |             noteId: sourceNote.noteId, | ||||||
|             excerpts |             excerpts | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user