typora-issues
typora-issues copied to clipboard
[Feature] Allow editing footnotes in inline popup
Hovering a footnote / citation shows it in a popup. This is quite wonderful already. But it'd be even better if I could click within that popup and edit the footnote's content.
I write long documents. This would save me a lot of time scrolling all the way to the bottom and back again.
relates #1571
But I think it would be ok to keep current way, since when you edit the footnote, you can already preview the formats (not in popup through). So there won't be too much scrolling..
But I think it would be ok to keep current way, since when you edit the footnote, you can already preview the formats (not in popup through). So there won't be too much scrolling..
I considered doing this, but it won't work when one footnote is cited multiple times throughout the document.
I've written a "plugin" to allow editing footnotes inline, as I described:
- To install, add this code to the bottom of C:\Program Files\Typora/resources/appsrc/window/frame.js, right above the sourcMappingURL line.
- It adds a footnotes button to the page footer.
- If the footnotes popup is open, clicking a footnote in the editor will scroll to that footnote in the popup.
- I intercept ctrl+s to put the footnotes back where they're supposed to go before saving. Otherwise the document becomes corrupt. Saving via the menu (instead of with ctrl+s) will break the document. Wasn't sure how to intercept that way of saving.
// -----------------------------
// Footnotes Popup Plugin Start
//------------------------------
// Show a footnotes window that can be toggled from the toolbar.
// Add this code to the bottom of C:/Program Files/Typora/resources/appsrc/window/frame.js, right above the sourcMappingURL line.
let footnotesDiv;
let write = document.querySelector('#write');
let footnotesScroll = 0;
function showFootnotes() {
if (!footnotesDiv)
footnotesDiv = document.createElement('div');
footnotesDiv.setAttribute('class', 'floatingFootnotes');
footnotesDiv.setAttribute('style', 'position: sticky; bottom: 0; left: -20px; width: calc(100% + 40px); height: 20vh; z-index: 10; overflow: auto; background: inherit; padding: 10px 20px; border: 1px solid #555; border-top-left-radius: 10px; border-top-right-radius: 10px');
write.append(footnotesDiv);
let footnotes = document.querySelectorAll('.footnotes');
for (let ft of footnotes)
footnotesDiv.append(ft);
footnotesDiv.scrollTop = footnotesScroll;
}
function hideFootnotes() {
if (footnotesDiv && footnotesDiv.firstChild && footnotesDiv.firstChild.nodeType === 1 && footnotesDiv.firstChild.hasAttribute('cid')) {
footnotesScroll = footnotesDiv.scrollTop;
let id = parseInt(footnotesDiv.firstChild.getAttribute('cid').slice(1));
let prevNode = document.querySelector('[cid="n'+(id-1) + '"]').nextSibling;
for (let child of Array.from(footnotesDiv.children))
write.insertBefore(child, prevNode);
}
footnotesDiv.remove();
}
setTimeout(() => {
// Create footnotes toggle button
let toggle = document.createElement('div');
toggle.setAttribute('class', 'footer-item footer-item-left');
toggle.innerHTML = '<span ty-hint="Toggle Footnotes" aria-label="Toggle Footnotes">Footnotes</span>'
document.querySelector('footer').append(toggle);
toggle.addEventListener('click', e => {
if (footnotesDiv && footnotesDiv.parentNode)
hideFootnotes();
else
showFootnotes();
});
// Scroll to footnote when clicked.
document.addEventListener('click', function(e) {
if (!footnotesDiv || !footnotesDiv.parentNode)
return;
// Don't loos focus when editing a footnote
if (e.target.closest('.footnotes'))
return;
let footnote = e.target.closest('sup.md-footnote')
if (footnote) {
let name = footnote.dataset.ref;
for (let ftName of document.body.querySelectorAll('.footnotes .md-def-name')) {
if (ftName.textContent.trim() === name) {
footnotesDiv.scrollTop = ftName.offsetTop;
}
}
}
}, true);
// Detect save via ctrl+s.
// Because Typora will error if we don't hide/restore the footnotes DOM to the original layout before save.
// I don't know how to detect file->save.
// For that case, the user should manually close the footnotes first.
document.addEventListener('keydown', e => {
if ((e.key === 's' || e.key === 'S') && e.ctrlKey) {
if (footnotesDiv && footnotesDiv.parentNode) {
hideFootnotes();
setTimeout(showFootnotes, 100);
}
}
}, true);
}, 50);
// -----------------------------
// Footnotes Popup Plugin End
//------------------------------
I've noticed that sometimes this causes the footnotes to be eaten and lost. Not sure what's going on, but make sure you keep backups.