diff --git a/server/ui-src/components/AboutMailpit.vue b/server/ui-src/components/AboutMailpit.vue index eae4d2f..353d825 100644 --- a/server/ui-src/components/AboutMailpit.vue +++ b/server/ui-src/components/AboutMailpit.vue @@ -26,15 +26,14 @@ export default { }, methods: { - loadInfo: function () { - let self = this - self.get(self.resolve('/api/v1/info'), false, function (response) { + loadInfo() { + this.get(this.resolve('/api/v1/info'), false, (response) => { mailbox.appInfo = response.data - self.modal('AppInfoModal').show() + this.modal('AppInfoModal').show() }) }, - requestNotifications: function () { + requestNotifications() { // check if the browser supports notifications if (!("Notification" in window)) { alert("This browser does not support desktop notifications") @@ -42,7 +41,6 @@ export default { // we need to ask the user for permission else if (Notification.permission !== "denied") { - let self = this Notification.requestPermission().then(function (permission) { if (permission === "granted") { mailbox.notificationsEnabled = true @@ -180,7 +178,9 @@ export default { {{ formatNumber(mailbox.appInfo.RuntimeStats.SMTPAccepted) }} - ({{ getFileSize(mailbox.appInfo.RuntimeStats.SMTPAcceptedSize) }}) + ({{ + getFileSize(mailbox.appInfo.RuntimeStats.SMTPAcceptedSize) + }}) @@ -245,6 +245,6 @@ export default { - + diff --git a/server/ui-src/components/Favicon.vue b/server/ui-src/components/Favicon.vue index cb57b0e..a12d5ff 100644 --- a/server/ui-src/components/Favicon.vue +++ b/server/ui-src/components/Favicon.vue @@ -39,10 +39,9 @@ export default { } this.iconProcessing = true - let self = this window.setTimeout(() => { - self.icoUpdate() + this.icoUpdate() }, this.iconTimeout) }, }, diff --git a/server/ui-src/components/ListMessages.vue b/server/ui-src/components/ListMessages.vue index 2d90299..66f20d9 100644 --- a/server/ui-src/components/ListMessages.vue +++ b/server/ui-src/components/ListMessages.vue @@ -21,29 +21,25 @@ export default { }, mounted() { - let relativeTime = require('dayjs/plugin/relativeTime') + const relativeTime = require('dayjs/plugin/relativeTime') dayjs.extend(relativeTime) this.refreshUI() }, methods: { - refreshUI: function () { - let self = this - window.setTimeout( - () => { - self.$forceUpdate() - self.refreshUI() - }, - 30000 - ) + refreshUI() { + window.setTimeout(() => { + this.$forceUpdate() + this.refreshUI() + }, 30000) }, - getRelativeCreated: function (message) { - let d = new Date(message.Created) + getRelativeCreated(message) { + const d = new Date(message.Created) return dayjs(d).fromNow() }, - getPrimaryEmailTo: function (message) { + getPrimaryEmailTo(message) { for (let i in message.To) { return message.To[i].Address } @@ -51,11 +47,11 @@ export default { return '[ Undisclosed recipients ]' }, - isSelected: function (id) { + isSelected(id) { return mailbox.selected.indexOf(id) != -1 }, - toggleSelected: function (e, id) { + toggleSelected(e, id) { e.preventDefault() if (this.isSelected(id)) { @@ -67,7 +63,7 @@ export default { } }, - selectRange: function (e, id) { + selectRange(e, id) { e.preventDefault() let selecting = false @@ -102,7 +98,7 @@ export default { } }, - toTagUrl: function (t) { + toTagUrl(t) { if (t.match(/ /)) { t = `"${t}"` } diff --git a/server/ui-src/components/NavMailbox.vue b/server/ui-src/components/NavMailbox.vue index bb8dcfa..3932a7a 100644 --- a/server/ui-src/components/NavMailbox.vue +++ b/server/ui-src/components/NavMailbox.vue @@ -30,7 +30,7 @@ export default { }, methods: { - reloadInbox: function () { + reloadInbox() { const paginationParams = this.getPaginationParams() const reload = paginationParams?.start ? false : true @@ -41,24 +41,22 @@ export default { } }, - loadMessages: function () { + loadMessages() { this.hideNav() // hide mobile menu this.$emit('loadMessages') }, - markAllRead: function () { - let self = this - self.put(self.resolve(`/api/v1/messages`), { 'read': true }, function (response) { + markAllRead() { + this.put(this.resolve(`/api/v1/messages`), { 'read': true }, (response) => { window.scrollInPlace = true - self.loadMessages() + this.loadMessages() }) }, - deleteAllMessages: function () { - let self = this - self.delete(self.resolve(`/api/v1/messages`), false, function (response) { + deleteAllMessages() { + this.delete(this.resolve(`/api/v1/messages`), false, (response) => { pagination.start = 0 - self.loadMessages() + this.loadMessages() }) } } diff --git a/server/ui-src/components/NavSearch.vue b/server/ui-src/components/NavSearch.vue index 81d7b74..94341fb 100644 --- a/server/ui-src/components/NavSearch.vue +++ b/server/ui-src/components/NavSearch.vue @@ -30,22 +30,20 @@ export default { }, methods: { - loadMessages: function () { + loadMessages() { this.hideNav() // hide mobile menu this.$emit('loadMessages') }, - deleteAllMessages: function () { - let s = this.getSearch() + deleteAllMessages() { + const s = this.getSearch() if (!s) { return } - let self = this - - let uri = this.resolve(`/api/v1/search`) + '?query=' + encodeURIComponent(s) - this.delete(uri, false, function (response) { - self.$router.push('/') + const uri = this.resolve(`/api/v1/search`) + '?query=' + encodeURIComponent(s) + this.delete(uri, false, (response) => { + this.$router.push('/') }) } } diff --git a/server/ui-src/components/NavSelected.vue b/server/ui-src/components/NavSelected.vue index 3a95d84..13d7907 100644 --- a/server/ui-src/components/NavSelected.vue +++ b/server/ui-src/components/NavSelected.vue @@ -19,54 +19,52 @@ export default { }, methods: { - loadMessages: function () { + loadMessages() { this.$emit('loadMessages') }, // mark selected messages as read - markSelectedRead: function () { - let self = this + markSelectedRead() { if (!mailbox.selected.length) { return false } - self.put(self.resolve(`/api/v1/messages`), { 'Read': true, 'IDs': mailbox.selected }, function (response) { + this.put(this.resolve(`/api/v1/messages`), { 'Read': true, 'IDs': mailbox.selected }, (response) => { window.scrollInPlace = true - self.loadMessages() + this.loadMessages() }) }, - isSelected: function (id) { + isSelected(id) { return mailbox.selected.indexOf(id) != -1 }, // mark selected messages as unread - markSelectedUnread: function () { - let self = this + markSelectedUnread() { if (!mailbox.selected.length) { return false } - self.put(self.resolve(`/api/v1/messages`), { 'Read': false, 'IDs': mailbox.selected }, function (response) { + this.put(this.resolve(`/api/v1/messages`), { 'Read': false, 'IDs': mailbox.selected }, (response) => { window.scrollInPlace = true - self.loadMessages() + this.loadMessages() }) }, // universal handler to delete current or selected messages - deleteMessages: function () { + deleteMessages() { let ids = [] - let self = this ids = JSON.parse(JSON.stringify(mailbox.selected)) if (!ids.length) { return false } - self.delete(self.resolve(`/api/v1/messages`), { 'IDs': ids }, function (response) { + + this.delete(this.resolve(`/api/v1/messages`), { 'IDs': ids }, (response) => { window.scrollInPlace = true - self.loadMessages() + this.loadMessages() }) }, // test if any selected emails are unread - selectedHasUnread: function () { + selectedHasUnread() { if (!mailbox.selected.length) { return false } @@ -79,7 +77,7 @@ export default { }, // test of any selected emails are read - selectedHasRead: function () { + selectedHasRead() { if (!mailbox.selected.length) { return false } diff --git a/server/ui-src/components/NavTags.vue b/server/ui-src/components/NavTags.vue index 0b1ae29..89122d5 100644 --- a/server/ui-src/components/NavTags.vue +++ b/server/ui-src/components/NavTags.vue @@ -17,7 +17,7 @@ export default { methods: { // test whether a tag is currently being searched for (in the URL) - inSearch: function (tag) { + inSearch(tag) { const urlParams = new URLSearchParams(window.location.search) const query = urlParams.get('q') if (!query) { @@ -29,7 +29,7 @@ export default { }, // toggle a tag search in the search URL, add or remove it accordingly - toggleTag: function (e, tag) { + toggleTag(e, tag) { e.preventDefault() const urlParams = new URLSearchParams(window.location.search) diff --git a/server/ui-src/components/Notifications.vue b/server/ui-src/components/Notifications.vue index 153135c..288d44b 100644 --- a/server/ui-src/components/Notifications.vue +++ b/server/ui-src/components/Notifications.vue @@ -43,8 +43,7 @@ export default { // websocket connect connect() { const ws = new WebSocket(this.socketURI) - let self = this - ws.onmessage = function (e) { + ws.onmessage = (e) => { let response try { response = JSON.parse(e.data) @@ -65,7 +64,7 @@ export default { // update pagination offset pagination.start++ // prevent "Too many calls to Location or History APIs within a short timeframe" - self.delayedPaginationUpdate() + this.delayedPaginationUpdate() } } @@ -79,13 +78,13 @@ export default { } // send notifications - if (!self.pauseNotifications) { - self.pauseNotifications = true + if (!this.pauseNotifications) { + this.pauseNotifications = true let from = response.Data.From != null ? response.Data.From.Address : '[unknown]' - self.browserNotify("New mail from: " + from, response.Data.Subject) - self.setMessageToast(response.Data) + this.browserNotify("New mail from: " + from, response.Data.Subject) + this.setMessageToast(response.Data) // delay notifications by 2s - window.setTimeout(() => { self.pauseNotifications = false }, 2000) + window.setTimeout(() => { this.pauseNotifications = false }, 2000) } } else if (response.Type == "prune") { // messages have been deleted, reload messages to adjust @@ -98,24 +97,24 @@ export default { mailbox.unread = response.Data.Unread // detect version updated, refresh is needed - if (self.version != response.Data.Version) { + if (this.version != response.Data.Version) { location.reload() } } } - ws.onopen = function () { + ws.onopen = () => { mailbox.connected = true - self.socketLastConnection = Date.now() - if (self.reconnectRefresh) { - self.reconnectRefresh = false + this.socketLastConnection = Date.now() + if (this.reconnectRefresh) { + this.reconnectRefresh = false mailbox.refresh = true // trigger refresh window.setTimeout(() => { mailbox.refresh = false }, 500) } } - ws.onclose = function (e) { - if (self.socketLastConnection == 0) { + ws.onclose = (e) => { + if (this.socketLastConnection == 0) { // connection failed immediately after connecting to Mailpit implies proxy websockets aren't configured console.log('Unable to connect to websocket, disabling websocket support') return @@ -123,27 +122,27 @@ export default { if (mailbox.connected) { // count disconnections - self.socketBreaks++ + this.socketBreaks++ } // set disconnected state mailbox.connected = false - if (self.socketBreaks > 3) { + if (this.socketBreaks > 3) { // give up after > 3 successful socket connections & disconnections within a 15 second window, // something is not working right on their end, see issue #319 console.log('Unstable websocket connection, disabling websocket support') return } - if (Date.now() - self.socketLastConnection > 5000) { + if (Date.now() - this.socketLastConnection > 5000) { // only refresh mailbox if the last successful connection was broken for > 5 seconds - self.reconnectRefresh = true + this.reconnectRefresh = true } else { - self.reconnectRefresh = false + this.reconnectRefresh = false } - setTimeout(function () { - self.connect() // reconnect + setTimeout(() => { + this.connect() // reconnect }, 1000) } @@ -214,11 +213,10 @@ export default { this.toastMessage = m - let self = this const el = document.getElementById('messageToast') if (el) { el.addEventListener('hidden.bs.toast', () => { - self.toastMessage = false + this.toastMessage = false }) Toast.getOrCreateInstance(el).show() diff --git a/server/ui-src/components/Pagination.vue b/server/ui-src/components/Pagination.vue index 6ac00fd..f254e78 100644 --- a/server/ui-src/components/Pagination.vue +++ b/server/ui-src/components/Pagination.vue @@ -20,16 +20,16 @@ export default { }, computed: { - canPrev: function () { + canPrev() { return pagination.start > 0 }, - canNext: function () { + canNext() { return this.total > (pagination.start + mailbox.messages.length) }, // returns the number of next X messages - nextMessages: function () { + nextMessages() { let t = pagination.start + parseInt(pagination.limit, 10) if (t > this.total) { t = this.total @@ -40,17 +40,17 @@ export default { }, methods: { - changeLimit: function () { + changeLimit() { pagination.start = 0 this.updateQueryParams() }, - viewNext: function () { + viewNext() { pagination.start = parseInt(pagination.start, 10) + parseInt(pagination.limit, 10) this.updateQueryParams() }, - viewPrev: function () { + viewPrev() { let s = pagination.start - pagination.limit if (s < 0) { s = 0 @@ -59,7 +59,7 @@ export default { this.updateQueryParams() }, - updateQueryParams: function () { + updateQueryParams() { const path = this.$route.path const p = { ...this.$route.query diff --git a/server/ui-src/components/SearchForm.vue b/server/ui-src/components/SearchForm.vue index c90e780..3202b63 100644 --- a/server/ui-src/components/SearchForm.vue +++ b/server/ui-src/components/SearchForm.vue @@ -24,18 +24,18 @@ export default { }, methods: { - searchFromURL: function () { + searchFromURL() { const urlParams = new URLSearchParams(window.location.search) this.search = urlParams.get('q') ? urlParams.get('q') : '' }, - doSearch: function (e) { + doSearch(e) { pagination.start = 0 if (this.search == '') { this.$router.push('/') } else { const urlParams = new URLSearchParams(window.location.search) - let curr = urlParams.get('q') + const curr = urlParams.get('q') if (curr && curr == this.search) { pagination.start = 0 this.$emit('loadMessages') @@ -57,7 +57,7 @@ export default { e.preventDefault() }, - resetSearch: function () { + resetSearch() { this.search = '' this.$router.push('/') } diff --git a/server/ui-src/components/Settings.vue b/server/ui-src/components/Settings.vue index ca58a57..26bfa60 100644 --- a/server/ui-src/components/Settings.vue +++ b/server/ui-src/components/Settings.vue @@ -16,7 +16,7 @@ export default { }, watch: { - theme: function (v) { + theme(v) { if (v == 'auto') { localStorage.removeItem('theme') } else { @@ -34,7 +34,7 @@ export default { }, methods: { - setTheme: function () { + setTheme() { if ( this.theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches diff --git a/server/ui-src/components/message/Attachments.vue b/server/ui-src/components/message/Attachments.vue index fa350fe..aa978db 100644 --- a/server/ui-src/components/message/Attachments.vue +++ b/server/ui-src/components/message/Attachments.vue @@ -18,7 +18,7 @@ export default { }, methods: { - openAttachment: function (part, e) { + openAttachment(part, e) { let filename = part.FileName let contentType = part.ContentType let href = this.resolve('/api/v1/message/' + this.message.ID + '/part/' + part.PartID) diff --git a/server/ui-src/components/message/HTMLCheck.vue b/server/ui-src/components/message/HTMLCheck.vue index f194826..d124730 100644 --- a/server/ui-src/components/message/HTMLCheck.vue +++ b/server/ui-src/components/message/HTMLCheck.vue @@ -41,9 +41,7 @@ export default { }, computed: { - summary: function () { - let self = this - + summary() { if (!this.check) { return false } @@ -65,8 +63,8 @@ export default { } // filter by enabled platforms - let results = o.Results.filter(function (w) { - return self.platforms.indexOf(w.Platform) != -1 + let results = o.Results.filter((w) => { + return this.platforms.indexOf(w.Platform) != -1 }) if (results.length == 0) { @@ -98,7 +96,7 @@ export default { } let maxPartial = 0, maxUnsupported = 0 - result.Warnings.forEach(function (w) { + result.Warnings.forEach((w) => { let scoreWeight = 1 if (w.Score.Found < result.Total.Nodes) { // each error is weighted based on the number of occurrences vs: the total message nodes @@ -108,7 +106,7 @@ export default { // pseudo-classes & at-rules need to be weighted lower as we do not know how many times they // are actually used in the HTML, and including things like bootstrap styles completely throws // off the calculation as these dominate. - if (self.isPseudoClassOrAtRule(w.Title)) { + if (this.isPseudoClassOrAtRule(w.Title)) { scoreWeight = 0.05 w.PseudoClassOrAtRule = true } @@ -124,15 +122,15 @@ export default { }) // sort warnings by final score - result.Warnings.sort(function (a, b) { + result.Warnings.sort((a, b) => { let aWeight = a.Score.Found > result.Total.Nodes ? result.Total.Nodes : a.Score.Found / result.Total.Nodes let bWeight = b.Score.Found > result.Total.Nodes ? result.Total.Nodes : b.Score.Found / result.Total.Nodes - if (self.isPseudoClassOrAtRule(a.Title)) { + if (this.isPseudoClassOrAtRule(a.Title)) { aWeight = 0.05 } - if (self.isPseudoClassOrAtRule(b.Title)) { + if (this.isPseudoClassOrAtRule(b.Title)) { bWeight = 0.05 } @@ -148,7 +146,7 @@ export default { return result }, - graphSections: function () { + graphSections() { let s = Math.round(this.summary.Total.Supported) let p = Math.round(this.summary.Total.Partial) let u = 100 - s - p @@ -172,7 +170,7 @@ export default { }, // colors depend on both varying unsupported & partially unsupported percentages - scoreColor: function () { + scoreColor() { if (this.summary.Total.Unsupported < 5 && this.summary.Total.Partial < 10) { this.$emit('setBadgeStyle', 'bg-success') return 'text-success' @@ -197,62 +195,51 @@ export default { platforms(v) { localStorage.setItem('html-check-platforms', JSON.stringify(v)) }, - // enabled(v) { - // if (!v) { - // localStorage.setItem('htmlCheckDisabled', true) - // this.$emit('setHtmlScore', false) - // } else { - // localStorage.removeItem('htmlCheckDisabled') - // this.doCheck() - // } - // } }, methods: { - doCheck: function () { + doCheck() { this.check = false if (this.message.HTML == "") { return } - let self = this - // ignore any error, do not show loader - axios.get(self.resolve('/api/v1/message/' + self.message.ID + '/html-check'), null) - .then(function (result) { - self.check = result.data - self.error = false + axios.get(this.resolve('/api/v1/message/' + this.message.ID + '/html-check'), null) + .then((result) => { + this.check = result.data + this.error = false // set tooltips - window.setTimeout(function () { + window.setTimeout(() => { const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); [...tooltipTriggerList].map(tooltipTriggerEl => new Tooltip(tooltipTriggerEl)) }, 500) }) - .catch(function (error) { + .catch((error) => { // handle error if (error.response && error.response.data) { // The request was made and the server responded with a status code // that falls out of the range of 2xx if (error.response.data.Error) { - self.error = error.response.data.Error + this.error = error.response.data.Error } else { - self.error = error.response.data + this.error = error.response.data } } else if (error.request) { // The request was made but no response was received // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // http.ClientRequest in node.js - self.error = 'Error sending data to the server. Please try again.' + this.error = 'Error sending data to the server. Please try again.' } else { // Something happened in setting up the request that triggered an Error - self.error = error.message + this.error = error.message } }) }, - loadConfig: function () { + loadConfig() { let platforms = localStorage.getItem('html-check-platforms') if (platforms) { try { @@ -268,7 +255,7 @@ export default { }, // return a platform's families (email clients) - families: function (k) { + families(k) { if (this.check.Platforms[k]) { return this.check.Platforms[k] } @@ -277,19 +264,19 @@ export default { }, // return whether the test string is a pseudo class (:) or at rule (@) - isPseudoClassOrAtRule: function (t) { + isPseudoClassOrAtRule(t) { return t.match(/^(:|@)/) }, - round: function (v) { + round(v) { return Math.round(v) }, - round2dm: function (v) { + round2dm(v) { return Math.round(v * 100) / 100 }, - scrollToWarnings: function () { + scrollToWarnings() { if (!this.$refs.warnings) { return } @@ -312,9 +299,9 @@ export default {
- +

{{ round2dm(summary.Total.Supported) }}%

@@ -388,8 +375,9 @@ export default {
+ :aria-valuenow="warning.Score.Supported" aria-valuemin="0" + aria-valuemax="100" :style="{ width: warning.Score.Supported + '%' }" + title="Supported">
{{ round(warning.Score.Supported) + '%' }}
@@ -402,8 +390,9 @@ export default {
+ :aria-valuenow="warning.Score.Unsupported" aria-valuemin="0" + aria-valuemax="100" :style="{ width: warning.Score.Unsupported + '%' }" + title="Not supported">
{{ round(warning.Score.Unsupported) + '%' }}
@@ -420,7 +409,8 @@ export default { Detected {{ warning.Score.Found }} {{ warning.Title }} propert in the CSS styles, but unable to test if used or not. + v-else>ies in the CSS + styles, but unable to test if used or not.

@@ -487,9 +477,9 @@ export default {
- The support for HTML/CSS messages varies greatly across email clients. HTML check - attempts to calculate the overall support for your email for all selected platforms - to give you some idea of the general compatibility of your HTML email. + The support for HTML/CSS messages varies greatly across email clients. HTML + check attempts to calculate the overall support for your email for all selected + platforms to give you some idea of the general compatibility of your HTML email.
@@ -507,29 +497,31 @@ export default { Internally the original HTML message is run against {{ check.Total.Tests }} different HTML and CSS tests. All tests (except for <script>) correspond to a test on - caniemail.com, and the - final score is calculated using the available compatibility data. + caniemail.com, and + the final score is calculated using the available compatibility data.

- CSS support is very difficult to programmatically test, especially if a message - contains CSS style blocks or is linked to remote stylesheets. Remote stylesheets - are, unless blocked via --block-remote-css-and-fonts, downloaded - and injected into the message as style blocks. The email is then - inlined + CSS support is very difficult to programmatically test, especially if a + message contains CSS style blocks or is linked to remote stylesheets. Remote + stylesheets are, unless blocked via + --block-remote-css-and-fonts, + downloaded and injected into the message as style blocks. The email is then + inlined to matching HTML elements. This gives Mailpit fairly accurate results.

CSS properties such as @font-face, :visited, :hover etc cannot be inlined however, so these are searched for - within CSS blocks. This method is not accurate as Mailpit does not know how many - nodes it actually applies to, if any, so they are weighted lightly (5%) as not - to affect the score. An example of this would be any email linking to the full - bootstrap CSS which contains dozens of unused attributes. + within CSS blocks. This method is not accurate as Mailpit does not know how + many nodes it actually applies to, if any, so they are weighted lightly (5%) + as not to affect the score. An example of this would be any email linking to + the full bootstrap CSS which contains dozens of unused attributes.

- All warnings are displayed with their respective support, including any specific - notes, and it is up to you to decide what you do with that information and how - badly it may impact your message. + All warnings are displayed with their respective support, including any + specific notes, and it is up to you to decide what you do with that + information and how badly it may impact your message.

@@ -552,13 +544,15 @@ export default {

For each test, Mailpit calculates both the unsupported & partially-supported percentages in relation to the number of matches against the total number of - nodes (elements) in the HTML. The maximum unsupported and partially-supported - weighted scores are then used for the final score (ie: worst case scenario). + nodes (elements) in the HTML. The maximum unsupported and + partially-supported weighted scores are then used for the final score (ie: + worst case scenario).

To try explain this logic in very simple terms: Assuming a - <script> node (element) has 100% failure (not supported in - any email client), and a <p> node has 100% pass (supported). + <script> node (element) has 100% failure (not supported + in any email client), and a <p> node has 100% pass + (supported).

  • @@ -575,7 +569,8 @@ export default {

- Mailpit will sort the warnings according to their weighted unsupported scores. + Mailpit will sort the warnings according to their weighted unsupported + scores.

@@ -591,9 +586,9 @@ export default {
- HTML check does not detect if the original HTML is valid. In order to detect applied - styles to every node, the HTML email is run through a parser which is very good at - turning invalid input into valid output. It is what it is... + HTML check does not detect if the original HTML is valid. In order to detect + applied styles to every node, the HTML email is run through a parser which is + very good at turning invalid input into valid output. It is what it is...
diff --git a/server/ui-src/components/message/Headers.vue b/server/ui-src/components/message/Headers.vue index 47c9732..65b4b8c 100644 --- a/server/ui-src/components/message/Headers.vue +++ b/server/ui-src/components/message/Headers.vue @@ -1,4 +1,3 @@ -