mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-30 01:36:24 +01:00 
			
		
		
		
	chore(demo): move to right directory
This commit is contained in:
		| @@ -0,0 +1,28 @@ | ||||
| return api.res.send(404); | ||||
|  | ||||
| /** | ||||
|  * To activate this demo, comment (or remove) the very first line of this code note.  | ||||
|  * This is deactivated because custom request handler like this one can be a security risk. | ||||
|  * To test this, execute the following curl request: curl -X POST http://localhost:37740/custom/create-note -H "Content-Type: application/json" -d "{ \"secret\": \"secret-password\", \"title\": \"hello\", \"content\": \"world\" }" | ||||
|  * (host and port might have to be adjusted based on your setup) | ||||
|  * | ||||
|  * See https://github.com/zadam/trilium/wiki/Custom-request-handler for details. | ||||
|  */ | ||||
|  | ||||
| const {req, res} = api; | ||||
| const {secret, title, content} = req.body; | ||||
|  | ||||
| if (req.method == 'POST' && secret === 'secret-password') { | ||||
|     // notes must be saved somewhere in the tree hierarchy specified by a parent note.  | ||||
|     // This is defined by a relation from this code note to the "target" parent note | ||||
|     // alternetively you can just use constant noteId for simplicity (get that from "Note Info" dialog of the desired parent note) | ||||
|     const targetParentNoteId = api.currentNote.getRelationValue('targetNote'); | ||||
|      | ||||
|     const {note} = api.createTextNote(targetParentNoteId, title, content); | ||||
|     const notePojo = note.getPojo(); | ||||
|  | ||||
|     res.status(201).json(notePojo); | ||||
| } | ||||
| else { | ||||
|     res.send(400); | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| <div style="padding: 20px"> | ||||
|     <canvas class="stats-canvas" style="width: 500px; margin: auto;"></canvas> | ||||
|  | ||||
|     <br/> | ||||
|  | ||||
|     <table class="table stats-table" style="font-size: 90%;"> | ||||
|         <tr> | ||||
|             <th>Attribute name</th> | ||||
|             <th>Count</th> | ||||
|         </tr> | ||||
|     </table> | ||||
| </div> | ||||
| @@ -0,0 +1,12 @@ | ||||
| const attrCounts = await api.runOnBackend(() => { | ||||
|     return api.sql.getRows(` | ||||
|         SELECT | ||||
|             name, COUNT(*) AS count | ||||
|         FROM attributes | ||||
|         WHERE isDeleted = 0 | ||||
|         GROUP BY name | ||||
|         ORDER BY count DESC`); | ||||
| }); | ||||
|  | ||||
| renderPieChart(attrCounts.length <= 10 ? attrCounts : attrCounts.splice(0, 10)); | ||||
| renderTable(attrCounts); | ||||
| @@ -0,0 +1,45 @@ | ||||
| module.exports = data => { | ||||
|     const ctx = api.$container.find('.stats-canvas')[0].getContext("2d"); | ||||
|      | ||||
|     const myPieChart = new Chart(ctx, { | ||||
|         type: 'pie', | ||||
|         data: { | ||||
|             datasets: [{ | ||||
|                 data: data.map(nc => nc.count), | ||||
|                 backgroundColor: ['#3366CC','#DC3912','#FF9900','#109618','#990099','#3B3EAC','#0099C6','#DD4477','#66AA00','#B82E2E','#316395','#994499','#22AA99','#AAAA11','#6633CC','#E67300','#8B0707','#329262','#5574A6','#3B3EAC'], | ||||
|                 datalabels: { | ||||
|                     anchor: 'end' | ||||
|                 } | ||||
|             }], | ||||
|             labels: data.map(nc => nc.name) | ||||
|         }, | ||||
|         options: { | ||||
|             legend: { | ||||
|                 display: false | ||||
|             }, | ||||
|             plugins: { | ||||
|                 datalabels: { | ||||
|                     backgroundColor: function(context) { | ||||
|                         return context.dataset.backgroundColor; | ||||
|                     }, | ||||
|                     borderColor: 'white', | ||||
|                     borderRadius: 25, | ||||
|                     borderWidth: 2, | ||||
|                     color: 'white', | ||||
|                     display: function(context) { | ||||
|                         var dataset = context.dataset; | ||||
|                         var count = dataset.data.length; | ||||
|                         var value = dataset.data[context.dataIndex]; | ||||
|                         return value > count * 1.5; | ||||
|                     }, | ||||
|                     font: { | ||||
|                         weight: 'bold' | ||||
|                     }, | ||||
|                     formatter: function(value, context) { | ||||
|                         return context.chart.data.labels[context.dataIndex] + ": " + Math.round(value); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>chart.js</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>chart.js</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../../../../../Weight%20Tracker/Implementation/JS%20code/chart.js">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>chart.js</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>chart.js</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../../../../../../Weight%20Tracker/Implementation/JS%20code/chart.js">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,17 @@ | ||||
| module.exports = counts => { | ||||
|     const $statsTable = api.$container.find('.stats-table'); | ||||
|      | ||||
|     addRow('total', counts.reduce((acc, cur) => acc + cur.count, 0)); | ||||
|      | ||||
|     for (const count of counts) {      | ||||
|         addRow(count.name, count.count); | ||||
|     } | ||||
|  | ||||
|     function addRow(name, count) { | ||||
|         $statsTable.append( | ||||
|             $("<tr>") | ||||
|                 .append($("<td>").text(name)) | ||||
|                 .append($("<td>").text(count)) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,10 @@ | ||||
| <div style="padding: 20px"> | ||||
|     <p>This table shows 100 largest notes, including their revisions and attachments.</p>  | ||||
|  | ||||
|     <table class="table stats-table" style="font-size: 90%;"> | ||||
|         <tr> | ||||
|             <th>Note</th> | ||||
|             <th nowrap>Size</th> | ||||
|         </tr> | ||||
|     </table> | ||||
| </div> | ||||
| @@ -0,0 +1,47 @@ | ||||
| const notes = await api.runOnBackend(() => { | ||||
|     const blobSizes = api.sql.getMap(`SELECT blobId, LENGTH(content) FROM blobs`); | ||||
|     const noteBlobIds = api.sql.getRows(` | ||||
|         SELECT | ||||
|             notes.noteId, | ||||
|             notes.blobId, | ||||
|             GROUP_CONCAT(revisions.blobId) AS revisions_blobIds, | ||||
|             GROUP_CONCAT(note_attachments.blobId) AS note_attachments_blobIds, | ||||
|             GROUP_CONCAT(revision_attachments.blobId) AS revision_attachments_blobIds | ||||
|         FROM | ||||
|             notes | ||||
|             LEFT JOIN revisions USING (noteId) | ||||
|             LEFT JOIN attachments AS note_attachments ON notes.noteId = note_attachments.ownerId | ||||
|             LEFT JOIN attachments AS revision_attachments ON revisions.revisionId = revision_attachments.ownerId | ||||
|         GROUP BY noteId`); | ||||
|      | ||||
|     let noteSizes = []; | ||||
|      | ||||
|     for (const {noteId, blobId, revisions_blobIds, note_attachments_blobIds, revision_attachments_blobIds} of noteBlobIds) { | ||||
|        const blobIds = new Set(`${blobId},${revisions_blobIds},${note_attachments_blobIds},${revision_attachments_blobIds}`.split(',').filter(blobId => !!blobId)); | ||||
|          | ||||
|         const lengths = [...blobIds].map(blobId => blobSizes[blobId] || 0); | ||||
|         const totalLength = lengths.reduce((partialSum, a) => partialSum + a, 0); | ||||
|          | ||||
|         noteSizes.push({ noteId, size: totalLength }); | ||||
|     } | ||||
|      | ||||
|     noteSizes.sort((a, b) => a.size > b.size ? -1 : 1); | ||||
|      | ||||
|     noteSizes = noteSizes.splice(0, 100); | ||||
|      | ||||
|     return noteSizes; | ||||
| }); | ||||
|  | ||||
| const $statsTable = api.$container.find('.stats-table'); | ||||
|  | ||||
| for (const note of notes) {      | ||||
|     $statsTable.append( | ||||
|         $("<tr>") | ||||
|             .append( | ||||
|                 $("<td>").append(await api.createNoteLink(note.noteId, {showNotePath: true})) | ||||
|             )  | ||||
|             .append( | ||||
|                 $("<td nowrap>").text(note.size + " bytes") | ||||
|             ) | ||||
|     ); | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| <div style="padding: 20px"> | ||||
|     <table class="table stats-table" style="font-size: 90%;"> | ||||
|         <tr> | ||||
|             <th>Note</th> | ||||
|             <th nowrap>Clone count</th> | ||||
|         </tr> | ||||
|     </table> | ||||
| </div> | ||||
| @@ -0,0 +1,26 @@ | ||||
| const notes = await api.runOnBackend(() => { | ||||
|     return api.sql.getRows(` | ||||
|         SELECT | ||||
|             notes.noteId, | ||||
|             COUNT(branches.branchId) AS count | ||||
|         FROM notes | ||||
|         JOIN branches USING (noteId) | ||||
|         WHERE notes.isDeleted = 0 | ||||
|           AND branches.isDeleted = 0 | ||||
|         GROUP BY notes.noteId | ||||
|         HAVING count > 1 | ||||
|         ORDER BY count DESC | ||||
|         LIMIT 100`); | ||||
| }); | ||||
|  | ||||
| const $statsTable = api.$container.find('.stats-table'); | ||||
|  | ||||
| for (const note of notes) {      | ||||
|     $statsTable.append( | ||||
|         $("<tr>") | ||||
|             .append( | ||||
|                 $("<td>").append(await api.createNoteLink(note.noteId, {showNotePath: true})) | ||||
|             )  | ||||
|             .append($("<td nowrap>").text(note.count)) | ||||
|     ); | ||||
| } | ||||
| @@ -0,0 +1,10 @@ | ||||
| <div style="padding: 20px"> | ||||
|     <p>This table shows notes with most revisions</p>  | ||||
|  | ||||
|     <table class="table stats-table" style="font-size: 90%;"> | ||||
|         <tr> | ||||
|             <th>Note</th> | ||||
|             <th nowrap>Revision count</th> | ||||
|         </tr> | ||||
|     </table> | ||||
| </div> | ||||
| @@ -0,0 +1,24 @@ | ||||
| const notes = await api.runOnBackend(() => { | ||||
|     return api.sql.getRows(` | ||||
|         SELECT | ||||
|             notes.noteId, | ||||
|             COUNT(revisions.revisionId) AS count | ||||
|         FROM notes | ||||
|         JOIN revisions USING (noteId) | ||||
|         WHERE notes.isDeleted = 0 | ||||
|         GROUP BY notes.noteId | ||||
|         ORDER BY count DESC | ||||
|         LIMIT 100`); | ||||
| }); | ||||
|  | ||||
| const $statsTable = api.$container.find('.stats-table'); | ||||
|  | ||||
| for (const note of notes) {      | ||||
|     $statsTable.append( | ||||
|         $("<tr>") | ||||
|             .append( | ||||
|                 $("<td>").append(await api.createNoteLink(note.noteId, {showNotePath: true})) | ||||
|             )  | ||||
|             .append($("<td nowrap>").text(note.count)) | ||||
|     ); | ||||
| } | ||||
| @@ -0,0 +1,10 @@ | ||||
| <div style="padding: 20px"> | ||||
|     <p>This table shows notes which are linked by other notes through relations</p>  | ||||
|  | ||||
|     <table class="table stats-table" style="font-size: 90%;"> | ||||
|         <tr> | ||||
|             <th>Note</th> | ||||
|             <th nowrap>Relation count</th> | ||||
|         </tr> | ||||
|     </table> | ||||
| </div> | ||||
| @@ -0,0 +1,26 @@ | ||||
| const notes = await api.runOnBackend(() => { | ||||
|     return api.sql.getRows(` | ||||
|         SELECT | ||||
|             notes.noteId, | ||||
|             COUNT(attributes.attributeId) AS count | ||||
|         FROM notes | ||||
|         JOIN attributes ON attributes.value = notes.noteId | ||||
|         WHERE notes.isDeleted = 0 | ||||
|           AND attributes.isDeleted = 0 | ||||
|           AND attributes.type = 'relation' | ||||
|         GROUP BY notes.noteId | ||||
|         ORDER BY count DESC | ||||
|         LIMIT 100`); | ||||
| }); | ||||
|  | ||||
| const $statsTable = api.$container.find('.stats-table'); | ||||
|  | ||||
| for (const note of notes) {      | ||||
|     $statsTable.append( | ||||
|         $("<tr>") | ||||
|             .append( | ||||
|                 $("<td>").append(await api.createLink(note.noteId, {showNotePath: true})) | ||||
|             )  | ||||
|             .append($("<td nowrap>").text(note.count)) | ||||
|     ); | ||||
| } | ||||
| @@ -0,0 +1,13 @@ | ||||
| <div style="padding: 20px"> | ||||
|     <canvas class="stats-canvas" style="width: 500px; margin: auto;"></canvas> | ||||
|  | ||||
|     <br/> | ||||
|  | ||||
|     <table class="table stats-table" style="font-size: 90%;"> | ||||
|         <tr> | ||||
|             <th>Note type</th> | ||||
|             <th>Count (not deleted)</th> | ||||
|             <th>Count (deleted)</th> | ||||
|         </tr> | ||||
|     </table> | ||||
| </div> | ||||
| @@ -0,0 +1,20 @@ | ||||
| const noteCounts = await api.runOnBackend(() => { | ||||
|     return api.sql.getRows(` | ||||
|         SELECT | ||||
|             type, | ||||
|             isDeleted, | ||||
|             SUM(CASE WHEN isDeleted=0 THEN 1 ELSE 0 END) AS countNotDeleted, | ||||
|             SUM(CASE WHEN isDeleted=1 THEN 1 ELSE 0 END) AS countDeleted | ||||
|         FROM notes | ||||
|         GROUP BY type | ||||
|         ORDER BY countNotDeleted DESC`); | ||||
| }); | ||||
|  | ||||
| renderPieChart(noteCounts.map(nc => { | ||||
|     return { | ||||
|         name: nc.type, | ||||
|         count: nc.countNotDeleted | ||||
|     }; | ||||
| })); | ||||
|  | ||||
| renderTable(noteCounts); | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>renderPieChart</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>renderPieChart</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../../../Attribute%20count/template/js/renderPieChart.js">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| module.exports = counts => { | ||||
|     const $statsTable = api.$container.find('.stats-table'); | ||||
|      | ||||
|     addRow('total',  | ||||
|            counts.reduce((acc, cur) => acc + cur.countNotDeleted, 0),  | ||||
|            counts.reduce((acc, cur) => acc + cur.countDeleted, 0) | ||||
|     ); | ||||
|      | ||||
|     for (const count of counts) { | ||||
|         addRow(count.type, count.countNotDeleted, count.countDeleted); | ||||
|     } | ||||
|  | ||||
|     function addRow(type, countNotDeleted, countDeleted) { | ||||
|         $statsTable.append( | ||||
|             $("<tr>") | ||||
|                 .append($("<td>").text(type)) | ||||
|                 .append($("<td>").text(countNotDeleted)) | ||||
|                 .append($("<td>").text(countDeleted)) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,26 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Task manager</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Task manager</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a simple TODO/Task manager. You can see some description and explanation | ||||
|           here: <a href="https://github.com/zadam/trilium/wiki/Task-manager">https://github.com/zadam/trilium/wiki/Task-manager</a> | ||||
|  | ||||
|         </p> | ||||
|         <p>Please note that this is meant as scripting example only and feature/bug | ||||
|           support is very limited.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,10 @@ | ||||
| // will add a launcher to the left sidebar | ||||
| api.createOrUpdateLauncher({ | ||||
|     id: 'taskmanager', | ||||
|     type: 'script', | ||||
|     title: 'New task', | ||||
|     icon: 'bx-task', | ||||
|     keyboardShortcut: 'alt+n', | ||||
|     scriptNoteId: api.currentNote.getRelationValue('createNewTask'), | ||||
|     isVisible: true | ||||
| }); | ||||
| @@ -0,0 +1,27 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Buy a board game for Alice</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Buy a board game for Alice</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <figure class="image image-style-side"> | ||||
|           <img style="aspect-ratio:209/300;" src="Buy a board game for Alice.jpg" | ||||
|           width="209" height="300"> | ||||
|         </figure> | ||||
|         <p>Maybe CodeNames? <a href="https://boardgamegeek.com/boardgame/178900/codenames">https://boardgamegeek.com/boardgame/178900/codenames</a> | ||||
|  | ||||
|         </p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 7.0 KiB | 
| @@ -0,0 +1,19 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Dentist appointment</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Dentist appointment</h1> | ||||
|  | ||||
|       <div class="ck-content"></div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Get a gym membership</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Get a gym membership</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>Just in time for new years resolution!</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,7 @@ | ||||
| span.fancytree-node.todo .fancytree-title { | ||||
|     color: red !important; | ||||
| } | ||||
|  | ||||
| span.fancytree-node.done .fancytree-title { | ||||
|     color: green !important; | ||||
| } | ||||
| @@ -0,0 +1,63 @@ | ||||
| if (!["task", "location", "tag", "todoDate", "doneDate"].includes(api.originEntity.name)) { | ||||
|     return; | ||||
| } | ||||
|  | ||||
| const tagRootNote = api.getNoteWithLabel('taskTagRoot'); | ||||
| const doneRootNote = api.getNoteWithLabel('taskDoneRoot'); | ||||
| const todoRootNote = api.getNoteWithLabel('taskTodoRoot'); | ||||
|  | ||||
| if (!tagRootNote || !doneRootNote || !todoRootNote) { | ||||
|     console.log("One of the tagRootNote, doneRootNote or todoRootNote does not exist"); | ||||
|     return; | ||||
| } | ||||
|  | ||||
| const note = api.originEntity.getNote(); | ||||
|  | ||||
| if (note.isDeleted) { | ||||
|     return; | ||||
| } | ||||
|      | ||||
| const attributes = note.getAttributes(); | ||||
|  | ||||
| const todoDate = note.getLabelValue('todoDate'); | ||||
| const doneDate = note.getLabelValue('doneDate'); | ||||
|  | ||||
| function isWithinExpectedRange(date) { | ||||
|     if (!date) { | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     const year = parseInt(date.substr(0, 4)); | ||||
|      | ||||
|     return year >= 2010 && year < 2050; | ||||
| } | ||||
|  | ||||
| if (!isWithinExpectedRange(todoDate) || !isWithinExpectedRange(doneDate)) { | ||||
|     console.log(`One or both dates - ${todoDate}, ${doneDate} - is outside of expected range`); | ||||
|      | ||||
|     return; | ||||
| } | ||||
|  | ||||
| const isTaskDone = !!doneDate; | ||||
|  | ||||
| api.toggleNoteInParent(isTaskDone, note.noteId, doneRootNote.noteId); | ||||
| api.toggleNoteInParent(!isTaskDone, note.noteId, todoRootNote.noteId); | ||||
|  | ||||
| const location = note.getLabelValue('location'); | ||||
| const locationRootNote = api.getNoteWithLabel('taskLocationRoot'); | ||||
|  | ||||
| reconcileAssignments(note, locationRootNote, location ? [location] : [], 'taskLocationNote', isTaskDone); | ||||
|  | ||||
| const tags = attributes.filter(attr => attr.type === 'label' && attr.name === 'tag').map(attr => attr.value); | ||||
|  | ||||
| reconcileAssignments(note, tagRootNote, tags, 'taskTagNote', isTaskDone); | ||||
|  | ||||
| note.toggleLabel(isTaskDone, "cssClass", "done"); | ||||
|  | ||||
| const doneTargetNoteId = isTaskDone ?  api.getDayNote(doneDate).noteId : null; | ||||
| api.setNoteToParent(note.noteId, 'DONE', doneTargetNoteId); | ||||
|  | ||||
| note.toggleLabel(!isTaskDone, "cssClass", "todo"); | ||||
|  | ||||
| const todoTargetNoteId = (!isTaskDone && todoDate) ? api.getDayNote(todoDate).noteId : null; | ||||
| api.setNoteToParent(note.noteId, 'TODO', todoTargetNoteId); | ||||
| @@ -0,0 +1,25 @@ | ||||
| module.exports = function (note, categoryRootNote, assignedCategories, labelName, isTaskDone) { | ||||
|     const found = {}; | ||||
|      | ||||
|     for (const categoryNote of categoryRootNote.getChildNotes()) { | ||||
|         const label = categoryNote.getLabel(labelName); | ||||
|          | ||||
|         if (label) { | ||||
|             found[label.value] = !isTaskDone && assignedCategories.includes(label.value); | ||||
|  | ||||
|             api.toggleNoteInParent(found[label.value], note.noteId, categoryNote.noteId); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     if (!isTaskDone) { | ||||
|         for (const assignedCategory of assignedCategories) { | ||||
|             if (!found[assignedCategory]) { | ||||
|                 const categoryNote = api.createTextNote(categoryRootNote.noteId, assignedCategory, "").note; | ||||
|                  | ||||
|                 categoryNote.addLabel(labelName, assignedCategory); | ||||
|  | ||||
|                 api.ensureNoteIsPresentInParent(note.noteId, categoryNote.noteId); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| // creating notes is backend (server) responsibility so we need to pass | ||||
| // the control there | ||||
| const taskNoteId = await api.runOnBackend(() => { | ||||
|     const todoRootNote = api.getNoteWithLabel('taskTodoRoot'); | ||||
|     const resp = api.createTextNote(todoRootNote.noteId, 'new task', ''); | ||||
|  | ||||
|     return resp.note.noteId; | ||||
| }); | ||||
|  | ||||
| // wait until the frontend is fully synced with the changes made on the backend above | ||||
| await api.waitUntilSynced(); | ||||
|  | ||||
| // we got an ID of newly created note and we want to immediatelly display it | ||||
| await api.activateNewNote(taskNoteId); | ||||
| @@ -0,0 +1,19 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>task template</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>task template</h1> | ||||
|  | ||||
|       <div class="ck-content"></div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,19 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>gym</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>gym</h1> | ||||
|  | ||||
|       <div class="ck-content"></div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Buy some book for Bob</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Buy some book for Bob</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>Bob likes to read popular science books so something like that…</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,30 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Maybe Black Swan?</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Maybe Black Swan?</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p><a href="https://en.wikipedia.org/wiki/The_Black_Swan:_The_Impact_of_the_Highly_Improbable">https://en.wikipedia.org/wiki/The_Black_Swan:_The_Impact_of_the_Highly_Improbable</a> | ||||
|  | ||||
|         </p> | ||||
|         <p><em><strong>The Black Swan: The Impact of the Highly Improbable</strong></em> is | ||||
|           a 2007 book by author and former <a href="https://en.wikipedia.org/wiki/Options_trader">options trader</a>  | ||||
|           <a | ||||
|           href="https://en.wikipedia.org/wiki/Nassim_Nicholas_Taleb">Nassim Nicholas Taleb</a>. The book focuses on the extreme impact of rare | ||||
|             and unpredictable <a href="https://en.wikipedia.org/wiki/Outlier">outlier</a> events | ||||
|             — and the human tendency to find simplistic explanations for these events, | ||||
|             retrospectively. Taleb calls this the <a href="https://en.wikipedia.org/wiki/Black_Swan_theory">Black Swan theory</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,19 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Buy milk</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Buy milk</h1> | ||||
|  | ||||
|       <div class="ck-content"></div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,19 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Send invites for christmas party</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Send invites for christmas party</h1> | ||||
|  | ||||
|       <div class="ck-content"></div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Buy milk</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Buy milk</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../Locations/tesco/Buy%20milk.html">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Buy some book for Bob</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Buy some book for Bob</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../Locations/mall/Buy%20some%20book%20for%20Bob.html">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Send invites for christmas party</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Send invites for christmas party</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../Locations/work/Send%20invites%20for%20christmas%20par.html">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Buy some book for Bob</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Buy some book for Bob</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../../Locations/mall/Buy%20some%20book%20for%20Bob.html">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Buy milk</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Buy milk</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../../Locations/tesco/Buy%20milk.html">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,19 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>health</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>health</h1> | ||||
|  | ||||
|       <div class="ck-content"></div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Buy milk</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Buy milk</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../../Locations/tesco/Buy%20milk.html">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,21 @@ | ||||
| <html> | ||||
|    | ||||
|   <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <link rel="stylesheet" href="../../../../../../style.css"> | ||||
|     <base target="_parent"> | ||||
|     <title data-trilium-title>Buy some book for Bob</title> | ||||
|   </head> | ||||
|    | ||||
|   <body> | ||||
|     <div class="content"> | ||||
|        <h1 data-trilium-h1>Buy some book for Bob</h1> | ||||
|  | ||||
|       <div class="ck-content"> | ||||
|         <p>This is a clone of a note. Go to its <a href="../../Locations/mall/Buy%20some%20book%20for%20Bob.html">primary location</a>.</p> | ||||
|       </div> | ||||
|     </div> | ||||
|   </body> | ||||
|  | ||||
| </html> | ||||
| @@ -0,0 +1,5 @@ | ||||
| <div style="padding: 20px"> | ||||
|     <strong>See explanation <a href="https://github.com/zadam/trilium/wiki/Weight-tracker" target="_blank">here</a></strong>. | ||||
|  | ||||
|     <canvas></canvas> | ||||
| </div> | ||||
| @@ -0,0 +1,46 @@ | ||||
| async function getChartData() { | ||||
|     const days = await api.runOnBackend(() => { | ||||
|         const notes = api.getNotesWithLabel('weight'); | ||||
|         const days = []; | ||||
|  | ||||
|         for (const note of notes) { | ||||
|             const date = note.getLabelValue('dateNote'); | ||||
|             const weight = parseFloat(note.getLabelValue('weight')); | ||||
|  | ||||
|             if (date && weight) { | ||||
|                 days.push({ date, weight }); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         days.sort((a, b) => a.date > b.date ? 1 : -1); | ||||
|  | ||||
|         return days; | ||||
|     }); | ||||
|  | ||||
|     const datasets = [ | ||||
|         { | ||||
|             label: "Weight (kg)", | ||||
|             backgroundColor: 'red', | ||||
|             borderColor: 'red', | ||||
|             data: days.map(day => day.weight), | ||||
|             fill: false, | ||||
|             spanGaps: true, | ||||
|             datalabels: { | ||||
|                 display: false | ||||
|             }, | ||||
|             tension: 0.3 | ||||
|         } | ||||
|     ]; | ||||
|  | ||||
|     return { | ||||
|         datasets: datasets, | ||||
|         labels: days.map(day => day.date) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| const ctx = api.$container.find("canvas")[0].getContext("2d"); | ||||
|  | ||||
| new chartjs.Chart(ctx, { | ||||
|     type: 'line', | ||||
|     data: await getChartData() | ||||
| }); | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -0,0 +1,72 @@ | ||||
| /* | ||||
|  * This defines a custom widget which displays number of words and characters in a current text note. | ||||
|  * To be activated for a given note, add label 'wordCount' to the note, you can also make it inheritable and thus activate it for the whole subtree. | ||||
|  *  | ||||
|  * See it in action in "Books" and its subtree. | ||||
|  */ | ||||
| const TPL = `<div style="padding: 10px; border-top: 1px solid var(--main-border-color); contain: none;"> | ||||
|     <strong>Word count: </strong> | ||||
|     <span class="word-count"></span> | ||||
|  | ||||
|       | ||||
|  | ||||
|     <strong>Character count: </strong> | ||||
|     <span class="character-count"></span> | ||||
| </div>`; | ||||
|  | ||||
| class WordCountWidget extends api.NoteContextAwareWidget { | ||||
|     get position() { return 100; } // higher value means position towards the bottom/right | ||||
|  | ||||
|     get parentWidget() { return 'center-pane'; } | ||||
|  | ||||
|     isEnabled() { | ||||
|         return super.isEnabled() | ||||
|             && this.note.type === 'text' | ||||
|             && this.note.hasLabel('wordCount'); | ||||
|     } | ||||
|  | ||||
|     doRender() { | ||||
|         this.$widget = $(TPL); | ||||
|         this.$wordCount = this.$widget.find('.word-count'); | ||||
|         this.$characterCount = this.$widget.find('.character-count'); | ||||
|         return this.$widget; | ||||
|     } | ||||
|  | ||||
|     async refreshWithNote(note) { | ||||
|         const {content} = await note.getNoteComplement(); | ||||
|  | ||||
|         const text = $(content).text(); // get plain text only | ||||
|  | ||||
|         const counts = this.getCounts(text); | ||||
|  | ||||
|         this.$wordCount.text(counts.words); | ||||
|         this.$characterCount.text(counts.characters); | ||||
|     } | ||||
|  | ||||
|     getCounts(text) { | ||||
|         const chunks = text | ||||
|             .split(/[\s-+:,/\\]+/) | ||||
|             .filter(chunk => chunk !== ''); | ||||
|  | ||||
|         let words; | ||||
|  | ||||
|         if (chunks.length === 1 && chunks[0] === '') { | ||||
|             words = 0; | ||||
|         } | ||||
|         else { | ||||
|             words = chunks.length; | ||||
|         } | ||||
|  | ||||
|         const characters = chunks.join('').length; | ||||
|  | ||||
|         return {words, characters}; | ||||
|     } | ||||
|  | ||||
|     async entitiesReloadedEvent({loadResults}) { | ||||
|         if (loadResults.isNoteContentReloaded(this.noteId)) { | ||||
|             this.refresh(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = new WordCountWidget(); | ||||
		Reference in New Issue
	
	Block a user