app/popup.vue

Summary

Maintainability
Test Coverage
<template lang="pug">
  #popup(@keydown='keydown')
    .header
      img.link(src='img/memm_40.png',@click='openOptions')
      span.title memm 
      input(type='text', v-model='tag', ref='tag', placeholder='put some tags here', autofocus)

    .tags
      div.tag(v-for="(tag, index) in tags") 
        span {{tag}}

    .bookmarks(:class='{selected: selected!=-1}')
      a.bookmark(v-if='tags.length',
        href='#',
        click='addBookmark',
        :class='{selected: selected == 0}') Add bookmark
      a.bookmark(v-for='(bookmark, index) in bookmarks',
        :href='bookmark.url', target='parent'
        :class='{selected: selected==index+1}') {{bookmark.title}}

    .tags
      div.tag(v-for='tag in selectedBookmark.tags')
        span {{tag}}
      div.info {{selectedBookmark.url}}
      
</template>

<script>
const BROWSER = chrome || browser;
import util from './util'

export default {
  computed: {
    selectedBookmark () {
      if(this.bookmarks[this.selected-1])
        return this.bookmarks[this.selected-1]
      else
        return false
    }
  },
  data () {
    return {
      tags: [],
      tag: '',
      url: '',  
      bookmarks: [],
      selected: -1,
      title: '',
      tabId: undefined
    }
  },
  mounted () {
    util.getCurrentTabInfo()
    .then(info => {
      this.url = info.url
      this.title = info.title
      this.tabId = info.id
      this.$nextTick( () => {
        util.sendMessage({msg: 'getURLInfo', url: info.url, tabId: this.tabId})
          .then(this.currentTabInfo)
      })
    })
    this.$refs.tag.focus()
  },
  methods: {
    openOptions () {
      BROWSER.runtime.openOptionsPage()
    },
    removeTag (indexTag) {
      this.tags.splice(indexTag, 1)

      util.sendMessage({
        msg: 'setTags',
        title: this.title,
        url: this.url,
        tabId: this.tabId,
        tags: this.tags
      })
      .then(this.currentTabInfo)
    },
    matchBookmarks (bookmarks) {
      this.bookmarks = bookmarks.filter(b => b.url !== this.url)
      if (this.selected!==-1 && bookmarks.length<=this.selected)
        this.selected = 0
    },
    addBookmark () {
       const tag = this.tag.replace(/[,\s]+/,'')

        // check if empty or already there
        if (tag && this.tags.indexOf(tag)!==-1) {
          this.tags.push(tag)
        }

        if (!this.tags.length === 0) {
          this.tags.push('untagged')
        }

        this.tag = ''
        const message = {
          msg: 'setTags',
          title: this.title,
          url: this.url,
          tabId: this.tabId,
          tags: this.tags
        }

        util.sendMessage(message)

        window.close()
    },
    keydown (ev) {

      const ARROW_UP    = 38
      const ARROW_DOWN  = 40
      const ENTER       = 13
      const SPACE       = 32
      const TAB         = 9
      const COMMA       = 188

      if(ev.which == ARROW_UP) {
        if (this.selected === -1)
          this.selected = this.bookmarks.length
        else
          this.selected -= 1
      }

      if(ev.which === ARROW_DOWN) {
        if (this.selected === this.bookmarks.length)
          this.selected = -1
        else
          this.selected += 1
      }


      if(ev.which === SPACE || ev.which === COMMA || ev.which === TAB) {
        const tag = this.tag.replace(/[,\s]+/,'')
        // check if empty or already there
        if (!tag || this.tags.indexOf(tag)!==-1) {
          ev.preventDefault()
          return
        }

        this.tags.push(tag)

        this.tag = ''
        ev.preventDefault()
        util.sendMessage({msg: 'searchTags', tags: this.tags})
          .then( bookmarks => {
            this.bookmarks = bookmarks.filter( b => b.url != this.url )
          } )

      } 

      // add current tag
      if (ev.which === ENTER) {
        if (this.selected == -1) {
          this.addBookmark()
          return
        }

         // clear the input box
        const tag = this.tag.replace(/[,\s]+/,'')

        if (tag && this.tags.indexOf(tag)===-1) {
          this.tags.push(tag)
        }

        if (this.selectedBookmark) {
          BROWSER.tabs.create({url: this.selectedBookmark.url, active: true})
        } else {
          // send message to background
          util.sendMessage( {msg: 'searchTags', tags: this.tags } )
            .then( bookmarks => this.bookmarks = bookmarks )
            // title: this.title, url: this.url, tags: this.tags, tabId: this.tabId, nocb: true})
        }

        // popup close !
        window.close()
      }
    },
    currentTabInfo (info) {
      if (!info) {
        console.error('lastError', BROWSER.runtime.lastError)
        this.bookmarks = []
      } else {
        this.tags = info.tags
        this.bookmarks = info.related.filter(r => r.url !== this.url)
      }

      if (this.selected!==-1 && this.info.related.length<=this.selected) {
        this.selected = -1
      }
    }
  }
}
</script>

<style lang="stylus">
$orange = #3f4c5a
$tag = #eee

html
body
  width: 350px
  font-family: Cantarell, Arial, sans-serif
  background-color $orange
  margin 0px 0px
  padding 0px 0px

.tags
  padding 10px 10px 5px 10px
  background-color #303a46

  .info
    font-size: 15px
    word-break break-all
    color #ffa500

#popup  

  a
    text-decoration none
    color red

  .header
    display flex
    align-items center
    margin 8px 

    img
      height 25px
      cursor pointer

    .title
      font-family Cantarell, Arial, sans-serif
      font-size 18px
      font-weight bold
      margin 0px 10px
      color orange

    input
      font-family Cantarell, Arial, sans-serif
      font-size 12px
      padding 5px
      border none
      flex-grow 1
      background-color $tag
      border-radius 2px

      &:focus
        outline: none

  .url
    color: blue
    font-size: 11px

  .tag
    background-color #c9c8d2
    padding: 1px 5px 2px 5px
    margin-right: 5px
    margin-bottom: 5px
    border-radius: 2px
    display: inline-block

  .bookmarks
    background-color $tag

    &:hover,
    &.selected
      
      a.bookmark
        color #ff5b5b
        background-color: #2a353f

        &:nth-child(odd)
          background-color #313c47

    a.bookmark
      transition: color .5s, background-color .5s, border .5s
      display block
      color black
      padding 5px
      color #904646
      padding-left 10px
      word-break break-all
      background-color #27313b
      border-left 3px solid transparent

      &.selected
        background-color #6a748b !important
        color white
        border-left 3px solid orange


</style>