diff --git a/helpers/stringTransforms.js b/helpers/stringTransforms.js
index 7624549e5..cb98598fd 100644
--- a/helpers/stringTransforms.js
+++ b/helpers/stringTransforms.js
@@ -39,9 +39,7 @@ export function changeBareLinksToHTMLLink(original: string, addWebIcon: boolean
// clo(captures, `${String(captures.length)} results from bare URL matches:`)
for (const capture of captures) {
const linkURL = capture[3]
- const URLForDisplay = (truncateIfNecessary && linkURL.length > 20)
- ? linkURL.slice(0, 50) + '...'
- : linkURL
+ const URLForDisplay = truncateIfNecessary && linkURL.length > 20 ? linkURL.slice(0, 50) + '...' : linkURL
// logDebug('changeBareLinksToHTMLLink', `${linkURL} / ${URLForDisplay}`)
if (addWebIcon) {
// not displaying icon
@@ -359,11 +357,11 @@ export function encodeRFC3986URIComponent(input: string): string {
.replace(/\[/g, '%5B')
.replace(/\]/g, '%5D')
.replace(/!/g, '%21')
- .replace(/'/g, "%27")
+ .replace(/'/g, '%27')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/\*/g, '%2A')
- // .replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)
+ // .replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)
}
/**
@@ -374,13 +372,98 @@ export function encodeRFC3986URIComponent(input: string): string {
* @returns {string}
*/
export function decodeRFC3986URIComponent(input: string): string {
- const decodedSpecials = input
- .replace(/%5B/g, '[')
- .replace(/%5D/g, ']')
- .replace(/%21/g, '!')
- .replace(/%27/g, "'")
- .replace(/%28/g, '(')
- .replace(/%29/g, ')')
- .replace(/%2A/g, '*')
+ const decodedSpecials = input.replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%21/g, '!').replace(/%27/g, "'").replace(/%28/g, '(').replace(/%29/g, ')').replace(/%2A/g, '*')
return decodeURIComponent(decodedSpecials)
}
+
+/**
+ * @method truncateOnWord(length, [from] = 'right', [ellipsis] = '...')
+ * @returns String
+ * @short Truncates a string without splitting up words.
+ * @extra [from] can be `'right'`, `'left'`, or `'middle'`. If the string is
+ * shorter than `length`, [ellipsis] will not be added. A "word" is
+ * defined as any sequence of non-whitespace characters.
+ *
+ * @example
+ *
+ * 'here we go'.truncateOnWord(5) -> 'here...'
+ * 'here we go'.truncateOnWord(5, 'left') -> '...we go'
+ *
+ * @param {number} length
+ * @param {string} [from] can be `'right'`, `'left'`, or `'middle'`.
+ * @param {boolean} [fromLeft] - whether to truncate from the left or not
+ *
+ * @author Sugar.js https://github.com/andrewplummer/Sugar/blob/b757c66c4e6361af710431117eadcafc5c7d42bc/lib/string.js
+ **/
+function truncateOnWord(str: string, limit: number, fromLeft: boolean = false): string {
+ if (fromLeft) {
+ return truncateOnWord(str.split('').reverse().join(''), limit).split('').reverse().join('')
+ }
+ // WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
+ const TRIM_CHARS = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF'
+ const TRUNC_REG = RegExp(`(?=[${TRIM_CHARS}])`)
+ const words = str.split(TRUNC_REG)
+ let count = 0
+ const result = []
+
+ for (const word of words) {
+ count += word.length
+ if (count <= limit) {
+ result.push(word)
+ } else {
+ break
+ }
+ }
+
+ return result.join('')
+}
+
+/**
+ * @method truncateString(length, [from] = 'right', [split] = true)
+ * @returns String
+ * @short Truncates a string.
+ * @extra [from] can be `'right'`, `'left'`, or `'middle'`. If the string is
+ * shorter than `length`, [ellipsis] will not be added.
+ *
+ * @example
+ *
+ * 'sittin on the dock'.truncate(10) -> 'sittin on ...'
+ * 'sittin on the dock'.truncate(10, 'left') -> '...n the dock'
+ * 'sittin on the dock'.truncate(10, 'middle') -> 'sitti... dock'
+ *
+ * @param {number} length
+ * @param {string} [from] can be `'right'`, `'left'`, or `'middle'`.
+ * @param {boolean} [split] - whether to split on words or not
+ *
+ * @author Sugar.js https://github.com/andrewplummer/Sugar/blob/b757c66c4e6361af710431117eadcafc5c7d42bc/lib/string.js
+ **/
+export function truncateString(str: string, length: number, from: string, splitOnWord: boolean = true): string {
+ let str1, str2, len1, len2
+ if (str.length <= length) {
+ return str.toString()
+ }
+ const ellipsisStr = '...'
+ switch (from) {
+ case 'left':
+ str2 = splitOnWord ? truncateOnWord(str, length, true) : str.slice(str.length - length)
+ return ellipsisStr + str2
+ case 'middle':
+ len1 = Math.ceil(length / 2)
+ len2 = Math.floor(length / 2)
+ str1 = splitOnWord ? truncateOnWord(str, len1) : str.slice(0, len1)
+ str2 = splitOnWord ? truncateOnWord(str, len2, true) : str.slice(str.length - len2)
+ return str1 + ellipsisStr + str2
+ default:
+ str1 = splitOnWord ? truncateOnWord(str, length) : str.slice(0, length)
+ return str1 + ellipsisStr
+ }
+}
+
+/**
+ * Remove text between () inclusive
+ * @param {string} str
+ * @returns {string}
+ */
+export function removeTextBetweenParentheses(str: string): string {
+ return str.replace(/\(.*?\)/g, '')
+}
\ No newline at end of file
diff --git a/jgclark.Summaries/README.md b/jgclark.Summaries/README.md
index 5bbec723c..ff2e9bca2 100644
--- a/jgclark.Summaries/README.md
+++ b/jgclark.Summaries/README.md
@@ -57,22 +57,23 @@ All notes in the special folders (@Archive, @Templates and @Trash) are **ignored
Note: **Why use `@run(...)` (mentions) rather than `#run(...)` (hashtags)**? Well, it just felt more right to use `@run(...)` as there are already `@done(...)` and `@repeat(...)` mentions in use in NotePlan that include a value in the brackets. And in NotePlan, hashtags that end with a number ignore the fractional part (e.g. `#run/5.3` ignores the `.3`) but they are not ignored inside `@run(5.3)`. However, you _can_ use a `#hashtag/value` if you don't mind this limitation.
-## Tracking checklist completetion
+## Tracking checklist completion
-To track checklist completion you must create a referance checklist in the template folder:
+To track checklist completion you must create a reference checklist in the template folder:
+
+
-
Add the title of this template to settings:
-
+
If you want to use this template in another note it can be imported using
`<%- import("Daily tasks”) -%>`
Completion is tracked using the 'appendProgressUpdate' command
-
+
## 'heatmap for complete tasks' command
This displays a 'heatmap' chart of many tasks you've completed on each day (see example above). It uses the `@done(...)` dates in all daily, weekly and project notes over the number of weeks you specify to look back (via the 'Chart Duration (in weeks)' setting). If you set this to 0, the plugin will generate a sensible longish period between 6 and 12 months. It also counts completed tasks without `@done(...)` dates on Calendar notes, and assumes the tasks were completed on the day or start of week in question.
diff --git a/jgclark.Summaries/checklist-2.png b/jgclark.Summaries/checklist_count.png
similarity index 100%
rename from jgclark.Summaries/checklist-2.png
rename to jgclark.Summaries/checklist_count.png
diff --git a/jgclark.Summaries/checklist-1.png b/jgclark.Summaries/checklist_refnote.png
similarity index 100%
rename from jgclark.Summaries/checklist-1.png
rename to jgclark.Summaries/checklist_refnote.png
diff --git a/jgclark.Summaries/checklist.png b/jgclark.Summaries/checklist_settings.png
similarity index 100%
rename from jgclark.Summaries/checklist.png
rename to jgclark.Summaries/checklist_settings.png
diff --git a/jgclark.Summaries/plugin.json b/jgclark.Summaries/plugin.json
index 98edb1161..d8a8eff5d 100644
--- a/jgclark.Summaries/plugin.json
+++ b/jgclark.Summaries/plugin.json
@@ -8,8 +8,8 @@
"plugin.author": "Jonathan Clark",
"plugin.url": "https://github.com/NotePlan/plugins/tree/main/jgclark.Summaries/",
"plugin.changelog": "https://github.com/NotePlan/plugins/blob/main/jgclark.Summaries/CHANGELOG.md",
- "plugin.version": "0.22.0",
- "plugin.lastUpdateInfo": "0.22.0: Add support for checklist progress.\n0.21.0: Add Mermaid charting command. Fix to simple weekly CSV generation.\n0.20.3: Bug fix for progressUpdate() in templates.\n0.20.2: Added x-callback options for /periodStats.\n0.20.1: fix refresh after '/append progress update' command. Logging change.\n0.20.0: add new '/today progress' and '/heatmap for tag' commands, and add refresh button to /periodStats output.\n0.19.3: bug fixes on 'weekly stats generation' commands.\n0.19.2: change date library.\n0.19.1: bug fix\n. 0.19.0: adds totals and averages for hashtags as well. Improve output of averages.",
+ "plugin.version": "0.22.1",
+ "plugin.lastUpdateInfo": "0.22.1 Fix formatting with long lines.\n0.22.0: Add support for checklist progress.\n0.21.0: Add Mermaid charting command. Fix to simple weekly CSV generation.\n0.20.3: Bug fix for progressUpdate() in templates.\n0.20.2: Added x-callback options for /periodStats.\n0.20.1: fix refresh after '/append progress update' command. Logging change.\n0.20.0: add new '/today progress' and '/heatmap for tag' commands, and add refresh button to /periodStats output.\n0.19.3: bug fixes on 'weekly stats generation' commands.\n0.19.2: change date library.\n0.19.1: bug fix\n. 0.19.0: adds totals and averages for hashtags as well. Improve output of averages.",
"plugin.dependencies": [],
"plugin.script": "script.js",
"plugin.isRemote": "false",
diff --git a/jgclark.Summaries/src/summaryHelpers.js b/jgclark.Summaries/src/summaryHelpers.js
index 330e98726..12d7b3efb 100644
--- a/jgclark.Summaries/src/summaryHelpers.js
+++ b/jgclark.Summaries/src/summaryHelpers.js
@@ -30,6 +30,11 @@ import {
isHashtagWanted,
isMentionWanted,
} from '@helpers/search'
+import {
+ removeTextBetweenParentheses,
+ truncateString,
+} from '@helpers/stringTransforms'
+import NPTemplating from 'NPTemplating'
//------------------------------------------------------------------------------
// Get settings
@@ -719,11 +724,24 @@ export async function generateProgressUpdate(occObjs: Array, peri
const daysBetween = toDateMom.diff(fromDateMom, 'days')
// Include sparklines only if this period is a month or less
const showSparklines = (requestToShowSparklines && daysBetween <= 31)
+
+ await occObjs.forEach(async (m) => {
+ if (m.term.includes('<%') && m.term.includes('%>')) {
+ m.term = await NPTemplating.render(m.term)
+ logError('generateProgressUpdate', `rendered term: ${m.term}`)
+ }
+ if (showSparklines) {
+ m.term = truncateString(removeTextBetweenParentheses(m.term), 60, 'middle')
+ }
+ })
+
+ logError('generateProgressUpdate', `this should be after the await`)
+
// Get length of longest progress term (to use with sparklines)
const maxTermLen = Math.max(...occObjs.map((m) => m.term.length))
- let outputArray: Array = []
- for (let occObj of occObjs) {
+ const outputArray: Array = []
+ for (const occObj of occObjs) {
// occObj.logValuesMap()
let thisOutput = ''
switch (style) {