plugin-manager.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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. function setBusy(busy, statusMessage = '') {
  25. for (const control of document.querySelectorAll('button, input')) control.disabled = busy
  26. status.textContent = statusMessage
  27. }
  28. async function render() {
  29. const backend = await api.backend.status()
  30. document.querySelector('#recovery').hidden = backend.phase !== 'error'
  31. document.querySelector('#startup-error').textContent = backend.phase === 'error' ? backend.message : ''
  32. const plugins = await api.plugins.list()
  33. list.replaceChildren(...plugins.map(plugin => {
  34. const item = document.createElement('li')
  35. const identity = document.createElement('span')
  36. const version = document.createElement('span')
  37. version.className = 'package-version'
  38. version.textContent = plugin.enabled ? plugin.version : `${plugin.version} · ${messages.disabled}`
  39. identity.append(document.createTextNode(plugin.name), version)
  40. const remove = document.createElement('button')
  41. remove.type = 'button'
  42. remove.textContent = messages.remove
  43. remove.addEventListener('click', () => void run(
  44. () => api.plugins.remove(plugin.name),
  45. message('removing', { name: plugin.name }),
  46. ))
  47. const update = document.createElement('button')
  48. update.type = 'button'
  49. update.textContent = messages.update
  50. update.addEventListener('click', () => {
  51. const next = window.prompt(message('targetVersion', { name: plugin.name }), plugin.version)?.trim()
  52. if (next === undefined || next === '' || next === plugin.version) return
  53. void run(() => api.plugins.update(plugin.name, next), message('updating', { name: plugin.name }))
  54. })
  55. const actions = document.createElement('span')
  56. actions.className = 'package-actions'
  57. const toggle = document.createElement('button')
  58. toggle.type = 'button'
  59. toggle.textContent = plugin.enabled ? messages.disable : messages.enable
  60. toggle.addEventListener('click', () => void run(
  61. () => api.plugins.toggle(plugin.name, !plugin.enabled), messages.changingActivation,
  62. ))
  63. actions.append(toggle, update, remove)
  64. item.append(identity, actions)
  65. return item
  66. }))
  67. empty.hidden = plugins.length !== 0
  68. }
  69. async function run(operation, statusMessage) {
  70. setBusy(true, statusMessage)
  71. try {
  72. await operation()
  73. await render()
  74. status.textContent = messages.operationComplete
  75. } catch (error) {
  76. status.textContent = error instanceof Error ? error.message : String(error)
  77. } finally {
  78. setBusy(false, status.textContent)
  79. }
  80. }
  81. async function load(statusMessage, success) {
  82. setBusy(true, statusMessage)
  83. try {
  84. await render()
  85. status.textContent = success
  86. } catch (error) {
  87. status.textContent = error instanceof Error ? error.message : String(error)
  88. } finally {
  89. setBusy(false, status.textContent)
  90. }
  91. }
  92. form.addEventListener('submit', (event) => {
  93. event.preventDefault()
  94. const spec = input.value.trim()
  95. if (spec === '') return
  96. void run(async () => {
  97. await api.plugins.add(spec)
  98. input.value = ''
  99. }, message('installing', { spec }))
  100. })
  101. document.querySelector('#retry').addEventListener('click', () => void run(() => api.backend.retry(), messages.retry))
  102. document.querySelector('#disable-all').addEventListener('click', () => void run(() => api.plugins.disableAll(), messages.changingActivation))
  103. refresh.addEventListener('click', () => void load(messages.refreshing, messages.refreshed))
  104. await load(messages.loadingPlugins, '')
  105. }
  106. void main()