feat: plugins v1.5
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
Output,
|
||||
computed,
|
||||
inject,
|
||||
input,
|
||||
signal
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
@@ -21,13 +22,14 @@ import {
|
||||
lucideStore,
|
||||
lucideX
|
||||
} from '@ng-icons/lucide';
|
||||
import type { PluginCapabilityId } from '../../../../shared-kernel';
|
||||
import type { PluginCapabilityId, TojuPluginInstallScope } from '../../../../shared-kernel';
|
||||
import { PluginCapabilityService } from '../../application/services/plugin-capability.service';
|
||||
import { PluginHostService } from '../../application/services/plugin-host.service';
|
||||
import { PluginLoggerService } from '../../application/services/plugin-logger.service';
|
||||
import { PluginRegistryService } from '../../application/services/plugin-registry.service';
|
||||
import { PluginRequirementStateService } from '../../application/services/plugin-requirement-state.service';
|
||||
import { PluginUiRegistryService } from '../../application/services/plugin-ui-registry.service';
|
||||
import { getPluginInstallScope } from '../../domain/logic/plugin-install-scope.logic';
|
||||
import type { RegisteredPlugin } from '../../domain/models/plugin-runtime.models';
|
||||
import { PluginRenderHostComponent } from '../plugin-render-host/plugin-render-host.component';
|
||||
|
||||
@@ -60,6 +62,8 @@ type PluginManagerTab = 'docs' | 'extensions' | 'installed' | 'logs' | 'requirem
|
||||
export class PluginManagerComponent {
|
||||
@Output() readonly closed = new EventEmitter<void>();
|
||||
|
||||
readonly scope = input<TojuPluginInstallScope>('client');
|
||||
|
||||
readonly capabilities = inject(PluginCapabilityService);
|
||||
readonly host = inject(PluginHostService);
|
||||
readonly logger = inject(PluginLoggerService);
|
||||
@@ -71,7 +75,12 @@ export class PluginManagerComponent {
|
||||
readonly busyPluginId = signal<string | null>(null);
|
||||
readonly busyAll = signal(false);
|
||||
readonly selectedPluginId = signal<string | null>(null);
|
||||
readonly entries = this.registry.entries;
|
||||
readonly allEntries = this.registry.entries;
|
||||
readonly entries = computed(() => this.allEntries().filter((entry) => this.entryBelongsToScope(entry)));
|
||||
readonly managerTitle = computed(() => this.scope() === 'server' ? 'Server plugins' : 'Client plugins');
|
||||
readonly managerDescription = computed(() => this.scope() === 'server'
|
||||
? 'Plugins installed for the current chat server.'
|
||||
: 'Global client plugins installed on this device.');
|
||||
readonly selectedPlugin = computed(() => {
|
||||
const selectedPluginId = this.selectedPluginId();
|
||||
|
||||
@@ -89,17 +98,18 @@ export class PluginManagerComponent {
|
||||
.slice(-20) : [];
|
||||
});
|
||||
readonly extensionCounts = computed(() => ({
|
||||
appPages: this.uiRegistry.appPages().length,
|
||||
channelSections: this.uiRegistry.channelSections().length,
|
||||
composerActions: this.uiRegistry.composerActions().length,
|
||||
embeds: this.uiRegistry.embeds().length,
|
||||
profileActions: this.uiRegistry.profileActions().length,
|
||||
settingsPages: this.uiRegistry.settingsPages().length,
|
||||
sidePanels: this.uiRegistry.sidePanels().length,
|
||||
toolbarActions: this.uiRegistry.toolbarActions().length
|
||||
appPages: this.uiRegistry.appPageRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
|
||||
channelSections: this.uiRegistry.channelSectionRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
|
||||
composerActions: this.uiRegistry.composerActionRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
|
||||
embeds: this.uiRegistry.embedRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
|
||||
profileActions: this.uiRegistry.profileActionRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
|
||||
settingsPages: this.uiRegistry.settingsPageRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
|
||||
sidePanels: this.uiRegistry.sidePanelRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
|
||||
toolbarActions: this.uiRegistry.toolbarActionRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length
|
||||
}));
|
||||
readonly requirementComparisons = this.requirementState.comparisons;
|
||||
readonly uiConflicts = this.uiRegistry.conflicts;
|
||||
readonly requirementComparisons = computed(() => this.scope() === 'server' ? this.requirementState.comparisons() : []);
|
||||
readonly uiConflicts = computed(() => this.uiRegistry.conflicts()
|
||||
.filter((conflict) => conflict.pluginIds.some((pluginId) => this.hasVisiblePlugin(pluginId))));
|
||||
readonly selectedRequirement = computed(() => {
|
||||
const selectedPlugin = this.selectedPlugin();
|
||||
|
||||
@@ -113,6 +123,10 @@ export class PluginManagerComponent {
|
||||
? this.uiRegistry.settingsPageRecords().filter((record) => record.pluginId === selectedPlugin.manifest.id)
|
||||
: [];
|
||||
});
|
||||
readonly emptyTitle = computed(() => this.scope() === 'server' ? 'No server plugins installed.' : 'No client plugins installed.');
|
||||
readonly emptyBody = computed(() => this.scope() === 'server'
|
||||
? 'Server-scoped plugins use scope: server in toju-plugin.json.'
|
||||
: 'Client-scoped plugins use scope: client or omit scope in toju-plugin.json.');
|
||||
readonly selectedDocs = computed(() => {
|
||||
const manifest = this.selectedPlugin()?.manifest;
|
||||
|
||||
@@ -176,11 +190,21 @@ export class PluginManagerComponent {
|
||||
}
|
||||
}
|
||||
|
||||
async activate(entry: RegisteredPlugin): Promise<void> {
|
||||
this.busyPluginId.set(entry.manifest.id);
|
||||
|
||||
try {
|
||||
await this.host.activatePluginById(entry.manifest.id);
|
||||
} finally {
|
||||
this.busyPluginId.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
async unload(entry: RegisteredPlugin): Promise<void> {
|
||||
this.busyPluginId.set(entry.manifest.id);
|
||||
|
||||
try {
|
||||
await this.host.deactivatePlugin(entry.manifest.id);
|
||||
await this.host.deactivatePlugin(entry.manifest.id, { forgetActivation: true });
|
||||
} finally {
|
||||
this.busyPluginId.set(null);
|
||||
}
|
||||
@@ -194,6 +218,10 @@ export class PluginManagerComponent {
|
||||
return this.selectedPlugin()?.manifest.id === entry.manifest.id;
|
||||
}
|
||||
|
||||
isActive(entry: RegisteredPlugin): boolean {
|
||||
return this.host.isPluginActive(entry.manifest.id);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.closed.emit();
|
||||
}
|
||||
@@ -205,4 +233,12 @@ export class PluginManagerComponent {
|
||||
trackCapability(index: number, capability: PluginCapabilityId): string {
|
||||
return capability;
|
||||
}
|
||||
|
||||
private entryBelongsToScope(entry: RegisteredPlugin): boolean {
|
||||
return getPluginInstallScope(entry.manifest) === this.scope();
|
||||
}
|
||||
|
||||
private hasVisiblePlugin(pluginId: string): boolean {
|
||||
return this.entries().some((entry) => entry.manifest.id === pluginId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user