github outline - github.com

Github outline 懸浮視窗

  1. // ==UserScript==
  2. // @name github outline - github.com
  3. // @namespace Violentmonkey Scripts
  4. // @match https://github.com/*
  5. // @grant none
  6. // @version 1.05
  7. // @author TianyiLi-e0991100238@gmail.com
  8. // @description Get Readme file outline at github
  9. // @description:zh-TW Github outline 懸浮視窗
  10. // ==/UserScript==
  11. if (document.querySelector('#readme article')) {
  12. const styles = /*css*/`
  13. .outline-reader__ctn {
  14. position: fixed;
  15. top: 10vh;
  16. right: 8vw;
  17. width: 300px;
  18. border-radius: 5px;
  19. background: white;
  20. border: solid 1px #e0e0e0;
  21. overflow-y: auto;
  22. font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
  23. max-height: 60vh;
  24. }
  25. .outline-reader__ctn .head {
  26. cursor: pointer;
  27. padding: .5rem .7rem;
  28. font-size: 2rem;
  29. position: sticky;
  30. top: 0px;
  31. background: white;
  32. border-bottom: solid black 1px;
  33. }
  34. .outline-reader__ctn .body {
  35. padding: .8rem .3rem;
  36. }
  37. .outline-reader__ctn a.link {
  38. width: 100%;
  39. text-overflow: ellipsis;
  40. white-space: nowrap;
  41. overflow-x: hidden;
  42. }
  43. `
  44. const styleHeads = document.createElement('style')
  45. styleHeads.innerHTML = styles
  46. document.querySelector('head').appendChild(styleHeads)
  47. const canvas = document.createElement('div')
  48. canvas.classList.add('outline-reader__ctn')
  49. const markdownDOM = document.querySelector('#readme article')
  50. function recursiveNodeReader(node) {
  51. let nodes = [...node.querySelectorAll('h1, h2, h3, h4, h5')]
  52. let n = nodes.shift()
  53. let result = []
  54. do {
  55. if (['h1', 'h2', 'h3', 'h4', 'h5'].includes(n.tagName.toLowerCase())) {
  56. result.push({
  57. text: n.textContent,
  58. level:
  59. ['h1', 'h2', 'h3', 'h4', 'h5'].indexOf(n.tagName.toLowerCase()) + 1,
  60. link: n.querySelector('a').href,
  61. })
  62. }
  63. } while ((n = nodes.shift()))
  64. return result
  65. }
  66.  
  67. function renderDOM(domTree) {
  68. return domTree
  69. .map(
  70. (node, i) =>
  71. `<a class="link" style="display:block" title="${node.text}" href="${
  72. node.link
  73. }" level="${node.level}">${'│&nbsp;&nbsp;'.repeat(
  74. node.level - 2 > 0 ? node.level - 2 : 0
  75. ) +
  76. (domTree[i + 1]
  77. ? domTree[i + 1].level < node.level
  78. ? '└'
  79. : '├'
  80. : '└') +
  81. '─'} ${node.text}</a>`
  82. )
  83. .join('')
  84. }
  85. const body = document.createElement('div')
  86. const header = document.createElement('div')
  87. header.textContent = 'Outline'
  88. header.classList.add('head')
  89. body.classList.add('body')
  90.  
  91. body.addEventListener('mousedown', e => e.stopPropagation(), {
  92. capture: true,
  93. })
  94. body.addEventListener('click', e => e.stopPropagation(), { capture: true })
  95. let isMove = false
  96. let position = {
  97. x: 0,
  98. y: 0,
  99. }
  100. let target = null
  101. function eleMove(e) {
  102. if (!isMove) return
  103. target.style.top = e.clientY - position.y + 'px'
  104. target.style.left = e.clientX - position.x + 'px'
  105. target.style.right = ''
  106. }
  107. canvas.addEventListener('mousedown', function(e) {
  108. isMove = true
  109. position.x = e.offsetX
  110. position.y = e.offsetY
  111. target = this
  112. window.addEventListener('mousemove', eleMove, true)
  113. })
  114. canvas.addEventListener('mouseup', () => {
  115. isMove = false
  116. window.removeEventListener('mousemove', eleMove, true)
  117. })
  118.  
  119. body.innerHTML = renderDOM(recursiveNodeReader(markdownDOM))
  120. canvas.appendChild(header)
  121. canvas.appendChild(body)
  122.  
  123. document.body.appendChild(canvas)
  124. }