UNPKG

verlog

Version:

Generate Version and Changelog for git repo

361 lines (352 loc) 10.1 kB
<title>{{TITLE}}</title> <style> body { word-break: break-all; overflow-wrap: break-word; } </style> <div style="display: flex;"> <div style="flex:3; position: relative;" id="left"></div> <div style="flex:7; position: relative; font-size: 14px;">{{DIFF}}</div> </div> <div id="footer" style="position: relative; display: flex;"> <button onclick="fetch('/add').then(r=>r.json()).then(data=>{ data.ok && location.reload() data.message && alert(data.message) })">add</button>&nbsp; <input id="additional-message" placeholder="additional message"> &nbsp; <button onclick="submit()">commit</button>&nbsp; <label>amend? <input type="checkbox" onclick="submitArgs = (this.checked ? ['--amend'] : [])"></label> </div> <dialog id="result"></dialog> <dialog id="error"></dialog> <template id="input-template"><style> :host { font-size: 12px; height: 100%; min-height: 100px; display: flex; flex-direction: column; } input { width: 100%; border: 1px solid #eee; line-height: 1.5em; color: #0048ff; } select { width: 70px; } span { padding: 0 10px; } .item { cursor: pointer; } .title { display: flex; } </style> <div class="title"> <span>1</span> <select tabindex="-1"> <option value="">none</option> <option value="fix">fix</option> <option value="feat">feat</option> <option value="chore">chore</option> <option value="docs">docs</option> <option value="style">style</option> <option value="refactor">refactor</option> <option value="perf">perf</option> <option value="test">test</option> <option value="BREAKING CHANGE">BREAKING CHANGE</option> </select> <input> </div> <div></div></template> <script> const dialog = document.getElementById('result') const allBlockNodes = [] const store = { lastInput: '', inputs: new Set() } function addToArray(arr, value) { if(arr.indexOf(value) < 0) { arr.push(value) } return arr.length } function arrayUnique(arr){ return Array.from(new Set(arr)) } var source = new EventSource("/events/") source.onerror = function(e) { source.close() document.title = 'OFFLINE:' + document.title const errDialog = document.getElementById('error') errDialog.close() errDialog.innerHTML = `<h4>OFFLINE? You can close this window now.</h4>` errDialog.showModal() } var submitArgs = [] function getAllMessage (){ const additionalMessage = document.getElementById('additional-message').value const messageList = [...document.querySelectorAll('commit-input')].map(input=>input.getValue()) const values = arrayUnique([additionalMessage].concat(messageList).filter(Boolean)) return {additionalMessage, messageList, values} } function submit(){ const {values} = getAllMessage() console.log(values) if(values.length && !confirm('Confirm commit message:\n'+ values.join('\n'))) { return } if(dialog.open) dialog.close() fetch('/commit', { body: JSON.stringify({ commits: values, args: submitArgs }), method: 'POST', headers:{ 'Content-Type':'application/json', Accept: 'application/json' } }).then(r=>r.json()).then(ret=>{ const command = ret.command ? `<pre>$ ${ret.command}</pre>` : '' const actions = ret.ok ? `<button onclick="window.location.reload()">Reload Page</button>` : `<button onclick="dialog.close()">Close Dialog</button>` dialog.innerHTML = `${ret.ok ? 'OK' : 'Fail'}${command}<pre>${ret.message || ret.all}</pre>${actions}` dialog.showModal() }).catch(err=>{ dialog.innerHTML = err.message + `<div><button onclick="dialog.close()">Close Dialog</button></div>` dialog.showModal() }) } function parseCommitMessage (message) { let value = message let prefix = '' const match = /^(.*?): (.*)$/.exec(message) if(match) { value = match[2] prefix = match[1] } return {value, prefix} } function setInputValue(item){ const root = item.getRootNode().host const valueToSet = item.textContent addToArray(root.history.values, valueToSet) const {value, prefix} = parseCommitMessage(valueToSet) const con = item.parentNode.previousElementSibling const input = con.lastElementChild const select = input.previousElementSibling input.value = value select.value = prefix } function getList(){ const html = `<div style="overflow: scroll;">`+Array.from(store.inputs).map(value=>( `<div class="item" onclick="setInputValue(this)">${value}</div>` )).join('')+`</div>` return createElementFromHTML(html) } function restoreMessage(){ const additionalMessage = localStorage.getItem('additionalMessage') || '' let messageList = [] try{ messageList = JSON.parse(localStorage.getItem('messageList')) || [] }catch(e){} if(Array.isArray(messageList)){ allBlockNodes.map(div=>div.querySelector('commit-input')).slice(0, messageList.length).map((v,i)=>{ v.setValue(messageList[i]) }) } document.getElementById('additional-message').value = additionalMessage } function updateAllList() { const {additionalMessage, messageList, values} = getAllMessage() localStorage.setItem('additionalMessage', additionalMessage || '') localStorage.setItem('messageList', JSON.stringify(arrayUnique(messageList))) allBlockNodes.map(div=>div.querySelector('commit-input')).map(v=>v.updateList()) } class CommitInput extends HTMLElement { constructor(){ super() const root = this.root = this.attachShadow({ 'mode': 'open' }) root.appendChild(document.getElementById('input-template').content.cloneNode(true)) this.input = root.querySelector('input') this.span = root.querySelector('span') this.select = root.querySelector('select') this.span.innerHTML = (this.dataset.index | 0) + 1; this.history = { index: 0, values: [], getNext() { if(this.values.length < 1) return const nextIndex = ()=>{ this.index = (this.index - 1) % this.values.length if(this.index < 0) { this.index = this.values.length + this.index } } nextIndex() while(this.index<0 || this.index >= this.values.length) nextIndex() return this.values[this.index] } } this.focus = () => this.input.focus() this.updateList = ()=>{ root.removeChild(root.lastElementChild) root.appendChild(getList()) } this.input.onfocus = e=>{ const el = e.target const {lastInput} = store this.setValue(lastInput, true) } this.setValue = (messageValue, isFocus)=>{ if(messageValue) { const {value, prefix} = parseCommitMessage(messageValue) addToArray(this.history.values, messageValue) this.input.value = value this.select.value = prefix if(isFocus){ this.input.select() } } } this.getValue = ()=>{ let {value} = this.input if(value.trim()){ if(this.select.value){ value = this.select.value + ': ' + value } } return value } this.updateMessage = ()=>{ let {value} = this.input if(value.trim()){ if(this.select.value){ value = this.select.value + ': ' + value } store.lastInput = value store.inputs.add(value) updateAllList() } } this.input.onblur = e=>this.updateMessage() this.select.oninput = e=>{ this.updateMessage() this.focus() } this.onKeyDown = (e) => { const hasCtrl = e.metaKey || e.ctrlKey switch(e.key) { case 'Enter': if(hasCtrl) { submit() break } case 'Tab': { e.preventDefault() this.updateMessage() const index = allBlockNodes.indexOf(this.parentElement) const next = allBlockNodes[e.shiftKey ? index - 1 : index + 1] if(next) { const nextInput = next.querySelector('commit-input') nextInput.focus() nextInput.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'center'}) } break } case 'z': { e.preventDefault() const message = this.history.getNext() if(!message) return const oldValue = this.getValue() addToArray(this.history.values, oldValue) const {value, prefix} = parseCommitMessage(message) this.input.value = value this.select.value = prefix this.input.select() break } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { if(hasCtrl){ e.preventDefault() this.select.value = this.select.options[e.key].value } break } } } } connectedCallback() { this.input.addEventListener('keydown', this.onKeyDown) } disconnectedCallback() { this.input.removeEventListener('keydown', this.onKeyDown) } } customElements.define('commit-input', CommitInput); </script> <script> function info(node) { return { node, top: node.offsetTop, height: node.offsetHeight } } function last(arr) { return arr[arr.length-1] } const blocks = [] let list = [ ...document.querySelectorAll('span[style="color:#A00"],span[style="color:#0A0"],div[style="color:red"]'), ].map(info) let block = [] blocks.push(block) list.forEach((info,i)=>{ block.push(info) const next = list[i+1] if(!next) return if(next.top == info.top) return if(next.top != info.top + info.height) { block = [] blocks.push(block) } }) const div = document.getElementById('left') blocks.filter(block=>block.length).forEach((block, i)=>{ const top = block[0].top const bottom = last(block).top + last(block).height const blockNode = createElementFromHTML(`<div style="background:#66d7ff;width:100%; position: absolute; left:0; top: ${top}px; height:${bottom-top}px"> <commit-input data-index=${i} id="block${i}"></commit-input></dvi>`); allBlockNodes.push(blockNode) div.appendChild(blockNode) }) // div.querySelector('commit-input').focus() restoreMessage() function createElementFromHTML(htmlString) { var div = document.createElement('div'); div.innerHTML = htmlString.trim(); // Change this to div.childNodes to support multiple top-level nodes return div.firstChild; } </script>