diff --git a/background/messages.js b/background/messages.js
index 14a1603..12bd672 100644
--- a/background/messages.js
+++ b/background/messages.js
@@ -26,22 +26,30 @@ md.messages = ({storage: {defaults, state, set}, compilers, mathjax, xhr, webreq
})
}
else if (req.message === 'prism') {
- chrome.scripting.executeScript({
- target: {tabId: sender.tab.id},
- files: [
- `/vendor/prism/prism-${req.language}.min.js`,
- ],
- injectImmediately: true
- }, sendResponse)
+ if (sender.tab) {
+ chrome.scripting.executeScript({
+ target: {tabId: sender.tab.id},
+ files: [
+ `/vendor/prism/prism-${req.language}.min.js`,
+ ],
+ injectImmediately: true
+ }, sendResponse)
+ } else {
+ sendResponse()
+ }
}
else if (req.message === 'mathjax') {
- chrome.scripting.executeScript({
- target: {tabId: sender.tab.id},
- files: [
- `/vendor/mathjax/extensions/${req.extension}.js`,
- ],
- injectImmediately: true
- }, sendResponse)
+ if (sender.tab) {
+ chrome.scripting.executeScript({
+ target: {tabId: sender.tab.id},
+ files: [
+ `/vendor/mathjax/extensions/${req.extension}.js`,
+ ],
+ injectImmediately: true
+ }, sendResponse)
+ } else {
+ sendResponse()
+ }
}
// popup
diff --git a/background/storage.js b/background/storage.js
index 8f5e403..1a6911b 100644
--- a/background/storage.js
+++ b/background/storage.js
@@ -82,10 +82,19 @@ md.storage.bug = (res) => {
// reload extension bug
chrome.permissions.getAll((permissions) => {
var origins = Object.keys(res.origins || {})
- chrome.permissions.remove({
- origins: permissions.origins
- .filter((origin) => origins.indexOf(origin.slice(0, -2)) === -1)
- })
+ var originsToRemove = (permissions.origins || [])
+ .filter((origin) => origins.indexOf(origin.slice(0, -2)) === -1)
+ if (originsToRemove.length) {
+ var promise = chrome.permissions.remove({
+ origins: originsToRemove
+ }, () => {
+ // Ignore error when trying to remove required permissions
+ var error = chrome.runtime.lastError
+ })
+ if (promise && promise.catch) {
+ promise.catch(() => {})
+ }
+ }
})
}
diff --git a/content/index.css b/content/index.css
index 63b8dd9..7ee75ff 100644
--- a/content/index.css
+++ b/content/index.css
@@ -359,3 +359,101 @@ img.emojione {
/* prevent img stretch */
width: auto;
}
+
+/*---------------------------------------------------------------------------*/
+/*copy button*/
+
+#_html pre {
+ position: relative;
+}
+
+#_html pre .markdown-copy-button {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ opacity: 0;
+ transition: opacity 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
+ border: 1px solid var(--copy-btn-border);
+ background-color: var(--copy-btn-bg);
+ color: var(--copy-btn-color);
+ padding: 6px;
+ border-radius: 6px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 10;
+ user-select: none;
+ -webkit-user-select: none;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+}
+
+/* Show button on pre hover and focus */
+#_html pre:hover .markdown-copy-button,
+#_html pre .markdown-copy-button:focus {
+ opacity: 1;
+}
+
+#_html pre .markdown-copy-button:hover {
+ background-color: var(--copy-btn-bg-hover);
+ border-color: var(--copy-btn-border-hover);
+ color: var(--copy-btn-color-hover);
+}
+
+#_html pre .markdown-copy-button.copied {
+ background-color: var(--copy-btn-bg-copied);
+ border-color: var(--copy-btn-border-copied);
+ color: var(--copy-btn-color-copied);
+}
+
+/* Base Theme Variables */
+body {
+ --copy-btn-bg: rgba(255, 255, 255, 0.85);
+ --copy-btn-bg-hover: rgba(240, 240, 240, 0.95);
+ --copy-btn-border: #e1e4e8;
+ --copy-btn-border-hover: #b0b5bc;
+ --copy-btn-color: #57606a;
+ --copy-btn-color-hover: #24292f;
+ --copy-btn-bg-copied: #dcffe4;
+ --copy-btn-border-copied: #85e89d;
+ --copy-btn-color-copied: #22863a;
+}
+
+@media (prefers-color-scheme: dark) {
+ body {
+ --copy-btn-bg: rgba(33, 38, 45, 0.85);
+ --copy-btn-bg-hover: rgba(48, 54, 61, 0.95);
+ --copy-btn-border: #30363d;
+ --copy-btn-border-hover: #8b949e;
+ --copy-btn-color: #8b949e;
+ --copy-btn-color-hover: #c9d1d9;
+ --copy-btn-bg-copied: rgba(56, 139, 253, 0.15);
+ --copy-btn-border-copied: rgba(56, 139, 253, 0.4);
+ --copy-btn-color-copied: #58a6ff;
+ }
+}
+
+._color-light {
+ --copy-btn-bg: rgba(255, 255, 255, 0.85);
+ --copy-btn-bg-hover: rgba(240, 240, 240, 0.95);
+ --copy-btn-border: #e1e4e8;
+ --copy-btn-border-hover: #b0b5bc;
+ --copy-btn-color: #57606a;
+ --copy-btn-color-hover: #24292f;
+ --copy-btn-bg-copied: #dcffe4;
+ --copy-btn-border-copied: #85e89d;
+ --copy-btn-color-copied: #22863a;
+}
+
+._color-dark {
+ --copy-btn-bg: rgba(33, 38, 45, 0.85);
+ --copy-btn-bg-hover: rgba(48, 54, 61, 0.95);
+ --copy-btn-border: #30363d;
+ --copy-btn-border-hover: #8b949e;
+ --copy-btn-color: #8b949e;
+ --copy-btn-color-hover: #c9d1d9;
+ --copy-btn-bg-copied: rgba(56, 139, 253, 0.15);
+ --copy-btn-border-copied: rgba(56, 139, 253, 0.4);
+ --copy-btn-color-copied: #58a6ff;
+}
+
diff --git a/content/index.js b/content/index.js
index b7cf50e..21ac864 100644
--- a/content/index.js
+++ b/content/index.js
@@ -110,6 +110,59 @@ var update = (update) => {
if (state.content.mathjax) {
setTimeout(() => mj.render(), 60)
}
+
+ setTimeout(() => addCopyButtons(), 80)
+}
+
+var addCopyButtons = () => {
+ var preElements = document.querySelectorAll('#_html pre')
+ preElements.forEach((pre) => {
+ if (pre.querySelector('code.mermaid') || pre.querySelector('svg[id^=mermaid]')) {
+ return
+ }
+ if (pre.querySelector('.markdown-copy-button')) {
+ return
+ }
+
+ var button = document.createElement('button')
+ button.className = 'markdown-copy-button'
+ button.type = 'button'
+ button.title = 'Copy code'
+ button.setAttribute('aria-label', 'Copy code')
+
+ button.innerHTML = `
+
+
+ `
+
+ button.addEventListener('click', () => {
+ var codeElement = pre.querySelector('code')
+ var text = codeElement ? codeElement.innerText : pre.innerText
+
+ navigator.clipboard.writeText(text).then(() => {
+ var copyIcon = button.querySelector('.copy-icon')
+ var checkIcon = button.querySelector('.check-icon')
+ copyIcon.style.display = 'none'
+ checkIcon.style.display = 'inline'
+ button.classList.add('copied')
+
+ setTimeout(() => {
+ copyIcon.style.display = 'inline'
+ checkIcon.style.display = 'none'
+ button.classList.remove('copied')
+ }, 2000)
+ }).catch((err) => {
+ console.error('Failed to copy text: ', err)
+ })
+ })
+
+ pre.appendChild(button)
+ })
}
var render = (md) => {
diff --git a/content/mathjax.js b/content/mathjax.js
index d105b90..a360961 100644
--- a/content/mathjax.js
+++ b/content/mathjax.js
@@ -4,11 +4,22 @@ var MathJax = {
pathFilters: [
({name}) => name.startsWith('[tex]') ? false : true // keep the name
],
- require: (path) => path.startsWith('[tex]') ?
- chrome.runtime.sendMessage({
- message: 'mathjax',
- extension: path.replace('[tex]/', '')
- }) : null
+ require: (path) => {
+ if (path.startsWith('[tex]')) {
+ var extension = path.replace('[tex]/', '')
+ if (location.protocol.endsWith('-extension:')) {
+ var script = document.createElement('script')
+ script.src = `/vendor/mathjax/extensions/${extension}.js`
+ document.body.appendChild(script)
+ return null
+ }
+ return chrome.runtime.sendMessage({
+ message: 'mathjax',
+ extension
+ })
+ }
+ return null
+ }
},
tex: {
inlineMath: [
diff --git a/content/preview.html b/content/preview.html
new file mode 100644
index 0000000..06bffbd
--- /dev/null
+++ b/content/preview.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Markdown Preview
+
+
+
+
+
+
+
diff --git a/content/preview.js b/content/preview.js
new file mode 100644
index 0000000..99ab5bc
--- /dev/null
+++ b/content/preview.js
@@ -0,0 +1,75 @@
+;(async () => {
+ const localData = await chrome.storage.local.get('temporaryMarkdown')
+ const markdown = localData.temporaryMarkdown || ''
+
+ const pre = document.querySelector('pre')
+ pre.textContent = markdown
+
+ const state = await chrome.storage.sync.get()
+
+ window.args = {
+ theme: state.theme,
+ raw: state.raw,
+ themes: state.themes,
+ content: state.content,
+ compiler: state.compiler,
+ custom: state.custom,
+ icon: state.settings.icon,
+ }
+
+ const loadCSS = (href) => {
+ return new Promise((resolve) => {
+ const link = document.createElement('link')
+ link.rel = 'stylesheet'
+ link.type = 'text/css'
+ link.href = href
+ link.onload = resolve
+ document.head.appendChild(link)
+ })
+ }
+
+ const loadScript = (src) => {
+ return new Promise((resolve, reject) => {
+ const script = document.createElement('script')
+ script.src = src
+ script.onload = resolve
+ script.onerror = reject
+ document.body.appendChild(script)
+ })
+ }
+
+ await Promise.all([
+ loadCSS('/content/index.css'),
+ loadCSS('/content/themes.css')
+ ])
+
+ await loadScript('/vendor/mithril.min.js')
+
+ if (window.args.content.syntax) {
+ await loadScript('/vendor/prism.min.js')
+ await loadScript('/vendor/prism-autoloader.min.js')
+ await loadScript('/content/prism.js')
+ }
+
+ if (window.args.content.emoji) {
+ await loadScript('/content/emoji.js')
+ }
+
+ if (window.args.content.mermaid) {
+ await loadScript('/vendor/mermaid.min.js')
+ await loadScript('/vendor/panzoom.min.js')
+ await loadScript('/content/mermaid.js')
+ }
+
+ if (window.args.content.mathjax) {
+ await loadScript('/content/mathjax.js')
+ await loadScript('/vendor/mathjax/tex-mml-chtml.js')
+ }
+
+ await loadScript('/content/index.js')
+ await loadScript('/content/scroll.js')
+
+ if (window.args.content.autoreload) {
+ await loadScript('/content/autoreload.js')
+ }
+})()
diff --git a/content/prism.js b/content/prism.js
index e615315..6b9effe 100644
--- a/content/prism.js
+++ b/content/prism.js
@@ -1,7 +1,16 @@
Prism.plugins.autoloader.addScript = (language, done) => {
- chrome.runtime.sendMessage({
- message: 'prism',
- language
- }, done)
+ if (location.protocol.endsWith('-extension:')) {
+ var script = document.createElement('script')
+ script.src = `/vendor/prism/prism-${language}.min.js`
+ script.onload = () => done()
+ script.onerror = () => done()
+ document.body.appendChild(script)
+ }
+ else {
+ chrome.runtime.sendMessage({
+ message: 'prism',
+ language
+ }, done)
+ }
}
diff --git a/manifest.chrome.json b/manifest.chrome.json
index 7f52256..9a93aef 100644
--- a/manifest.chrome.json
+++ b/manifest.chrome.json
@@ -52,7 +52,8 @@
"permissions": [
"storage",
- "scripting"
+ "scripting",
+ "activeTab"
],
"host_permissions": [
diff --git a/manifest.firefox.json b/manifest.firefox.json
index 8a0a721..cf325df 100644
--- a/manifest.firefox.json
+++ b/manifest.firefox.json
@@ -74,7 +74,8 @@
"permissions": [
"storage",
- "scripting"
+ "scripting",
+ "activeTab"
],
"host_permissions": [
diff --git a/popup/index.css b/popup/index.css
index 15542f3..0610617 100644
--- a/popup/index.css
+++ b/popup/index.css
@@ -67,9 +67,9 @@ body.dark .m-button {
/*button*/
/*defaults button*/
-#popup button:nth-child(2) { float: right; }
+#popup > button:nth-child(2) { float: right; }
/*advanced options button*/
-#popup button:nth-child(2n+3) { float: right; margin-top: 20px; }
+#popup > button:nth-child(2n+3) { float: right; margin-top: 20px; }
.m-button {
background-color: #ececec !important;
@@ -145,14 +145,16 @@ body.dark .m-button {
line-height: 36px;
border-bottom: 1px solid #e0e0e0;
display: inline-block;
- width: 33.33%;
- max-width: 33.33%;
- min-width: 33.33%;
+ width: 20%;
+ max-width: 20%;
+ min-width: 20%;
height: 36px;
max-height: 36px;
min-height: 36px;
padding: 0;
align-content: stretch;
+ font-size: 11px !important;
+ text-align: center;
}
.m-tabs .mdc-tab-bar__indicator { background: #607d8b; }
@@ -160,6 +162,147 @@ body.dark .m-button {
.m-panel { display: none; }
.m-panel.is-active { display: block; }
+/* preview tab styling */
+.preview-textarea {
+ width: 100%;
+ height: 180px;
+ box-sizing: border-box;
+ margin-top: 15px;
+ padding: 10px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ font-family: monospace;
+ font-size: 13px;
+ resize: vertical;
+ background-color: #f9f9f9;
+ color: #333;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+}
+.preview-textarea:focus {
+ outline: none;
+ border-color: #607d8b;
+ box-shadow: 0 0 0 2px rgba(96, 125, 139, 0.25);
+}
+
+/* preview controls layout */
+.preview-controls {
+ display: flex;
+ gap: 10px;
+ margin-top: 15px;
+}
+.clear-btn {
+ flex: 1;
+ background-color: #f5f5f5 !important;
+ color: #333 !important;
+ margin-top: 0 !important;
+}
+.preview-btn-half {
+ flex: 1;
+ margin-top: 0 !important;
+}
+
+/* history panel styling */
+.history-scroll {
+ max-height: 250px;
+ margin-top: 15px;
+}
+.history-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 10px;
+ border-bottom: 1px solid #eee;
+ font-family: sans-serif !important;
+ transition: background-color 0.2s ease;
+}
+.history-item:hover {
+ background-color: #f7f7f7;
+}
+.history-info {
+ flex: 1;
+ min-width: 0;
+}
+.history-title {
+ font-size: 13px;
+ font-weight: bold;
+ color: #333;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.history-time {
+ font-size: 11px;
+ color: #888;
+ margin-top: 2px;
+}
+.history-actions {
+ display: flex;
+ gap: 3px;
+ margin-left: 10px;
+}
+.action-btn {
+ border: none;
+ background: none;
+ font-size: 16px;
+ cursor: pointer;
+ padding: 4px 8px;
+ border-radius: 4px;
+ transition: background-color 0.2s ease, color 0.2s ease;
+ line-height: 1;
+}
+.reopen-btn {
+ color: #607d8b;
+}
+.reopen-btn:hover {
+ background-color: rgba(96, 125, 139, 0.15);
+}
+.rename-btn {
+ color: #ff9800;
+}
+.rename-btn:hover {
+ background-color: rgba(255, 152, 0, 0.15);
+}
+.delete-btn {
+ color: #d32f2f;
+}
+.delete-btn:hover {
+ background-color: rgba(211, 47, 47, 0.15);
+}
+.save-btn {
+ color: #2e7d32;
+}
+.save-btn:hover {
+ background-color: rgba(46, 125, 50, 0.15);
+}
+.discard-btn {
+ color: #c62828;
+}
+.discard-btn:hover {
+ background-color: rgba(198, 40, 40, 0.15);
+}
+.history-empty {
+ text-align: center;
+ padding: 30px;
+ color: #999;
+ font-size: 14px;
+ font-family: sans-serif !important;
+}
+
+/* inline rename input styling */
+.edit-title-input {
+ font-size: 13px;
+ padding: 2px 5px;
+ border: 1px solid #607d8b;
+ border-radius: 4px;
+ width: 100%;
+ box-sizing: border-box;
+}
+.edit-title-input:focus {
+ outline: none;
+ box-shadow: 0 0 0 2px rgba(96, 125, 139, 0.25);
+}
+
+
/*---------------------------------------------------------------------------*/
/*options scroll*/
diff --git a/popup/index.js b/popup/index.js
index fe01bae..458e906 100644
--- a/popup/index.js
+++ b/popup/index.js
@@ -53,7 +53,7 @@ var Popup = () => {
],
raw: false,
tab: '',
- tabs: ['theme', 'compiler', 'content'],
+ tabs: ['theme', 'compiler', 'content', 'preview', 'history'],
compilers: [],
description: {
themes: {},
@@ -67,7 +67,29 @@ var Popup = () => {
syntax: 'Syntax highlighting for fenced code blocks',
}
},
- settings: {}
+ settings: {},
+ tempMarkdown: '',
+ history: [],
+ editingId: null,
+ editingTitle: ''
+ }
+
+ var openPreview = (previewUrl) => {
+ chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
+ var activeTab = tabs[0]
+ var isNewTab = activeTab && activeTab.url && (
+ activeTab.url === 'chrome://newtab/' ||
+ activeTab.url === 'about:newtab' ||
+ activeTab.url === 'about:home' ||
+ activeTab.url === 'about:blank' ||
+ activeTab.url.startsWith('chrome-search://')
+ )
+ if (isNewTab) {
+ chrome.tabs.update(activeTab.id, { url: previewUrl })
+ } else {
+ chrome.tabs.create({ url: previewUrl })
+ }
+ })
}
var events = {
@@ -141,6 +163,89 @@ var Popup = () => {
advanced: () => {
chrome.runtime.sendMessage({message: 'popup.advanced'})
+ },
+
+ preview: () => {
+ chrome.storage.local.set({ temporaryMarkdown: state.tempMarkdown || '' }, () => {
+ chrome.storage.local.get('previewHistory', (localRes) => {
+ var history = localRes.previewHistory || []
+
+ var title = ''
+ var lines = (state.tempMarkdown || '').trim().split('\n')
+ if (lines.length && lines[0].startsWith('#')) {
+ title = lines[0].replace(/^#+\s*/, '').trim()
+ }
+ if (!title) {
+ var snippet = (state.tempMarkdown || '').trim().substring(0, 30)
+ title = snippet ? (snippet + (state.tempMarkdown.length > 30 ? '...' : '')) : 'Empty Markdown'
+ }
+
+ var historyItem = {
+ id: Date.now().toString(),
+ title: title,
+ timestamp: new Date().toLocaleString(),
+ markdown: state.tempMarkdown || ''
+ }
+
+ history.unshift(historyItem)
+ if (history.length > 50) {
+ history = history.slice(0, 50)
+ }
+ state.history = history
+
+ chrome.storage.local.set({ previewHistory: history }, () => {
+ openPreview(chrome.runtime.getURL('/content/preview.html'))
+ })
+ })
+ })
+ },
+
+ clear: () => {
+ state.tempMarkdown = ''
+ chrome.storage.local.set({ temporaryMarkdown: '' }, () => {
+ m.redraw()
+ })
+ },
+
+ reopenHistory: (item) => {
+ chrome.storage.local.set({ temporaryMarkdown: item.markdown }, () => {
+ openPreview(chrome.runtime.getURL('/content/preview.html'))
+ })
+ },
+
+ startRename: (item) => {
+ state.editingId = item.id
+ state.editingTitle = item.title
+ },
+
+ saveRename: (itemId) => {
+ if (state.editingTitle.trim() !== '') {
+ var history = state.history.map((item) => {
+ if (item.id === itemId) {
+ item.title = state.editingTitle.trim()
+ }
+ return item
+ })
+ state.history = history
+ chrome.storage.local.set({ previewHistory: history }, () => {
+ state.editingId = null
+ state.editingTitle = ''
+ m.redraw()
+ })
+ }
+ },
+
+ cancelRename: () => {
+ state.editingId = null
+ state.editingTitle = ''
+ },
+
+ deleteHistory: (itemId) => {
+ var history = state.history.filter((item) => item.id !== itemId)
+ state.history = history
+ chrome.storage.local.set({ previewHistory: history }, () => {
+ m.redraw()
+ })
}
}
@@ -152,7 +257,7 @@ var Popup = () => {
state.themes = res.themes
state.raw = res.raw
- state.tab = localStorage.getItem('tab') || 'theme'
+ state.tab = localStorage.getItem('tab') || 'preview'
state.compilers = res.compilers
state.description.compiler = res.description
@@ -162,7 +267,13 @@ var Popup = () => {
m.redraw()
}
- chrome.runtime.sendMessage({message: 'popup'}, init)
+ chrome.runtime.sendMessage({message: 'popup'}, (res) => {
+ chrome.storage.local.get(['temporaryMarkdown', 'previewHistory'], (localRes) => {
+ state.tempMarkdown = localRes.temporaryMarkdown || ''
+ state.history = localRes.previewHistory || []
+ init(res)
+ })
+ })
var oncreate = {
ripple: (vnode) => {
@@ -293,6 +404,84 @@ var Popup = () => {
m('span.mdc-switch-label', key)
))
)
+ ),
+ // preview
+ m('.m-panel', {
+ class: state.tab === 'preview' ? 'is-active' : ''
+ },
+ m('textarea.preview-textarea', {
+ placeholder: 'Paste your Markdown here...',
+ value: state.tempMarkdown,
+ oninput: (e) => {
+ state.tempMarkdown = e.target.value
+ }
+ }),
+ m('.preview-controls',
+ m('button.mdc-button mdc-button--raised m-button clear-btn', {
+ oncreate: oncreate.ripple,
+ onclick: events.clear
+ }, 'Clear'),
+ m('button.mdc-button mdc-button--raised m-button preview-btn-half', {
+ oncreate: oncreate.ripple,
+ onclick: events.preview
+ }, 'Preview')
+ )
+ ),
+ // history
+ m('.m-panel', {
+ class: state.tab === 'history' ? 'is-active' : ''
+ },
+ m('.scroll.history-scroll',
+ state.history && state.history.length ?
+ state.history.map((item) =>
+ m('.history-item',
+ m('.history-info',
+ state.editingId === item.id ?
+ m('input.edit-title-input', {
+ value: state.editingTitle,
+ oninput: (e) => {
+ state.editingTitle = e.target.value
+ },
+ onkeydown: (e) => {
+ if (e.key === 'Enter') events.saveRename(item.id)
+ if (e.key === 'Escape') events.cancelRename()
+ }
+ })
+ : [
+ m('.history-title', item.title),
+ m('.history-time', item.timestamp)
+ ]
+ ),
+ state.editingId === item.id ?
+ m('.history-actions',
+ m('button.action-btn.save-btn', {
+ title: 'Save',
+ onclick: () => events.saveRename(item.id)
+ }, m.trust('')),
+ m('button.action-btn.discard-btn', {
+ title: 'Discard',
+ onclick: () => events.cancelRename()
+ }, m.trust(''))
+ )
+ :
+ m('.history-actions',
+ m('button.action-btn.reopen-btn', {
+ title: 'Open preview',
+ onclick: () => events.reopenHistory(item)
+ }, m.trust('')),
+ m('button.action-btn.rename-btn', {
+ title: 'Rename',
+ onclick: () => events.startRename(item)
+ }, m.trust('')),
+ m('button.action-btn.delete-btn', {
+ title: 'Delete',
+ onclick: () => events.deleteHistory(item.id)
+ }, m.trust(''))
+ )
+ )
+ )
+ : m('.history-empty', 'No preview history found.')
+ )
)
),