// ==UserScript==
// @name WorkFlowy - Inline Code Formatting [ARCHIVED]
// @description Adds inline code formatting to WorkFlowy (e.g. `code`).
// @author Gavin Elster
// @version 2023.04.24
// @license MIT
//
// @namespace https://github.com/elstgav
// @homepageURL https://github.com/elstgav/workflowy
// @supportURL https://github.com/elstgav/workflowy/issues
//
// @downloadURL https://raw.githubusercontent.com/elstgav/workflowy/main/dist/scripts/archive/inline-code-style/workflowy.inline-code-style.user.js
// @updateURL https://raw.githubusercontent.com/elstgav/workflowy/main/dist/scripts/archive/inline-code-style/workflowy.inline-code-style.user.js
//
// @match https://workflowy.com/*
//
// @grant none
// @run-at document-end
// ==/UserScript==
//#region src/scripts/archive/inline-code-style/inline-code-style.ts
let currentBullet = null
let page = null
const INLINE_CODE = /`([^`]+)`/g
const MANGLED_BACKTICK_ANCHOR_TAGS = /<\/a>]+ href="([^"]+)">`<\/a>]+ href="(\1)">/g
const createElementFromHTML = (html) => {
const template = document.createElement('template')
template.innerHTML = html.trim()
return template.content.firstChild
}
const style = createElementFromHTML(
``.replaceAll(/[\s\n]+/g, ' '),
)
const highlight = (container) => {
if (!container) return
if (document.getElementById('srch-input')?.value?.includes('`')) return
container.querySelectorAll('.content[contenteditable] .innerContentContainer').forEach((item) => {
if (!(item instanceof HTMLElement)) return
requestAnimationFrame(() => {
if (
!item.textContent ||
item.innerHTML.includes('') ||
!item.textContent.match(INLINE_CODE)
)
return
if ((item.innerHTML.match(MANGLED_BACKTICK_ANCHOR_TAGS)?.length ?? 0) >= 2)
item.innerHTML = item.innerHTML.replaceAll(MANGLED_BACKTICK_ANCHOR_TAGS, '`')
item.innerHTML = item.innerHTML.replaceAll(
INLINE_CODE,
'`$1`',
)
})
})
}
const currentBulletRoot = () =>
currentBullet?.closest('.project.root > .children > .project') ?? null
const currentFocusRoot = () =>
document.querySelector('.project.root > .children > .project:focus-within')
const currentBulletObserver = new MutationObserver((mutationList) => {
const firstMutation = mutationList[0]
if (!firstMutation || !(firstMutation.target instanceof Element)) return
if (
(firstMutation.oldValue?.includes('open') ?? false) !==
firstMutation.target.classList.contains('open')
)
highlight(currentFocusRoot())
})
const onFocusIn = (event) => {
if (currentBullet) {
currentBulletObserver.disconnect()
highlight(currentBulletRoot())
}
currentBullet = event.target instanceof Element ? event.target.closest('.project') : null
if (!currentBullet) return
currentBulletObserver.observe(currentBullet, {
attributes: true,
attributeFilter: ['class'],
attributeOldValue: true,
})
}
const existingListeners = window.WFEventListener
const onWFEvent = (event) => {
existingListeners?.(event)
switch (event) {
case 'indent':
case 'outdent':
case 'operation--bulk_create':
case 'operation--bulk_move':
case 'operation--delete': {
const focusRoot = currentFocusRoot()
if (currentBullet === focusRoot) highlight(page)
else {
highlight(currentFocusRoot())
if (!focusRoot?.contains(currentBullet)) highlight(currentBulletRoot())
}
highlight(page)
break
}
case 'locationChanged':
highlight(page)
break
default:
break
}
}
const appObserver = new MutationObserver(() => {
page = document.querySelector('.page.active')
if (!page) return
appObserver.disconnect()
highlight(page)
page.addEventListener('focusin', onFocusIn)
window.WFEventListener = onWFEvent
})
if (style) document.head.appendChild(style)
appObserver.observe(document.body, {
subtree: true,
childList: true,
})
//#endregion