plugin-manager.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. const api = window.dshDesktop
  2. async function main() {
  3. const locale = await api.locale()
  4. const messages = locale.messages
  5. const message = (key, values = {}) => messages[key].replaceAll(/\{([^{}]+)\}/gu, (placeholder, name) => values[name] ?? placeholder)
  6. document.documentElement.lang = locale.id
  7. document.querySelector('#page-title').textContent = messages.pluginManagerTitle
  8. document.querySelector('#title').textContent = messages.pluginManagerTitle
  9. document.querySelector('#description').textContent = messages.pluginManagerDescription
  10. document.querySelector('#refresh').textContent = messages.refresh
  11. document.querySelector('#package-label').textContent = messages.npmPackage
  12. document.querySelector('#install').textContent = messages.install
  13. document.querySelector('#installed-heading').textContent = messages.installed
  14. document.querySelector('#empty').textContent = messages.noPlugins
  15. document.querySelector('#recovery-description').textContent = messages.recoveryDescription
  16. document.querySelector('#retry').textContent = messages.retry
  17. document.querySelector('#disable-all').textContent = messages.disableAll
  18. const list = document.querySelector('#plugins')
  19. const empty = document.querySelector('#empty')
  20. const status = document.querySelector('#status')
  21. const form = document.querySelector('#install-form')
  22. const input = document.querySelector('#package-spec')
  23. const refresh = document.querySelector('#refresh')
  24. const updateForm = document.querySelector('#update-form')
  25. const updateVersion = document.querySelector('#update-version')
  26. let updateName = ''
  27. let updateTrigger
  28. document.querySelector('#update-submit').textContent = messages.update
  29. document.querySelector('#update-cancel').textContent = messages.cancel
  30. function setBusy(busy, statusMessage = '') {
  31. for (const control of document.querySelectorAll('button, input')) control.disabled = busy
  32. status.textContent = statusMessage
  33. }
  34. async function render() {
  35. const backend = await api.backend.status()
  36. document.querySelector('#recovery').hidden = backend.phase !== 'error'
  37. document.querySelector('#startup-error').textContent = backend.phase === 'error' ? backend.message : ''
  38. const plugins = await api.plugins.list()
  39. list.replaceChildren(...plugins.map(plugin => {
  40. const item = document.createElement('li')
  41. const identity = document.createElement('span')
  42. const version = document.createElement('span')
  43. version.className = 'package-version'
  44. version.textContent = plugin.enabled ? plugin.version : `${plugin.version} · ${messages.disabled}`
  45. identity.append(document.createTextNode(plugin.name), version)
  46. const remove = document.createElement('button')
  47. remove.type = 'button'
  48. remove.textContent = messages.remove
  49. remove.addEventListener('click', () => void run(
  50. () => api.plugins.remove(plugin.name),
  51. message('removing', { name: plugin.name }),
  52. ))
  53. const update = document.createElement('button')
  54. update.type = 'button'
  55. update.textContent = messages.update
  56. update.addEventListener('click', () => {
  57. updateName = plugin.name
  58. updateTrigger = update
  59. document.querySelector('#update-label').textContent = message('targetVersion', { name: plugin.name })
  60. updateVersion.value = plugin.version
  61. updateForm.hidden = false
  62. updateVersion.focus()
  63. updateVersion.select()
  64. })
  65. const actions = document.createElement('span')
  66. actions.className = 'package-actions'
  67. const toggle = document.createElement('button')
  68. toggle.type = 'button'
  69. toggle.textContent = plugin.enabled ? messages.disable : messages.enable
  70. toggle.addEventListener('click', () => void run(
  71. () => api.plugins.toggle(plugin.name, !plugin.enabled), messages.changingActivation,
  72. ))
  73. actions.append(toggle, update, remove)
  74. item.append(identity, actions)
  75. return item
  76. }))
  77. empty.hidden = plugins.length !== 0
  78. }
  79. async function run(operation, statusMessage) {
  80. setBusy(true, statusMessage)
  81. try {
  82. await operation()
  83. await render()
  84. status.textContent = messages.operationComplete
  85. } catch (error) {
  86. status.textContent = error instanceof Error ? error.message : String(error)
  87. } finally {
  88. setBusy(false, status.textContent)
  89. }
  90. }
  91. async function load(statusMessage, success) {
  92. setBusy(true, statusMessage)
  93. try {
  94. await render()
  95. status.textContent = success
  96. } catch (error) {
  97. status.textContent = error instanceof Error ? error.message : String(error)
  98. } finally {
  99. setBusy(false, status.textContent)
  100. }
  101. }
  102. form.addEventListener('submit', (event) => {
  103. event.preventDefault()
  104. const spec = input.value.trim()
  105. if (spec === '') return
  106. void run(async () => {
  107. await api.plugins.add(spec)
  108. input.value = ''
  109. }, message('installing', { spec }))
  110. })
  111. updateForm.addEventListener('submit', (event) => {
  112. event.preventDefault()
  113. const version = updateVersion.value.trim()
  114. if (version === '') return
  115. const name = updateName
  116. updateForm.hidden = true
  117. void run(() => api.plugins.update(name, version), message('updating', { name }))
  118. })
  119. document.querySelector('#update-cancel').addEventListener('click', () => {
  120. updateForm.hidden = true
  121. updateTrigger.focus()
  122. })
  123. document.querySelector('#retry').addEventListener('click', () => void run(() => api.backend.retry(), messages.retry))
  124. document.querySelector('#disable-all').addEventListener('click', () => void run(() => api.plugins.disableAll(), messages.changingActivation))
  125. refresh.addEventListener('click', () => void load(messages.refreshing, messages.refreshed))
  126. await load(messages.loadingPlugins, '')
  127. }
  128. void main()