From df399f5d3757e6f4cfae4278799885d27ebe8cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20=C3=85hall?= Date: Fri, 12 Jun 2026 07:49:00 +0200 Subject: [PATCH] Update node content when toggling checkboxes --- static/js/marked_position.mjs | 49 ++++++++++++++++++++++------------- static/js/page_node.mjs | 25 ++++++++++++++++++ 2 files changed, 56 insertions(+), 18 deletions(-) diff --git a/static/js/marked_position.mjs b/static/js/marked_position.mjs index 311e806..5f77251 100644 --- a/static/js/marked_position.mjs +++ b/static/js/marked_position.mjs @@ -92,7 +92,8 @@ function escapeHtmlEntities(html, encode) {// {{{ export class MarkedPosition { constructor() {// {{{ - window.setpos = (event) => this.setpos(event) + window.marked_setpos = (event) => this.setpos(event) + window.marked_changecheckbox = (event) => this.changecheckbox(event) this.render() }// }}} setpos(event) {// {{{ @@ -106,6 +107,18 @@ export class MarkedPosition { } }) }// }}} + changecheckbox(event) {// {{{ + event.stopPropagation() + event.preventDefault() + + _mbus.dispatch('MARKDOWN_CHANGE_CHECKBOX', { + checkbox: event.target, + position: { + start: event.target.closest('[data-offset-start]').dataset.offsetStart, + end: event.target.closest('[data-offset-start]').dataset.offsetEnd, + } + }) + }// }}} render() {// {{{ const markedObject = this this.marked = new Marked() @@ -116,7 +129,7 @@ export class MarkedPosition { const content = this.parser.parseInline(token.tokens) return `
- ${content}\n + ${content}\n
\n
` @@ -124,7 +137,7 @@ export class MarkedPosition { paragraph(token) { const content = this.parser.parseInline(token.tokens) - return `

${content}

\n` + return `

${content}

\n` }, list(token) { @@ -143,7 +156,7 @@ export class MarkedPosition { }, listitem(token) { - return `
  • ${this.parser.parse(token.tokens)}
  • \n` + return `
  • ${this.parser.parse(token.tokens)}
  • \n` }, code(token) { @@ -152,12 +165,12 @@ export class MarkedPosition { const code = token.text.replace(other.endingNewline, '') + '\n' if (!langString) { - return `
    `
    +						return `
    `
     							+ (token.escaped ? code : escapeHtmlEntities(code, true))
     							+ '
    \n' } - return `
    '
     						+ (token.escaped ? code : escapeHtmlEntities(code, true))
    @@ -166,7 +179,7 @@ export class MarkedPosition {
     
     				blockquote(token) {
     					const body = this.parser.parse(token.tokens)
    -					return `
    \n${body}
    \n` + return `
    \n${body}
    \n` }, html(token) { @@ -178,13 +191,13 @@ export class MarkedPosition { }, hr(token) { - return `
    \n` + return `
    \n` }, checkbox(token) { - return ` ' + + 'type="checkbox"> ' }, table(token) { @@ -227,7 +240,7 @@ export class MarkedPosition { if (token.tokens.length > 0) { const start = token.tokens[0].position.start.offset const end = token.tokens[0].position.end.offset - ofs = `ondblclick="setpos(event)" data-offset-start="${start}" data-offset-end="${end}"` + ofs = `ondblclick="marked_setpos(event)" data-offset-start="${start}" data-offset-end="${end}"` } const content = this.parser.parseInline(token.tokens); @@ -239,23 +252,23 @@ export class MarkedPosition { }, strong(token) { - return `${this.parser.parseInline(token.tokens)}` + return `${this.parser.parseInline(token.tokens)}` }, em(token) { - return `${this.parser.parseInline(token.tokens)}` + return `${this.parser.parseInline(token.tokens)}` }, codespan(token) { - return `${escapeHtmlEntities(token.text, true)}` + return `${escapeHtmlEntities(token.text, true)}` }, br(token) { - return `
    ` + return `
    ` }, del(token) { - return `${this.parser.parseInline(token.tokens)}` + return `${this.parser.parseInline(token.tokens)}` }, link(token) { @@ -265,7 +278,7 @@ export class MarkedPosition { return text } token.href = cleanHref - let out = ' this.showMarkdown(!this.showMarkdown())) _mbus.subscribe('MARKDOWN_EDIT', ({ detail }) => this.editMarkdown(detail.data)) + _mbus.subscribe('MARKDOWN_CHANGE_CHECKBOX', ({ detail }) => this.checkboxUpdated(detail.data)) this.elName.addEventListener('click', async () => this.renameNode()) this.elNodeContent.addEventListener('input', event => this.contentChanged(event)) @@ -288,6 +289,30 @@ export class N2PageNodeUI extends CustomHTMLElement { return lines }// }}} + // "marked" sends a messagebus event when checking/unchecking a checkbox. + // Updates node and content textarea. + checkboxUpdated(eventData) {// {{{ + const checkbox = eventData.checkbox + const pos = eventData.position + const content = this.node.content() + + // Basic validation to verify that Marked does what is known and expected at this writing. + const mdCheckboxStr = content.slice(pos.start, pos.end) + if (!mdCheckboxStr.match(/^\[[ xX]\] $/)) { + alert(`Checkbox string didn't pass validation: '${mdCheckboxStr}'`) + console.error(`Checkbox string didn't pass validation: '${mdCheckboxStr}'`) + } + + // Node is modified with the new value. User has to save manually, otherwise other changes could be saved + // when a save wasn't expected. + const newValue =`[${checkbox.checked ? 'x' : ' '}] ` + const modifiedContent = this.node.content().slice(0, pos.start) + newValue + this.node.content().slice(pos.end) + this.node.setContent(modifiedContent) + + // Also update the textarea since the node model doesn't know about it. + this.elNodeContent.setRangeText(newValue, pos.start, pos.end, 'select') + + }// }}} } customElements.define('n2-nodeui', N2PageNodeUI)