plugin-manager.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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. const list = document.querySelector('#plugins')
  16. const empty = document.querySelector('#empty')
  17. const status = document.querySelector('#status')
  18. const form = document.querySelector('#install-form')
  19. const input = document.querySelector('#package-spec')
  20. const refresh = document.querySelector('#refresh')
  21. const updateForm = document.querySelector('#update-form')
  22. const updateVersion = document.querySelector('#update-version')
  23. let updateName = ''
  24. let updateTrigger
  25. document.querySelector('#update-submit').textContent = messages.update
  26. document.querySelector('#update-cancel').textContent = messages.cancel
  27. function setBusy(busy, statusMessage = '') {
  28. for (const control of document.querySelectorAll('button, input')) control.disabled = busy
  29. status.textContent = statusMessage
  30. }
  31. async function render() {
  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. updateName = plugin.name
  52. updateTrigger = update
  53. document.querySelector('#update-label').textContent = message('targetVersion', { name: plugin.name })
  54. updateVersion.value = plugin.version
  55. updateForm.hidden = false
  56. updateVersion.focus()
  57. updateVersion.select()
  58. })
  59. const actions = document.createElement('span')
  60. actions.className = 'package-actions'
  61. const toggle = document.createElement('button')
  62. toggle.type = 'button'
  63. toggle.textContent = plugin.enabled ? messages.disable : messages.enable
  64. toggle.addEventListener('click', () => void run(
  65. () => api.plugins.toggle(plugin.name, !plugin.enabled), messages.changingActivation,
  66. ))
  67. actions.append(toggle, update, remove)
  68. item.append(identity, actions)
  69. return item
  70. }))
  71. empty.hidden = plugins.length !== 0
  72. }
  73. async function run(operation, statusMessage) {
  74. setBusy(true, statusMessage)
  75. try {
  76. await operation()
  77. await render()
  78. status.textContent = messages.operationComplete
  79. } catch (error) {
  80. status.textContent = error instanceof Error ? error.message : String(error)
  81. } finally {
  82. setBusy(false, status.textContent)
  83. }
  84. }
  85. async function load(statusMessage, success) {
  86. setBusy(true, statusMessage)
  87. try {
  88. await render()
  89. status.textContent = success
  90. } catch (error) {
  91. status.textContent = error instanceof Error ? error.message : String(error)
  92. } finally {
  93. setBusy(false, status.textContent)
  94. }
  95. }
  96. form.addEventListener('submit', (event) => {
  97. event.preventDefault()
  98. const spec = input.value.trim()
  99. if (spec === '') return
  100. void run(async () => {
  101. await api.plugins.add(spec)
  102. input.value = ''
  103. }, message('installing', { spec }))
  104. })
  105. updateForm.addEventListener('submit', (event) => {
  106. event.preventDefault()
  107. const version = updateVersion.value.trim()
  108. if (version === '') return
  109. const name = updateName
  110. updateForm.hidden = true
  111. void run(() => api.plugins.update(name, version), message('updating', { name }))
  112. })
  113. document.querySelector('#update-cancel').addEventListener('click', () => {
  114. updateForm.hidden = true
  115. updateTrigger.focus()
  116. })
  117. refresh.addEventListener('click', () => void load(messages.refreshing, messages.refreshed))
  118. await load(messages.loadingPlugins, '')
  119. }
  120. void main()