import QtQuick import qs.Common import qs.Services import qs.Widgets FocusScope { id: pluginsTab property string expandedPluginId: "" property bool isRefreshingPlugins: false property var parentModal: null property var installedPluginsData: ({}) property bool isReloading: false property alias sharedTooltip: sharedTooltip focus: true DankTooltipV2 { id: sharedTooltip } DankFlickable { anchors.fill: parent clip: true contentHeight: mainColumn.height + Theme.spacingXL contentWidth: width Column { id: mainColumn width: Math.min(550, parent.width - Theme.spacingL * 2) anchors.horizontalCenter: parent.horizontalCenter spacing: Theme.spacingXL StyledRect { width: parent.width height: headerColumn.implicitHeight + Theme.spacingL * 2 radius: Theme.cornerRadius color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) border.width: 0 Column { id: headerColumn anchors.fill: parent anchors.margins: Theme.spacingL spacing: Theme.spacingM Row { width: parent.width spacing: Theme.spacingM DankIcon { name: "extension" size: Theme.iconSize color: Theme.primary anchors.verticalCenter: parent.verticalCenter } Column { anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingXS StyledText { text: I18n.tr("Plugin Management") font.pixelSize: Theme.fontSizeLarge color: Theme.surfaceText font.weight: Font.Medium } StyledText { text: I18n.tr("Manage and configure plugins for extending DMS functionality") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText } } } StyledRect { width: parent.width height: dmsWarningColumn.implicitHeight + Theme.spacingM * 2 radius: Theme.cornerRadius color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.1) border.color: Theme.warning border.width: 1 visible: !DMSService.dmsAvailable Column { id: dmsWarningColumn anchors.fill: parent anchors.margins: Theme.spacingM spacing: Theme.spacingXS Row { spacing: Theme.spacingXS DankIcon { name: "warning" size: 16 color: Theme.warning anchors.verticalCenter: parent.verticalCenter } StyledText { text: I18n.tr("DMS Plugin Manager Unavailable") font.pixelSize: Theme.fontSizeSmall color: Theme.warning font.weight: Font.Medium anchors.verticalCenter: parent.verticalCenter } } StyledText { text: I18n.tr("The DMS_SOCKET environment variable is not set or the socket is unavailable. Automated plugin management requires the DMS_SOCKET.") font.pixelSize: Theme.fontSizeSmall - 1 color: Theme.surfaceVariantText wrapMode: Text.WordWrap width: parent.width } } } Flow { width: parent.width spacing: Theme.spacingM DankButton { text: I18n.tr("Browse") iconName: "store" enabled: DMSService.dmsAvailable onClicked: { pluginBrowser.show(); } } DankButton { text: I18n.tr("Scan") iconName: "refresh" onClicked: { pluginsTab.isRefreshingPlugins = true; PluginService.scanPlugins(); if (DMSService.dmsAvailable) { DMSService.listInstalled(); } pluginsTab.refreshPluginList(); } } DankButton { text: I18n.tr("Create Dir") iconName: "create_new_folder" onClicked: { PluginService.createPluginDirectory(); ToastService.showInfo("Created plugin directory: " + PluginService.pluginDirectory); } } } } } StyledRect { width: parent.width height: directoryColumn.implicitHeight + Theme.spacingL * 2 radius: Theme.cornerRadius color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) border.width: 0 Column { id: directoryColumn anchors.fill: parent anchors.margins: Theme.spacingL spacing: Theme.spacingM StyledText { text: I18n.tr("Plugin Directory") font.pixelSize: Theme.fontSizeLarge color: Theme.surfaceText font.weight: Font.Medium } StyledText { text: PluginService.pluginDirectory font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText font.family: "monospace" } StyledText { text: I18n.tr("Place plugin directories here. Each plugin should have a plugin.json manifest file.") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText wrapMode: Text.WordWrap width: parent.width } } } StyledRect { width: parent.width height: Math.max(200, availableColumn.implicitHeight + Theme.spacingL * 2) radius: Theme.cornerRadius color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) border.width: 0 Column { id: availableColumn anchors.fill: parent anchors.margins: Theme.spacingL spacing: Theme.spacingM StyledText { text: I18n.tr("Available Plugins") font.pixelSize: Theme.fontSizeLarge color: Theme.surfaceText font.weight: Font.Medium } Column { width: parent.width spacing: Theme.spacingM Repeater { id: pluginRepeater model: PluginService.availablePluginsList PluginListItem { pluginData: modelData expandedPluginId: pluginsTab.expandedPluginId hasUpdate: { if (DMSService.apiVersion < 8) return false; return pluginsTab.installedPluginsData[pluginId] || pluginsTab.installedPluginsData[pluginName] || false; } isReloading: pluginsTab.isReloading sharedTooltip: pluginsTab.sharedTooltip onExpandedPluginIdChanged: { pluginsTab.expandedPluginId = expandedPluginId; } onIsReloadingChanged: { pluginsTab.isReloading = isReloading; } } } StyledText { width: parent.width text: I18n.tr("No plugins found.") + "\n" + I18n.tr("Place plugins in") + " " + PluginService.pluginDirectory font.pixelSize: Theme.fontSizeMedium color: Theme.surfaceVariantText horizontalAlignment: Text.AlignHCenter visible: pluginRepeater.model && pluginRepeater.model.length === 0 } } } } } } function refreshPluginList() { pluginsTab.isRefreshingPlugins = false; } Connections { target: PluginService function onPluginLoaded() { refreshPluginList(); if (isReloading) { isReloading = false; } } function onPluginUnloaded() { refreshPluginList(); if (!isReloading && pluginsTab.expandedPluginId !== "" && !PluginService.isPluginLoaded(pluginsTab.expandedPluginId)) { pluginsTab.expandedPluginId = ""; } } function onPluginListUpdated() { if (DMSService.apiVersion >= 8) { DMSService.listInstalled(); } refreshPluginList(); } } Connections { target: DMSService function onPluginsListReceived(plugins) { pluginBrowser.isLoading = false; pluginBrowser.allPlugins = plugins; pluginBrowser.updateFilteredPlugins(); } function onInstalledPluginsReceived(plugins) { var pluginMap = {}; for (var i = 0; i < plugins.length; i++) { var plugin = plugins[i]; var hasUpdate = plugin.hasUpdate || false; if (plugin.id) { pluginMap[plugin.id] = hasUpdate; } if (plugin.name) { pluginMap[plugin.name] = hasUpdate; } } installedPluginsData = pluginMap; Qt.callLater(refreshPluginList); } function onOperationSuccess(message) { ToastService.showInfo(message); } function onOperationError(error) { ToastService.showError(error); } } Component.onCompleted: { pluginBrowser.parentModal = pluginsTab.parentModal; if (DMSService.dmsAvailable && DMSService.apiVersion >= 8) DMSService.listInstalled(); } PluginBrowser { id: pluginBrowser } }