1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

Allow removing force-padding on monitor widgets + plugin load fixes

This commit is contained in:
bbedward
2025-10-06 11:32:25 -04:00
parent e24ddb804d
commit 11a1af89f4
10 changed files with 209 additions and 44 deletions

View File

@@ -204,7 +204,8 @@ Singleton {
"size": 20, "size": 20,
"selectedGpuIndex": 0, "selectedGpuIndex": 0,
"pciId": "", "pciId": "",
"mountPath": "/" "mountPath": "/",
"minimumWidth": true
} }
leftWidgetsModel.append(dummyItem) leftWidgetsModel.append(dummyItem)
centerWidgetsModel.append(dummyItem) centerWidgetsModel.append(dummyItem)
@@ -811,6 +812,7 @@ Singleton {
var selectedGpuIndex = typeof order[i] === "string" ? undefined : order[i].selectedGpuIndex var selectedGpuIndex = typeof order[i] === "string" ? undefined : order[i].selectedGpuIndex
var pciId = typeof order[i] === "string" ? undefined : order[i].pciId var pciId = typeof order[i] === "string" ? undefined : order[i].pciId
var mountPath = typeof order[i] === "string" ? undefined : order[i].mountPath var mountPath = typeof order[i] === "string" ? undefined : order[i].mountPath
var minimumWidth = typeof order[i] === "string" ? undefined : order[i].minimumWidth
var item = { var item = {
"widgetId": widgetId, "widgetId": widgetId,
"enabled": enabled "enabled": enabled
@@ -823,6 +825,8 @@ Singleton {
item.pciId = pciId item.pciId = pciId
if (mountPath !== undefined) if (mountPath !== undefined)
item.mountPath = mountPath item.mountPath = mountPath
if (minimumWidth !== undefined)
item.minimumWidth = minimumWidth
listModel.append(item) listModel.append(item)
} }

View File

@@ -827,6 +827,7 @@ Item {
return processListPopoutLoader.item return processListPopoutLoader.item
} }
parentScreen: barWindow.screen parentScreen: barWindow.screen
widgetData: parent.widgetData
toggleProcessList: () => { toggleProcessList: () => {
processListPopoutLoader.active = true processListPopoutLoader.active = true
return processListPopoutLoader.item?.toggle() return processListPopoutLoader.item?.toggle()
@@ -846,6 +847,7 @@ Item {
return processListPopoutLoader.item return processListPopoutLoader.item
} }
parentScreen: barWindow.screen parentScreen: barWindow.screen
widgetData: parent.widgetData
toggleProcessList: () => { toggleProcessList: () => {
processListPopoutLoader.active = true processListPopoutLoader.active = true
return processListPopoutLoader.item?.toggle() return processListPopoutLoader.item?.toggle()
@@ -875,6 +877,7 @@ Item {
return processListPopoutLoader.item return processListPopoutLoader.item
} }
parentScreen: barWindow.screen parentScreen: barWindow.screen
widgetData: parent.widgetData
toggleProcessList: () => { toggleProcessList: () => {
processListPopoutLoader.active = true processListPopoutLoader.active = true
return processListPopoutLoader.item?.toggle() return processListPopoutLoader.item?.toggle()

View File

@@ -65,6 +65,14 @@ Loader {
restoreMode: Binding.RestoreNone restoreMode: Binding.RestoreNone
} }
Binding {
target: root.item
when: root.item && "widgetData" in root.item
property: "widgetData"
value: root.widgetData
restoreMode: Binding.RestoreNone
}
onLoaded: { onLoaded: {
if (item) { if (item) {
contentItemReady(item) contentItemReady(item)

View File

@@ -17,6 +17,8 @@ Rectangle {
property var parentScreen: null property var parentScreen: null
property real barThickness: 48 property real barThickness: 48
property real widgetThickness: 30 property real widgetThickness: 30
property var widgetData: null
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
width: isVertical ? widgetThickness : (cpuContent.implicitWidth + horizontalPadding * 2) width: isVertical ? widgetThickness : (cpuContent.implicitWidth + horizontalPadding * 2)
@@ -141,7 +143,7 @@ Rectangle {
text: "100%" text: "100%"
} }
width: Math.max(cpuBaseline.width, paintedWidth) width: root.minimumWidth ? Math.max(cpuBaseline.width, paintedWidth) : paintedWidth
Behavior on width { Behavior on width {
NumberAnimation { NumberAnimation {

View File

@@ -17,6 +17,8 @@ Rectangle {
property var parentScreen: null property var parentScreen: null
property real barThickness: 48 property real barThickness: 48
property real widgetThickness: 30 property real widgetThickness: 30
property var widgetData: null
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
width: isVertical ? widgetThickness : (cpuTempContent.implicitWidth + horizontalPadding * 2) width: isVertical ? widgetThickness : (cpuTempContent.implicitWidth + horizontalPadding * 2)
@@ -141,7 +143,7 @@ Rectangle {
text: "100°" text: "100°"
} }
width: Math.max(tempBaseline.width, paintedWidth) width: root.minimumWidth ? Math.max(tempBaseline.width, paintedWidth) : paintedWidth
Behavior on width { Behavior on width {
NumberAnimation { NumberAnimation {

View File

@@ -19,6 +19,7 @@ Rectangle {
property real barThickness: 48 property real barThickness: 48
property real widgetThickness: 30 property real widgetThickness: 30
property int selectedGpuIndex: (widgetData && widgetData.selectedGpuIndex !== undefined) ? widgetData.selectedGpuIndex : 0 property int selectedGpuIndex: (widgetData && widgetData.selectedGpuIndex !== undefined) ? widgetData.selectedGpuIndex : 0
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
property real displayTemp: { property real displayTemp: {
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) { if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
@@ -209,7 +210,7 @@ Rectangle {
text: "100°" text: "100°"
} }
width: Math.max(gpuTempBaseline.width, paintedWidth) width: root.minimumWidth ? Math.max(gpuTempBaseline.width, paintedWidth) : paintedWidth
Behavior on width { Behavior on width {
NumberAnimation { NumberAnimation {

View File

@@ -17,6 +17,8 @@ Rectangle {
property var parentScreen: null property var parentScreen: null
property real barThickness: 48 property real barThickness: 48
property real widgetThickness: 30 property real widgetThickness: 30
property var widgetData: null
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
width: isVertical ? widgetThickness : (ramContent.implicitWidth + horizontalPadding * 2) width: isVertical ? widgetThickness : (ramContent.implicitWidth + horizontalPadding * 2)
@@ -30,6 +32,7 @@ Rectangle {
const baseColor = ramArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; const baseColor = ramArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
} }
Component.onCompleted: { Component.onCompleted: {
DgopService.addRef(["memory"]); DgopService.addRef(["memory"]);
} }
@@ -141,7 +144,7 @@ Rectangle {
text: "100%" text: "100%"
} }
width: Math.max(ramBaseline.width, paintedWidth) width: root.minimumWidth ? Math.max(ramBaseline.width, paintedWidth) : paintedWidth
Behavior on width { Behavior on width {
NumberAnimation { NumberAnimation {

View File

@@ -263,6 +263,9 @@ Item {
if (widgetId === "diskUsage") { if (widgetId === "diskUsage") {
widgetObj.mountPath = "/" widgetObj.mountPath = "/"
} }
if (widgetId === "cpuUsage" || widgetId === "memUsage" || widgetId === "cpuTemp" || widgetId === "gpuTemp") {
widgetObj.minimumWidth = true
}
var widgets = [] var widgets = []
if (targetSection === "left") { if (targetSection === "left") {
@@ -509,6 +512,54 @@ Item {
} }
} }
function handleMinimumWidthChanged(sectionId, widgetIndex, enabled) {
var widgets = []
if (sectionId === "left")
widgets = SettingsData.dankBarLeftWidgets.slice()
else if (sectionId === "center")
widgets = SettingsData.dankBarCenterWidgets.slice()
else if (sectionId === "right")
widgets = SettingsData.dankBarRightWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
var widget = widgets[widgetIndex]
if (typeof widget === "string") {
widgets[widgetIndex] = {
"id": widget,
"enabled": true,
"minimumWidth": enabled
}
} else {
var newWidget = {
"id": widget.id,
"enabled": widget.enabled,
"minimumWidth": enabled
}
if (widget.size !== undefined)
newWidget.size = widget.size
if (widget.selectedGpuIndex !== undefined)
newWidget.selectedGpuIndex = widget.selectedGpuIndex
if (widget.pciId !== undefined)
newWidget.pciId = widget.pciId
if (widget.mountPath !== undefined)
newWidget.mountPath = widget.mountPath
if (widget.id === "controlCenterButton") {
newWidget.showNetworkIcon = widget.showNetworkIcon !== undefined ? widget.showNetworkIcon : true
newWidget.showBluetoothIcon = widget.showBluetoothIcon !== undefined ? widget.showBluetoothIcon : true
newWidget.showAudioIcon = widget.showAudioIcon !== undefined ? widget.showAudioIcon : true
}
widgets[widgetIndex] = newWidget
}
}
if (sectionId === "left")
SettingsData.setDankBarLeftWidgets(widgets)
else if (sectionId === "center")
SettingsData.setDankBarCenterWidgets(widgets)
else if (sectionId === "right")
SettingsData.setDankBarRightWidgets(widgets)
}
function getItemsForSection(sectionId) { function getItemsForSection(sectionId) {
var widgets = [] var widgets = []
var widgetData = [] var widgetData = []
@@ -532,6 +583,7 @@ Item {
var widgetShowNetworkIcon = typeof widget === "string" ? undefined : widget.showNetworkIcon var widgetShowNetworkIcon = typeof widget === "string" ? undefined : widget.showNetworkIcon
var widgetShowBluetoothIcon = typeof widget === "string" ? undefined : widget.showBluetoothIcon var widgetShowBluetoothIcon = typeof widget === "string" ? undefined : widget.showBluetoothIcon
var widgetShowAudioIcon = typeof widget === "string" ? undefined : widget.showAudioIcon var widgetShowAudioIcon = typeof widget === "string" ? undefined : widget.showAudioIcon
var widgetMinimumWidth = typeof widget === "string" ? undefined : widget.minimumWidth
var widgetDef = baseWidgetDefinitions.find(w => { var widgetDef = baseWidgetDefinitions.find(w => {
return w.id === widgetId return w.id === widgetId
}) })
@@ -552,6 +604,8 @@ Item {
item.showBluetoothIcon = widgetShowBluetoothIcon item.showBluetoothIcon = widgetShowBluetoothIcon
if (widgetShowAudioIcon !== undefined) if (widgetShowAudioIcon !== undefined)
item.showAudioIcon = widgetShowAudioIcon item.showAudioIcon = widgetShowAudioIcon
if (widgetMinimumWidth !== undefined)
item.minimumWidth = widgetMinimumWidth
widgets.push(item) widgets.push(item)
} }
@@ -1208,6 +1262,10 @@ Item {
dankBarTab.handleDiskMountSelectionChanged( dankBarTab.handleDiskMountSelectionChanged(
sectionId, widgetIndex, mountPath) sectionId, widgetIndex, mountPath)
} }
onMinimumWidthChanged: (sectionId, widgetIndex, enabled) => {
dankBarTab.handleMinimumWidthChanged(
sectionId, widgetIndex, enabled)
}
} }
} }
@@ -1280,6 +1338,10 @@ Item {
dankBarTab.handleDiskMountSelectionChanged( dankBarTab.handleDiskMountSelectionChanged(
sectionId, widgetIndex, mountPath) sectionId, widgetIndex, mountPath)
} }
onMinimumWidthChanged: (sectionId, widgetIndex, enabled) => {
dankBarTab.handleMinimumWidthChanged(
sectionId, widgetIndex, enabled)
}
} }
} }
@@ -1352,6 +1414,10 @@ Item {
dankBarTab.handleDiskMountSelectionChanged( dankBarTab.handleDiskMountSelectionChanged(
sectionId, widgetIndex, mountPath) sectionId, widgetIndex, mountPath)
} }
onMinimumWidthChanged: (sectionId, widgetIndex, enabled) => {
dankBarTab.handleMinimumWidthChanged(
sectionId, widgetIndex, enabled)
}
} }
} }
} }

View File

@@ -22,6 +22,7 @@ Column {
signal gpuSelectionChanged(string sectionId, int widgetIndex, int selectedIndex) signal gpuSelectionChanged(string sectionId, int widgetIndex, int selectedIndex)
signal diskMountSelectionChanged(string sectionId, int widgetIndex, string mountPath) signal diskMountSelectionChanged(string sectionId, int widgetIndex, string mountPath)
signal controlCenterSettingChanged(string sectionId, int widgetIndex, string settingName, bool value) signal controlCenterSettingChanged(string sectionId, int widgetIndex, string settingName, bool value)
signal minimumWidthChanged(string sectionId, int widgetIndex, bool enabled)
width: parent.width width: parent.width
height: implicitHeight height: implicitHeight
@@ -283,6 +284,37 @@ Column {
} }
} }
DankActionButton {
id: minimumWidthButton
buttonSize: 28
visible: modelData.id === "cpuUsage"
|| modelData.id === "memUsage"
|| modelData.id === "cpuTemp"
|| modelData.id === "gpuTemp"
iconName: "straighten"
iconSize: 16
iconColor: (modelData.minimumWidth !== undefined ? modelData.minimumWidth : true) ? Theme.primary : Theme.outline
onClicked: {
var currentEnabled = modelData.minimumWidth !== undefined ? modelData.minimumWidth : true
root.minimumWidthChanged(root.sectionId, index, !currentEnabled)
}
onEntered: {
minimumWidthTooltipLoader.active = true
if (minimumWidthTooltipLoader.item) {
var currentEnabled = modelData.minimumWidth !== undefined ? modelData.minimumWidth : true
const tooltipText = currentEnabled ? "Force Padding" : "Dynamic Width"
const p = minimumWidthButton.mapToItem(null, minimumWidthButton.width / 2, 0)
minimumWidthTooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
}
}
onExited: {
if (minimumWidthTooltipLoader.item) {
minimumWidthTooltipLoader.item.hide()
}
minimumWidthTooltipLoader.active = false
}
}
Row { Row {
spacing: Theme.spacingXS spacing: Theme.spacingXS
visible: modelData.id === "clock" visible: modelData.id === "clock"
@@ -892,4 +924,10 @@ Column {
active: false active: false
sourceComponent: DankTooltip {} sourceComponent: DankTooltip {}
} }
Loader {
id: minimumWidthTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
} }

View File

@@ -41,6 +41,7 @@ Singleton {
property int currentScanIndex: 0 property int currentScanIndex: 0
property var scanResults: [] property var scanResults: []
property var foundPlugins: ({})
property var lsProcess: Process { property var lsProcess: Process {
id: dirScanner id: dirScanner
@@ -68,12 +69,14 @@ Singleton {
scanNextDirectory() scanNextDirectory()
} else { } else {
currentScanIndex = 0 currentScanIndex = 0
cleanupRemovedPlugins()
} }
} }
} }
function scanPlugins() { function scanPlugins() {
currentScanIndex = 0 currentScanIndex = 0
foundPlugins = {}
scanNextDirectory() scanNextDirectory()
} }
@@ -88,39 +91,57 @@ Singleton {
function loadPluginManifest(manifestPath) { function loadPluginManifest(manifestPath) {
var readerId = "reader_" + Date.now() + "_" + Math.random() var readerId = "reader_" + Date.now() + "_" + Math.random()
var catProcess = Qt.createComponent("data:text/plain,import Quickshell.Io; Process { stdout: StdioCollector { } }") var checkProcess = Qt.createComponent("data:text/plain,import Quickshell.Io; Process { stdout: StdioCollector { } }")
if (catProcess.status === Component.Ready) { if (checkProcess.status === Component.Ready) {
var process = catProcess.createObject(root) var checker = checkProcess.createObject(root)
process.command = ["cat", manifestPath] checker.command = ["test", "-f", manifestPath]
process.stdout.streamFinished.connect(function() { checker.exited.connect(function(exitCode) {
try {
var manifest = JSON.parse(process.stdout.text.trim())
processManifest(manifest, manifestPath)
} catch (e) {
console.error("PluginService: Failed to parse manifest", manifestPath, ":", e.message)
}
process.destroy()
delete manifestReaders[readerId]
})
process.exited.connect(function(exitCode) {
if (exitCode !== 0) { if (exitCode !== 0) {
console.error("PluginService: Failed to read manifest file:", manifestPath, "exit code:", exitCode) checker.destroy()
process.destroy()
delete manifestReaders[readerId] delete manifestReaders[readerId]
return
} }
var catProcess = Qt.createComponent("data:text/plain,import Quickshell.Io; Process { stdout: StdioCollector { } }")
if (catProcess.status === Component.Ready) {
var process = catProcess.createObject(root)
process.command = ["cat", manifestPath]
process.stdout.streamFinished.connect(function() {
try {
var manifest = JSON.parse(process.stdout.text.trim())
processManifest(manifest, manifestPath)
} catch (e) {
console.error("PluginService: Failed to parse manifest", manifestPath, ":", e.message)
}
process.destroy()
delete manifestReaders[readerId]
})
process.exited.connect(function(exitCode) {
if (exitCode !== 0) {
console.error("PluginService: Failed to read manifest file:", manifestPath, "exit code:", exitCode)
process.destroy()
delete manifestReaders[readerId]
}
})
manifestReaders[readerId] = process
process.running = true
} else {
console.error("PluginService: Failed to create manifest reader process")
}
checker.destroy()
}) })
manifestReaders[readerId] = process manifestReaders[readerId] = checker
process.running = true checker.running = true
} else { } else {
console.error("PluginService: Failed to create manifest reader process") console.error("PluginService: Failed to create file check process")
} }
} }
function processManifest(manifest, manifestPath) { function processManifest(manifest, manifestPath) {
registerPlugin(manifest, manifestPath) registerPlugin(manifest, manifestPath)
// Auto-load plugin if it's enabled in settings (default to enabled) var enabled = SettingsData.getPluginSetting(manifest.id, "enabled", false)
var enabled = SettingsData.getPluginSetting(manifest.id, "enabled", true)
if (enabled) { if (enabled) {
loadPlugin(manifest.id) loadPlugin(manifest.id)
} }
@@ -156,7 +177,10 @@ Singleton {
pluginInfo.loaded = false pluginInfo.loaded = false
pluginInfo.type = manifest.type || "widget" pluginInfo.type = manifest.type || "widget"
availablePlugins[manifest.id] = pluginInfo var newPlugins = Object.assign({}, availablePlugins)
newPlugins[manifest.id] = pluginInfo
availablePlugins = newPlugins
foundPlugins[manifest.id] = true
} }
function hasPermission(pluginId, permission) { function hasPermission(pluginId, permission) {
@@ -168,6 +192,27 @@ Singleton {
return permissions.indexOf(permission) !== -1 return permissions.indexOf(permission) !== -1
} }
function cleanupRemovedPlugins() {
var pluginsToRemove = []
for (var pluginId in availablePlugins) {
if (!foundPlugins[pluginId]) {
pluginsToRemove.push(pluginId)
}
}
if (pluginsToRemove.length > 0) {
var newPlugins = Object.assign({}, availablePlugins)
for (var i = 0; i < pluginsToRemove.length; i++) {
var pluginId = pluginsToRemove[i]
if (isPluginLoaded(pluginId)) {
unloadPlugin(pluginId)
}
delete newPlugins[pluginId]
}
availablePlugins = newPlugins
}
}
function loadPlugin(pluginId) { function loadPlugin(pluginId) {
var plugin = availablePlugins[pluginId] var plugin = availablePlugins[pluginId]
if (!plugin) { if (!plugin) {
@@ -184,14 +229,15 @@ Singleton {
var componentMap = isDaemon ? pluginDaemonComponents : pluginWidgetComponents var componentMap = isDaemon ? pluginDaemonComponents : pluginWidgetComponents
if (componentMap[pluginId]) { if (componentMap[pluginId]) {
var oldComponent = componentMap[pluginId] componentMap[pluginId]?.destroy()
if (oldComponent) {
oldComponent.destroy()
}
if (isDaemon) { if (isDaemon) {
delete pluginDaemonComponents[pluginId] var newDaemons = Object.assign({}, pluginDaemonComponents)
delete newDaemons[pluginId]
pluginDaemonComponents = newDaemons
} else { } else {
delete pluginWidgetComponents[pluginId] var newComponents = Object.assign({}, pluginWidgetComponents)
delete newComponents[pluginId]
pluginWidgetComponents = newComponents
} }
} }
@@ -204,7 +250,6 @@ Singleton {
if (component.status === Component.Error) { if (component.status === Component.Error) {
console.error("PluginService: Failed to create component for plugin:", pluginId, "Error:", component.errorString()) console.error("PluginService: Failed to create component for plugin:", pluginId, "Error:", component.errorString())
pluginLoadFailed(pluginId, component.errorString()) pluginLoadFailed(pluginId, component.errorString())
component.destroy()
} }
}) })
} }
@@ -212,7 +257,6 @@ Singleton {
if (component.status === Component.Error) { if (component.status === Component.Error) {
console.error("PluginService: Failed to create component for plugin:", pluginId, "Error:", component.errorString()) console.error("PluginService: Failed to create component for plugin:", pluginId, "Error:", component.errorString())
pluginLoadFailed(pluginId, component.errorString()) pluginLoadFailed(pluginId, component.errorString())
component.destroy()
return false return false
} }
@@ -250,18 +294,12 @@ Singleton {
var isDaemon = plugin.type === "daemon" var isDaemon = plugin.type === "daemon"
if (isDaemon && pluginDaemonComponents[pluginId]) { if (isDaemon && pluginDaemonComponents[pluginId]) {
var daemonComponent = pluginDaemonComponents[pluginId] pluginDaemonComponents[pluginId]?.destroy()
if (daemonComponent) {
daemonComponent.destroy()
}
var newDaemons = Object.assign({}, pluginDaemonComponents) var newDaemons = Object.assign({}, pluginDaemonComponents)
delete newDaemons[pluginId] delete newDaemons[pluginId]
pluginDaemonComponents = newDaemons pluginDaemonComponents = newDaemons
} else if (pluginWidgetComponents[pluginId]) { } else if (pluginWidgetComponents[pluginId]) {
var component = pluginWidgetComponents[pluginId] pluginWidgetComponents[pluginId]?.destroy()
if (component) {
component.destroy()
}
var newComponents = Object.assign({}, pluginWidgetComponents) var newComponents = Object.assign({}, pluginWidgetComponents)
delete newComponents[pluginId] delete newComponents[pluginId]
pluginWidgetComponents = newComponents pluginWidgetComponents = newComponents