refactor: use PluginLog for all logging in plugins directory

- Replaced all Log.* calls with PluginLog.* in plugins directory
- Modified 14 files with 179 total changes
- Removed unused Log imports
- All plugin-related logs now have [plugin] context prefix

This ensures consistent context-aware logging for all plugin operations,
making it easier to filter and debug plugin-related issues.
This commit is contained in:
Johannes Millan 2025-07-10 14:32:37 +02:00
parent fd099ed4a1
commit 5007038fe8
16 changed files with 370 additions and 196 deletions

146
scripts/migrate-to-plugin-log.ts Executable file
View file

@ -0,0 +1,146 @@
#!/usr/bin/env ts-node
import * as fs from 'fs';
import * as path from 'path';
import * as glob from 'glob';
interface Replacement {
pattern: RegExp;
replacement: string;
}
const replacements: Replacement[] = [
// Replace Log.log with PluginLog.log
{ pattern: /\bLog\.log\(/g, replacement: 'PluginLog.log(' },
// Replace Log.err with PluginLog.err
{ pattern: /\bLog\.err\(/g, replacement: 'PluginLog.err(' },
// Replace Log.info with PluginLog.info
{ pattern: /\bLog\.info\(/g, replacement: 'PluginLog.info(' },
// Replace Log.debug with PluginLog.debug
{ pattern: /\bLog\.debug\(/g, replacement: 'PluginLog.debug(' },
// Replace Log.verbose with PluginLog.verbose
{ pattern: /\bLog\.verbose\(/g, replacement: 'PluginLog.verbose(' },
// Replace Log.critical with PluginLog.critical
{ pattern: /\bLog\.critical\(/g, replacement: 'PluginLog.critical(' },
];
function updateImports(content: string): string {
// Check if file already imports PluginLog
const hasPluginLogImport =
/import\s*{[^}]*\bPluginLog\b[^}]*}\s*from\s*['"][^'"]*\/log['"]/.test(content);
if (hasPluginLogImport) {
// If PluginLog is already imported, just remove Log from the import if it's not used elsewhere
return content;
}
// Find existing Log import and add PluginLog to it
const logImportRegex = /import\s*{([^}]*\bLog\b[^}]*)}\s*from\s*(['"][^'"]*\/log['"])/;
const match = content.match(logImportRegex);
if (match) {
const [fullMatch, imports, importPath] = match;
const importList = imports.split(',').map((s) => s.trim());
// Add PluginLog if not already there
if (!importList.includes('PluginLog')) {
importList.push('PluginLog');
}
// Check if Log is still used after replacements
let tempContent = content;
for (const { pattern, replacement } of replacements) {
tempContent = tempContent.replace(pattern, replacement);
}
// Remove the import statement from check
tempContent = tempContent.replace(logImportRegex, '');
// If Log is no longer used, remove it from imports
const logStillUsed = /\bLog\b/.test(tempContent);
if (!logStillUsed) {
const logIndex = importList.indexOf('Log');
if (logIndex > -1) {
importList.splice(logIndex, 1);
}
}
const newImports = importList.join(', ');
const newImportStatement = `import { ${newImports} } from ${importPath}`;
content = content.replace(fullMatch, newImportStatement);
}
return content;
}
function processFile(filePath: string): { modified: boolean; changes: number } {
try {
let content = fs.readFileSync(filePath, 'utf8');
const originalContent = content;
let changeCount = 0;
// Count and apply replacements
for (const { pattern, replacement } of replacements) {
const matches = content.match(pattern);
if (matches) {
changeCount += matches.length;
content = content.replace(pattern, replacement);
}
}
// Update imports if changes were made
if (changeCount > 0) {
content = updateImports(content);
}
const modified = content !== originalContent;
if (modified) {
fs.writeFileSync(filePath, content, 'utf8');
}
return { modified, changes: changeCount };
} catch (error) {
console.error(`Error processing ${filePath}:`, error);
return { modified: false, changes: 0 };
}
}
function main() {
console.log('Migrating Log to PluginLog in plugins directory...\n');
// Find all TypeScript files in plugins directory
const files = glob.sync('src/app/plugins/**/*.ts', {
ignore: ['**/*.spec.ts', '**/node_modules/**'],
absolute: true,
});
console.log(`Found ${files.length} TypeScript files in plugins directory\n`);
const modifiedFiles: { path: string; changes: number }[] = [];
let totalChanges = 0;
for (const file of files) {
const result = processFile(file);
if (result.modified) {
modifiedFiles.push({ path: file, changes: result.changes });
totalChanges += result.changes;
}
}
console.log('\nMigration complete!\n');
console.log(`Total changes: ${totalChanges}`);
console.log(`Modified ${modifiedFiles.length} files:\n`);
modifiedFiles
.sort((a, b) => b.changes - a.changes)
.forEach(({ path: filePath, changes }) => {
console.log(` - ${path.relative(process.cwd(), filePath)} (${changes} changes)`);
});
if (modifiedFiles.length === 0) {
console.log(' No files needed modification.');
}
}
main();

View file

@ -241,4 +241,5 @@ export class Log {
// Pre-configured logger instances for specific contexts
// All context loggers now record to history
export const SyncLog = Log.withContext('sync');
export const PFLog = Log.withContext('pf');
export const PluginLog = Log.withContext('plugin');

View file

@ -20,7 +20,7 @@ import {
PluginManifest,
} from '@super-productivity/plugin-api';
import { PluginBridgeService } from './plugin-bridge.service';
import { Log } from '../core/log';
import { PluginLog } from '../core/log';
import {
taskCopyToTaskData,
projectCopyToProjectData,
@ -67,7 +67,7 @@ export class PluginAPI implements PluginAPIInterface {
}
pluginHooks.get(hook)!.push(fn);
Log.log(`Plugin ${this._pluginId} registered hook: ${hook}`);
PluginLog.log(`Plugin ${this._pluginId} registered hook: ${hook}`);
// Register hook with bridge
this._pluginBridge.registerHook(this._pluginId, hook, fn);
@ -75,14 +75,14 @@ export class PluginAPI implements PluginAPIInterface {
registerHeaderButton(headerBtnCfg: PluginHeaderBtnCfg): void {
this._headerButtons.push({ ...headerBtnCfg, pluginId: this._pluginId });
Log.log(`Plugin ${this._pluginId} registered header button`, headerBtnCfg);
PluginLog.log(`Plugin ${this._pluginId} registered header button`, headerBtnCfg);
this._pluginBridge.registerHeaderButton(headerBtnCfg);
}
registerMenuEntry(menuEntryCfg: Omit<PluginMenuEntryCfg, 'pluginId'>): void {
const fullMenuEntry = { ...menuEntryCfg, pluginId: this._pluginId };
this._menuEntries.push(fullMenuEntry);
Log.log(`Plugin ${this._pluginId} registered menu entry`, menuEntryCfg);
PluginLog.log(`Plugin ${this._pluginId} registered menu entry`, menuEntryCfg);
this._pluginBridge.registerMenuEntry(menuEntryCfg);
}
@ -100,7 +100,7 @@ export class PluginAPI implements PluginAPIInterface {
};
this._shortcuts.push(shortcut);
Log.log(`Plugin ${this._pluginId} registered shortcut`, shortcutCfg);
PluginLog.log(`Plugin ${this._pluginId} registered shortcut`, shortcutCfg);
// Register shortcut with bridge
this._pluginBridge.registerShortcut(shortcut);
@ -110,58 +110,64 @@ export class PluginAPI implements PluginAPIInterface {
sidePanelBtnCfg: Omit<PluginSidePanelBtnCfg, 'pluginId'>,
): void {
this._sidePanelButtons.push({ ...sidePanelBtnCfg, pluginId: this._pluginId });
Log.log(`Plugin ${this._pluginId} registered side panel button`, sidePanelBtnCfg);
PluginLog.log(
`Plugin ${this._pluginId} registered side panel button`,
sidePanelBtnCfg,
);
this._pluginBridge.registerSidePanelButton(sidePanelBtnCfg);
}
showIndexHtmlAsView(): void {
Log.log(`Plugin ${this._pluginId} requested to show index.html`);
PluginLog.log(`Plugin ${this._pluginId} requested to show index.html`);
return this._pluginBridge.showIndexHtmlAsView(this._pluginId);
}
async getTasks(): Promise<Task[]> {
Log.log(`Plugin ${this._pluginId} requested all tasks`);
PluginLog.log(`Plugin ${this._pluginId} requested all tasks`);
const tasks = await this._pluginBridge.getTasks();
return tasks.map(taskCopyToTaskData);
}
async getArchivedTasks(): Promise<Task[]> {
Log.log(`Plugin ${this._pluginId} requested archived tasks`);
PluginLog.log(`Plugin ${this._pluginId} requested archived tasks`);
const tasks = await this._pluginBridge.getArchivedTasks();
return tasks.map(taskCopyToTaskData);
}
async getCurrentContextTasks(): Promise<Task[]> {
Log.log(`Plugin ${this._pluginId} requested current context tasks`);
PluginLog.log(`Plugin ${this._pluginId} requested current context tasks`);
const tasks = await this._pluginBridge.getCurrentContextTasks();
return tasks.map(taskCopyToTaskData);
}
async updateTask(taskId: string, updates: Partial<Task>): Promise<void> {
Log.log(`Plugin ${this._pluginId} requested to update task ${taskId}:`, updates);
PluginLog.log(
`Plugin ${this._pluginId} requested to update task ${taskId}:`,
updates,
);
const taskCopyUpdates = taskDataToPartialTaskCopy(updates);
return this._pluginBridge.updateTask(taskId, taskCopyUpdates);
}
async addTask(taskData: PluginCreateTaskData): Promise<string> {
Log.log(`Plugin ${this._pluginId} requested to add task:`, taskData);
PluginLog.log(`Plugin ${this._pluginId} requested to add task:`, taskData);
return this._pluginBridge.addTask(taskData);
}
async getAllProjects(): Promise<Project[]> {
Log.log(`Plugin ${this._pluginId} requested all projects`);
PluginLog.log(`Plugin ${this._pluginId} requested all projects`);
const projects = await this._pluginBridge.getAllProjects();
return projects.map(projectCopyToProjectData);
}
async addProject(projectData: Partial<Project>): Promise<string> {
Log.log(`Plugin ${this._pluginId} requested to add project:`, projectData);
PluginLog.log(`Plugin ${this._pluginId} requested to add project:`, projectData);
const projectCopyData = projectDataToPartialProjectCopy(projectData);
return this._pluginBridge.addProject(projectCopyData);
}
async updateProject(projectId: string, updates: Partial<Project>): Promise<void> {
Log.log(
PluginLog.log(
`Plugin ${this._pluginId} requested to update project ${projectId}:`,
updates,
);
@ -170,19 +176,19 @@ export class PluginAPI implements PluginAPIInterface {
}
async getAllTags(): Promise<Tag[]> {
Log.log(`Plugin ${this._pluginId} requested all tags`);
PluginLog.log(`Plugin ${this._pluginId} requested all tags`);
const tags = await this._pluginBridge.getAllTags();
return tags.map(tagCopyToTagData);
}
async addTag(tagData: Partial<Tag>): Promise<string> {
Log.log(`Plugin ${this._pluginId} requested to add tag:`, tagData);
PluginLog.log(`Plugin ${this._pluginId} requested to add tag:`, tagData);
const tagCopyData = tagDataToPartialTagCopy(tagData);
return this._pluginBridge.addTag(tagCopyData);
}
async updateTag(tagId: string, updates: Partial<Tag>): Promise<void> {
Log.log(`Plugin ${this._pluginId} requested to update tag ${tagId}:`, updates);
PluginLog.log(`Plugin ${this._pluginId} requested to update tag ${tagId}:`, updates);
const tagCopyUpdates = tagDataToPartialTagCopy(updates);
return this._pluginBridge.updateTag(tagId, tagCopyUpdates);
}
@ -192,7 +198,7 @@ export class PluginAPI implements PluginAPIInterface {
contextId: string,
contextType: 'project' | 'task',
): Promise<void> {
Log.log(
PluginLog.log(
`Plugin ${this._pluginId} requested to reorder tasks in ${contextType} ${contextId}:`,
taskIds,
);
@ -204,27 +210,27 @@ export class PluginAPI implements PluginAPIInterface {
}
async notify(notifyCfg: NotifyCfg): Promise<void> {
Log.log(`Plugin ${this._pluginId} requested notification:`, notifyCfg);
PluginLog.log(`Plugin ${this._pluginId} requested notification:`, notifyCfg);
return this._pluginBridge.notify(notifyCfg);
}
persistDataSynced(dataStr: string): Promise<void> {
Log.log(`Plugin ${this._pluginId} requested to persist data:`, dataStr);
PluginLog.log(`Plugin ${this._pluginId} requested to persist data:`, dataStr);
return this._pluginBridge.persistDataSynced(dataStr);
}
loadSyncedData(): Promise<string | null> {
Log.log(`Plugin ${this._pluginId} requested to load persisted data:`);
PluginLog.log(`Plugin ${this._pluginId} requested to load persisted data:`);
return this._pluginBridge.loadPersistedData();
}
async openDialog(dialogCfg: DialogCfg): Promise<void> {
Log.log(`Plugin ${this._pluginId} requested to open dialog:`, dialogCfg);
PluginLog.log(`Plugin ${this._pluginId} requested to open dialog:`, dialogCfg);
return this._pluginBridge.openDialog(dialogCfg);
}
async triggerSync(): Promise<void> {
Log.log(`Plugin ${this._pluginId} requested to trigger sync`);
PluginLog.log(`Plugin ${this._pluginId} requested to trigger sync`);
return this._pluginBridge.triggerSync();
}
@ -234,7 +240,7 @@ export class PluginAPI implements PluginAPIInterface {
*/
onMessage(handler: (message: any) => Promise<any>): void {
this._messageHandler = handler;
Log.log(`Plugin ${this._pluginId} registered message handler`);
PluginLog.log(`Plugin ${this._pluginId} registered message handler`);
}
/**
@ -266,7 +272,7 @@ export class PluginAPI implements PluginAPIInterface {
* Execute an NgRx action if it's in the allowed list
*/
dispatchAction(action: any): void {
Log.log(`Plugin ${this._pluginId} requested to execute action:`, action);
PluginLog.log(`Plugin ${this._pluginId} requested to execute action:`, action);
return this._pluginBridge.dispatchAction(action);
}
@ -275,7 +281,7 @@ export class PluginAPI implements PluginAPIInterface {
* Called when the plugin is being unloaded
*/
cleanup(): void {
Log.log(`Cleaning up PluginAPI for plugin ${this._pluginId}`);
PluginLog.log(`Cleaning up PluginAPI for plugin ${this._pluginId}`);
// Clear all hook handlers
this._hookHandlers.clear();

View file

@ -39,7 +39,7 @@ import { isAllowedPluginAction } from './allowed-plugin-actions.const';
import { TranslateService } from '@ngx-translate/core';
import { T } from '../t.const';
import { SyncWrapperService } from '../imex/sync/sync-wrapper.service';
import { Log } from '../core/log';
import { PluginLog } from '../core/log';
/**
* PluginBridge acts as an intermediary layer between plugins and the main application services.
@ -120,7 +120,7 @@ export class PluginBridgeService implements OnDestroy {
duration: 5000, // 5 seconds default duration
});
Log.log('PluginBridge: Notification sent successfully', notifyCfg);
PluginLog.log('PluginBridge: Notification sent successfully', notifyCfg);
}
/**
@ -143,11 +143,11 @@ export class PluginBridgeService implements OnDestroy {
});
dialogRef.afterClosed().subscribe((result) => {
Log.log('PluginBridge: Dialog closed with result:', result);
PluginLog.log('PluginBridge: Dialog closed with result:', result);
resolve();
});
} catch (error) {
Log.err('PluginBridge: Failed to open dialog:', error);
PluginLog.err('PluginBridge: Failed to open dialog:', error);
reject(error);
}
});
@ -164,7 +164,7 @@ export class PluginBridgeService implements OnDestroy {
this._translateService.instant(T.PLUGINS.NO_PLUGIN_ID_PROVIDED_FOR_HTML),
);
}
Log.log('PluginBridge: Navigating to plugin index view', {
PluginLog.log('PluginBridge: Navigating to plugin index view', {
pluginId: targetPluginId,
});
// Navigate to the plugin index route
@ -195,12 +195,12 @@ export class PluginBridgeService implements OnDestroy {
return task as TaskCopy;
});
Log.log('PluginBridge: Retrieved archived tasks', {
PluginLog.log('PluginBridge: Retrieved archived tasks', {
count: archivedTasks.length,
});
return archivedTasks;
} catch (error) {
Log.err('PluginBridge: Failed to load archived tasks:', error);
PluginLog.err('PluginBridge: Failed to load archived tasks:', error);
return [];
}
}
@ -232,7 +232,7 @@ export class PluginBridgeService implements OnDestroy {
// Update the task using TaskService (TaskCopy is compatible with Task)
this._taskService.update(taskId, updates);
Log.log('PluginBridge: Task updated successfully', { taskId, updates });
PluginLog.log('PluginBridge: Task updated successfully', { taskId, updates });
}
/**
@ -270,7 +270,7 @@ export class PluginBridgeService implements OnDestroy {
}),
);
Log.log('PluginBridge: Subtask added successfully', {
PluginLog.log('PluginBridge: Subtask added successfully', {
taskId: task.id,
taskData,
});
@ -293,7 +293,7 @@ export class PluginBridgeService implements OnDestroy {
false, // isAddToBottom
);
Log.log('PluginBridge: Task added successfully', { taskId, taskData });
PluginLog.log('PluginBridge: Task added successfully', { taskId, taskData });
return taskId;
}
}
@ -312,7 +312,7 @@ export class PluginBridgeService implements OnDestroy {
async addProject(projectData: Partial<ProjectCopy>): Promise<string> {
typia.assert<Partial<ProjectCopy>>(projectData);
Log.log('PluginBridge: Project add', { projectData });
PluginLog.log('PluginBridge: Project add', { projectData });
return this._projectService.add(projectData);
}
@ -326,7 +326,7 @@ export class PluginBridgeService implements OnDestroy {
// Update the project using ProjectService (ProjectCopy is compatible with Project)
this._projectService.update(projectId, updates);
Log.log('PluginBridge: Project updated successfully', { projectId, updates });
PluginLog.log('PluginBridge: Project updated successfully', { projectId, updates });
}
/**
@ -345,7 +345,7 @@ export class PluginBridgeService implements OnDestroy {
// Add the tag using TagService (TagCopy is compatible with Tag)
const tagId = this._tagService.addTag(tagData);
Log.log('PluginBridge: Tag added successfully', { tagId, tagData });
PluginLog.log('PluginBridge: Tag added successfully', { tagId, tagData });
return tagId;
}
@ -358,7 +358,7 @@ export class PluginBridgeService implements OnDestroy {
// Update the tag using TagService (TagCopy is compatible with Tag)
this._tagService.updateTag(tagId, updates);
Log.log('PluginBridge: Tag updated successfully', { tagId, updates });
PluginLog.log('PluginBridge: Tag updated successfully', { tagId, updates });
}
/**
@ -401,7 +401,7 @@ export class PluginBridgeService implements OnDestroy {
allTasks?.filter((t) => t.projectId === contextId && !t.parentId) || [];
const taskIdsInProject = tasksInProject.map((t) => t.id);
Log.log('PluginBridge: Validating task reorder', {
PluginLog.log('PluginBridge: Validating task reorder', {
requestedTaskIds: taskIds,
projectTaskIds: allProjectTaskIds,
actualTasksInProject: taskIdsInProject,
@ -427,7 +427,7 @@ export class PluginBridgeService implements OnDestroy {
// Note: This assumes all tasks are in the regular list, not backlog
this._projectService.update(contextId, { taskIds });
Log.log('PluginBridge: Project tasks reordered successfully', {
PluginLog.log('PluginBridge: Project tasks reordered successfully', {
projectId: contextId,
newOrder: taskIds,
});
@ -461,7 +461,7 @@ export class PluginBridgeService implements OnDestroy {
// Update the task with new subtask order
this._taskService.update(contextId, { subTaskIds: taskIds });
Log.log('PluginBridge: Subtasks reordered successfully', {
PluginLog.log('PluginBridge: Subtasks reordered successfully', {
parentTaskId: contextId,
newOrder: taskIds,
});
@ -485,11 +485,11 @@ export class PluginBridgeService implements OnDestroy {
this._currentPluginId,
dataStr,
);
Log.log('PluginBridge: Plugin data persisted successfully', {
PluginLog.log('PluginBridge: Plugin data persisted successfully', {
pluginId: this._currentPluginId,
});
} catch (error) {
Log.err('PluginBridge: Failed to persist plugin data:', error);
PluginLog.err('PluginBridge: Failed to persist plugin data:', error);
throw new Error(this._translateService.instant(T.PLUGINS.UNABLE_TO_PERSIST_DATA));
}
}
@ -509,7 +509,7 @@ export class PluginBridgeService implements OnDestroy {
this._currentPluginId,
);
} catch (error) {
Log.err('PluginBridge: Failed to get persisted plugin data:', error);
PluginLog.err('PluginBridge: Failed to get persisted plugin data:', error);
return null;
}
}
@ -523,11 +523,11 @@ export class PluginBridgeService implements OnDestroy {
}
try {
Log.log('PluginBridge: Triggering sync for plugin', this._currentPluginId);
PluginLog.log('PluginBridge: Triggering sync for plugin', this._currentPluginId);
await this._syncWrapperService.sync();
Log.log('PluginBridge: Sync completed successfully');
PluginLog.log('PluginBridge: Sync completed successfully');
} catch (error) {
Log.err('PluginBridge: Sync failed:', error);
PluginLog.err('PluginBridge: Sync failed:', error);
throw error;
}
}
@ -555,7 +555,7 @@ export class PluginBridgeService implements OnDestroy {
this._removePluginSidePanelButtons(pluginId);
this.unregisterPluginShortcuts(pluginId);
Log.log('PluginBridge: All hooks unregistered for plugin', { pluginId });
PluginLog.log('PluginBridge: All hooks unregistered for plugin', { pluginId });
}
/**
@ -577,7 +577,7 @@ export class PluginBridgeService implements OnDestroy {
const currentButtons = this._headerButtons$.value;
this._headerButtons$.next([...currentButtons, newButton]);
Log.log('PluginBridge: Header button registered', {
PluginLog.log('PluginBridge: Header button registered', {
pluginId: this._currentPluginId,
headerBtnCfg,
});
@ -622,16 +622,19 @@ export class PluginBridgeService implements OnDestroy {
);
if (isDuplicate) {
Log.err('PluginBridge: Duplicate menu entry detected, skipping registration', {
pluginId: this._currentPluginId,
label: menuEntryCfg.label,
});
PluginLog.err(
'PluginBridge: Duplicate menu entry detected, skipping registration',
{
pluginId: this._currentPluginId,
label: menuEntryCfg.label,
},
);
return;
}
this._menuEntries$.next([...currentEntries, newMenuEntry]);
Log.log('PluginBridge: Menu entry registered', {
PluginLog.log('PluginBridge: Menu entry registered', {
pluginId: this._currentPluginId,
menuEntryCfg,
});
@ -647,7 +650,7 @@ export class PluginBridgeService implements OnDestroy {
);
this._headerButtons$.next(filteredButtons);
Log.log('PluginBridge: Header buttons removed for plugin', { pluginId });
PluginLog.log('PluginBridge: Header buttons removed for plugin', { pluginId });
}
/**
@ -658,7 +661,7 @@ export class PluginBridgeService implements OnDestroy {
const filteredEntries = currentEntries.filter((entry) => entry.pluginId !== pluginId);
this._menuEntries$.next(filteredEntries);
Log.log('PluginBridge: Menu entries removed for plugin', { pluginId });
PluginLog.log('PluginBridge: Menu entries removed for plugin', { pluginId });
}
/**
@ -698,7 +701,7 @@ export class PluginBridgeService implements OnDestroy {
);
if (isDuplicate) {
Log.err(
PluginLog.err(
'PluginBridge: Duplicate side panel button detected, skipping registration',
{
pluginId: this._currentPluginId,
@ -710,7 +713,7 @@ export class PluginBridgeService implements OnDestroy {
this._sidePanelButtons$.next([...currentButtons, newButton]);
Log.log('PluginBridge: Side panel button registered', {
PluginLog.log('PluginBridge: Side panel button registered', {
pluginId: this._currentPluginId,
sidePanelBtnCfg,
});
@ -726,7 +729,7 @@ export class PluginBridgeService implements OnDestroy {
);
this._sidePanelButtons$.next(filteredButtons);
Log.log('PluginBridge: Side panel buttons removed for plugin', { pluginId });
PluginLog.log('PluginBridge: Side panel buttons removed for plugin', { pluginId });
}
/**
@ -747,7 +750,7 @@ export class PluginBridgeService implements OnDestroy {
const currentShortcuts = this.shortcuts$.value;
this.shortcuts$.next([...currentShortcuts, shortcutWithPluginId]);
Log.log('PluginBridge: Shortcut registered', {
PluginLog.log('PluginBridge: Shortcut registered', {
pluginId: this._currentPluginId,
shortcut: shortcutWithPluginId,
});
@ -763,10 +766,12 @@ export class PluginBridgeService implements OnDestroy {
if (shortcut) {
try {
await Promise.resolve(shortcut.onExec());
Log.log(`Executed shortcut "${shortcut.label}" from plugin ${shortcut.pluginId}`);
PluginLog.log(
`Executed shortcut "${shortcut.label}" from plugin ${shortcut.pluginId}`,
);
return true;
} catch (error) {
Log.err(`Failed to execute shortcut "${shortcut.label}":`, error);
PluginLog.err(`Failed to execute shortcut "${shortcut.label}":`, error);
return false;
}
}
@ -785,7 +790,7 @@ export class PluginBridgeService implements OnDestroy {
if (filteredShortcuts.length !== currentShortcuts.length) {
this.shortcuts$.next(filteredShortcuts);
Log.log(
PluginLog.log(
`Unregistered ${currentShortcuts.length - filteredShortcuts.length} shortcuts for plugin ${pluginId}`,
);
}
@ -863,7 +868,9 @@ export class PluginBridgeService implements OnDestroy {
// Check if the action is in the allowed list
if (!isAllowedPluginAction(action)) {
Log.err(`PluginBridge: Action type '${action.type}' is not allowed for plugins`);
PluginLog.err(
`PluginBridge: Action type '${action.type}' is not allowed for plugins`,
);
throw new Error(
this._translateService.instant(T.PLUGINS.ACTION_TYPE_NOT_ALLOWED, {
actionType: action.type,
@ -873,7 +880,7 @@ export class PluginBridgeService implements OnDestroy {
// Dispatch the action
this._store.dispatch(action);
Log.log(`PluginBridge: Dispatched action for plugin ${this._currentPluginId}`, {
PluginLog.log(`PluginBridge: Dispatched action for plugin ${this._currentPluginId}`, {
actionType: action.type,
payload: action,
});
@ -926,7 +933,7 @@ export class PluginBridgeService implements OnDestroy {
return result;
} catch (error) {
Log.err('PluginBridge: Failed to execute Node.js script:', error);
PluginLog.err('PluginBridge: Failed to execute Node.js script:', error);
return {
success: false,
error:
@ -952,7 +959,7 @@ export class PluginBridgeService implements OnDestroy {
* Clean up all resources when service is destroyed
*/
ngOnDestroy(): void {
Log.log('PluginBridgeService: Cleaning up resources');
PluginLog.log('PluginBridgeService: Cleaning up resources');
// Complete all BehaviorSubjects
this._headerButtons$.complete();
@ -960,6 +967,6 @@ export class PluginBridgeService implements OnDestroy {
this.shortcuts$.complete();
this._sidePanelButtons$.complete();
Log.log('PluginBridgeService: Cleanup complete');
PluginLog.log('PluginBridgeService: Cleanup complete');
}
}

View file

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Log } from '../core/log';
import { PluginLog } from '../core/log';
export interface CachedPlugin {
id: string;
@ -65,18 +65,19 @@ export class PluginCacheService {
};
const totalSizeKB = Object.values(sizes).reduce((sum, size) => sum + size, 0);
Log.log(`[PluginCache] Storing plugin "${pluginId}":`);
Log.log(`[PluginCache] - Manifest: ${sizes.manifest.toFixed(2)}KB`);
Log.log(`[PluginCache] - Code: ${sizes.code.toFixed(2)}KB`);
if (indexHtml) Log.log(`[PluginCache] - IndexHtml: ${sizes.indexHtml.toFixed(2)}KB`);
if (icon) Log.log(`[PluginCache] - Icon: ${sizes.icon.toFixed(2)}KB`);
Log.log(
PluginLog.log(`[PluginCache] Storing plugin "${pluginId}":`);
PluginLog.log(`[PluginCache] - Manifest: ${sizes.manifest.toFixed(2)}KB`);
PluginLog.log(`[PluginCache] - Code: ${sizes.code.toFixed(2)}KB`);
if (indexHtml)
PluginLog.log(`[PluginCache] - IndexHtml: ${sizes.indexHtml.toFixed(2)}KB`);
if (icon) PluginLog.log(`[PluginCache] - Icon: ${sizes.icon.toFixed(2)}KB`);
PluginLog.log(
`[PluginCache] - Total size: ${totalSizeKB.toFixed(2)}KB (${(totalSizeKB / 1024).toFixed(2)}MB)`,
);
if (totalSizeKB > 1024) {
// Warn if plugin is larger than 1MB
Log.err(
PluginLog.err(
`[PluginCache] Large plugin detected: "${pluginId}" is ${(totalSizeKB / 1024).toFixed(2)}MB`,
);
}
@ -97,11 +98,14 @@ export class PluginCacheService {
return new Promise((resolve, reject) => {
const request = store.put(plugin);
request.onsuccess = () => {
Log.log(`[PluginCache] Successfully stored plugin "${pluginId}"`);
PluginLog.log(`[PluginCache] Successfully stored plugin "${pluginId}"`);
resolve();
};
request.onerror = () => {
Log.err(`[PluginCache] Failed to store plugin "${pluginId}":`, request.error);
PluginLog.err(
`[PluginCache] Failed to store plugin "${pluginId}":`,
request.error,
);
reject(new Error(`Failed to store plugin ${pluginId}`));
};
});
@ -111,7 +115,7 @@ export class PluginCacheService {
* Get a plugin from the cache
*/
async getPlugin(pluginId: string): Promise<CachedPlugin | null> {
Log.log(`[PluginCache] Loading plugin "${pluginId}"`);
PluginLog.log(`[PluginCache] Loading plugin "${pluginId}"`);
const db = await this._getDB();
const transaction = db.transaction([this.STORE_NAME], 'readonly');
const store = transaction.objectStore(this.STORE_NAME);
@ -142,7 +146,7 @@ export class PluginCacheService {
* Get all cached plugins
*/
async getAllPlugins(): Promise<CachedPlugin[]> {
Log.log('[PluginCache] Loading all plugins from cache');
PluginLog.log('[PluginCache] Loading all plugins from cache');
const db = await this._getDB();
const transaction = db.transaction([this.STORE_NAME], 'readonly');
const store = transaction.objectStore(this.STORE_NAME);

View file

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { Hooks, PluginHookHandler } from './plugin-api.model';
import { Log } from '../core/log';
import { PluginLog } from '../core/log';
/**
* Simplified plugin hooks service following KISS principles.
@ -20,7 +20,7 @@ export class PluginHooksService {
this._handlers.set(hook, new Map());
}
this._handlers.get(hook)!.set(pluginId, handler);
Log.log(`Plugin ${pluginId} registered for ${hook}`);
PluginLog.log(`Plugin ${pluginId} registered for ${hook}`);
}
/**
@ -37,7 +37,7 @@ export class PluginHooksService {
try {
await handler(payload);
} catch (error) {
Log.err(`Plugin ${pluginId} ${hook} handler error:`, error);
PluginLog.err(`Plugin ${pluginId} ${hook} handler error:`, error);
// Continue with other handlers
}
}

View file

@ -5,7 +5,7 @@ import { PluginManifest } from './plugin-api.model';
import { PluginCacheService } from './plugin-cache.service';
// KISS: No size checks in loader - trust the browser's limits
import { validatePluginManifest } from './util/validate-manifest.util';
import { Log } from '../core/log';
import { PluginLog } from '../core/log';
interface PluginAssets {
manifest: PluginManifest;
@ -70,7 +70,7 @@ export class PluginLoaderService {
.pipe(first())
.toPromise();
} catch (e) {
Log.err(`No index.html for plugin ${manifest.id}`);
PluginLog.err(`No index.html for plugin ${manifest.id}`);
}
}
@ -82,13 +82,13 @@ export class PluginLoaderService {
.pipe(first())
.toPromise();
} catch (e) {
Log.err(`No icon for plugin ${manifest.id}`);
PluginLog.err(`No icon for plugin ${manifest.id}`);
}
}
return { manifest, code, indexHtml, icon };
} catch (error) {
Log.err(`Failed to load plugin from ${pluginPath}:`, error);
PluginLog.err(`Failed to load plugin from ${pluginPath}:`, error);
throw error;
}
}

View file

@ -1,7 +1,7 @@
import { inject, Injectable } from '@angular/core';
import { PfapiService } from '../pfapi/pfapi.service';
import { PluginMetadata, PluginMetaDataState } from './plugin-persistence.model';
import { Log } from '../core/log';
import { PluginLog } from '../core/log';
/**
* Service for persisting plugin metadata using pfapi.
@ -28,7 +28,7 @@ export class PluginMetaPersistenceService {
// Deep compare the states
if (this._isDataEqual(currentState, newState)) {
Log.log('PluginMetaPersistenceService: No changes detected, skipping write');
PluginLog.log('PluginMetaPersistenceService: No changes detected, skipping write');
return;
}
@ -36,7 +36,7 @@ export class PluginMetaPersistenceService {
await this._pfapiService.pf.m.pluginMetadata.save(newState, {
isUpdateRevAndLastUpdate: true,
});
Log.log('PluginMetaPersistenceService: Data updated');
PluginLog.log('PluginMetaPersistenceService: Data updated');
}
/**

View file

@ -6,7 +6,7 @@ import { PluginSecurityService } from './plugin-security';
import { SnackService } from '../core/snack/snack.service';
import { IS_ELECTRON } from '../app.constants';
import { PluginCleanupService } from './plugin-cleanup.service';
import { Log } from '../core/log';
import { PluginLog } from '../core/log';
/**
* Simplified plugin runner following KISS principles.
@ -59,7 +59,7 @@ export class PluginRunner {
// Show warnings if any
if (analysis.warnings.length > 0) {
Log.err(`Plugin ${manifest.id} warnings:`, analysis.warnings);
PluginLog.err(`Plugin ${manifest.id} warnings:`, analysis.warnings);
this._snackService.open({
msg: `Plugin "${manifest.name}" has warnings: ${analysis.warnings[0]}`,
type: 'CUSTOM',
@ -69,7 +69,7 @@ export class PluginRunner {
// Log info for transparency
if (analysis.info.length > 0) {
Log.info(`Plugin ${manifest.id} info:`, analysis.info);
PluginLog.info(`Plugin ${manifest.id} info:`, analysis.info);
}
try {
@ -102,13 +102,13 @@ export class PluginRunner {
} catch (error) {
pluginInstance.error =
error instanceof Error ? error.message : 'Failed to load plugin';
Log.err(`Plugin ${manifest.id} error:`, error);
PluginLog.err(`Plugin ${manifest.id} error:`, error);
}
this._loadedPlugins.set(manifest.id, pluginInstance);
return pluginInstance;
} catch (error) {
Log.err(`Failed to load plugin ${manifest.id}:`, error);
PluginLog.err(`Failed to load plugin ${manifest.id}:`, error);
throw error;
}
}
@ -173,7 +173,7 @@ export class PluginRunner {
// Unregister hooks
this._pluginBridge.unregisterPluginHooks(pluginId);
Log.log(`Plugin ${pluginId} unloaded`);
PluginLog.log(`Plugin ${pluginId} unloaded`);
return true;
}
return false;

View file

@ -30,7 +30,7 @@ import { PluginLoaderService } from './plugin-loader.service';
import { validatePluginManifest } from './util/validate-manifest.util';
import { TranslateService } from '@ngx-translate/core';
import { T } from '../t.const';
import { Log } from '../core/log';
import { PluginLog } from '../core/log';
@Injectable({
providedIn: 'root',
@ -67,11 +67,11 @@ export class PluginService implements OnDestroy {
async initializePlugins(): Promise<void> {
if (this._isInitialized) {
Log.err(this._translateService.instant(T.PLUGINS.ALREADY_INITIALIZED));
PluginLog.err(this._translateService.instant(T.PLUGINS.ALREADY_INITIALIZED));
return;
}
Log.log('Initializing plugin system...');
PluginLog.log('Initializing plugin system...');
try {
// Only load manifests, not the actual plugin code
@ -82,9 +82,9 @@ export class PluginService implements OnDestroy {
await this._loadEnabledPlugins();
this._isInitialized = true;
Log.log('Plugin system initialized successfully');
PluginLog.log('Plugin system initialized successfully');
} catch (error) {
Log.err('Failed to initialize plugin system:', error);
PluginLog.err('Failed to initialize plugin system:', error);
throw error;
}
}
@ -132,13 +132,13 @@ export class PluginService implements OnDestroy {
}
} catch (e) {
// Icon is optional - silently ignore 404s and other errors
Log.debug(
PluginLog.debug(
`Icon not found for plugin ${manifest.id}: ${path}/${manifest.icon || 'icon.svg'}`,
);
}
}
} catch (error) {
Log.err(`Failed to discover plugin at ${path}:`, error);
PluginLog.err(`Failed to discover plugin at ${path}:`, error);
}
}
@ -187,13 +187,13 @@ export class PluginService implements OnDestroy {
this._pluginIcons.set(cachedPlugin.id, cachedPlugin.icon);
}
} catch (error) {
Log.err(`Failed to discover cached plugin ${cachedPlugin.id}:`, error);
PluginLog.err(`Failed to discover cached plugin ${cachedPlugin.id}:`, error);
}
}
this._updatePluginStates();
} catch (error) {
Log.err('Failed to discover cached plugins:', error);
PluginLog.err('Failed to discover cached plugins:', error);
}
}
@ -203,11 +203,11 @@ export class PluginService implements OnDestroy {
(state) => state.isEnabled,
);
Log.log(`Loading ${pluginsToLoad.length} enabled plugins...`);
PluginLog.log(`Loading ${pluginsToLoad.length} enabled plugins...`);
// Log which plugins are being loaded
for (const state of pluginsToLoad) {
Log.log(
PluginLog.log(
`Loading plugin: ${state.manifest.id} (enabled: ${state.isEnabled}, hooks: ${state.manifest.hooks?.length || 0}, sidePanel: ${state.manifest.sidePanel})`,
);
}
@ -228,7 +228,7 @@ export class PluginService implements OnDestroy {
async activatePlugin(pluginId: string): Promise<PluginInstance | null> {
const state = this._pluginStates.get(pluginId);
if (!state) {
Log.err(`Plugin ${pluginId} not found`);
PluginLog.err(`Plugin ${pluginId} not found`);
return null;
}
@ -259,7 +259,7 @@ export class PluginService implements OnDestroy {
this._updatePluginStates();
try {
Log.log(`Activating plugin: ${pluginId}`);
PluginLog.log(`Activating plugin: ${pluginId}`);
const instance = await this._loadPluginLazy(state);
state.status = 'loaded';
@ -273,7 +273,7 @@ export class PluginService implements OnDestroy {
return instance;
} catch (error) {
Log.err(`Failed to activate plugin ${pluginId}:`, error);
PluginLog.err(`Failed to activate plugin ${pluginId}:`, error);
state.status = 'error';
state.error = error instanceof Error ? error.message : String(error);
this._updatePluginStates();
@ -311,7 +311,7 @@ export class PluginService implements OnDestroy {
const promises = cachedPlugins.map(async (cachedPlugin) => {
try {
Log.log(`Loading cached plugin: ${cachedPlugin.id}`);
PluginLog.log(`Loading cached plugin: ${cachedPlugin.id}`);
// Set the path for reload functionality
this._pluginPaths.set(cachedPlugin.id, `uploaded://${cachedPlugin.id}`);
@ -319,14 +319,14 @@ export class PluginService implements OnDestroy {
await this._loadUploadedPlugin(cachedPlugin.id);
// The plugin instance is already added to _loadedPlugins in _loadUploadedPlugin if loaded successfully
} catch (error) {
Log.err(`Failed to load cached plugin ${cachedPlugin.id}:`, error);
PluginLog.err(`Failed to load cached plugin ${cachedPlugin.id}:`, error);
// Continue loading other plugins even if one fails
}
});
await Promise.allSettled(promises);
} catch (error) {
Log.err('Failed to load cached plugins:', error);
PluginLog.err('Failed to load cached plugins:', error);
// Don't throw - this shouldn't prevent other plugins from loading
}
}
@ -351,9 +351,9 @@ export class PluginService implements OnDestroy {
if (pluginInstance.manifest && pluginInstance.manifest.id) {
this._pluginPaths.set(pluginInstance.manifest.id, pluginPath);
}
Log.log(`${type} plugin loaded successfully from ${pluginPath}`);
PluginLog.log(`${type} plugin loaded successfully from ${pluginPath}`);
} catch (error) {
Log.err(`Failed to load ${type} plugin from ${pluginPath}:`, error);
PluginLog.err(`Failed to load ${type} plugin from ${pluginPath}:`, error);
// Continue loading other plugins even if one fails
}
});
@ -402,7 +402,9 @@ export class PluginService implements OnDestroy {
error: this._translateService.instant(T.PLUGINS.NODE_ONLY_DESKTOP),
};
this._pluginPaths.set(manifest.id, pluginPath); // Store the path for potential reload
Log.log(`Plugin ${manifest.id} requires desktop version, creating placeholder`);
PluginLog.log(
`Plugin ${manifest.id} requires desktop version, creating placeholder`,
);
return placeholderInstance;
}
@ -420,10 +422,10 @@ export class PluginService implements OnDestroy {
// Analyze plugin code (informational only - KISS approach)
const codeAnalysis = this._pluginSecurity.analyzePluginCode(pluginCode, manifest);
if (codeAnalysis.warnings.length > 0) {
Log.err(`Plugin ${manifest.id} warnings:`, codeAnalysis.warnings);
PluginLog.err(`Plugin ${manifest.id} warnings:`, codeAnalysis.warnings);
}
if (codeAnalysis.info.length > 0) {
Log.info(`Plugin ${manifest.id} info:`, codeAnalysis.info);
PluginLog.info(`Plugin ${manifest.id} info:`, codeAnalysis.info);
}
// If plugin is disabled, create a placeholder instance without loading code
@ -435,7 +437,7 @@ export class PluginService implements OnDestroy {
error: undefined,
};
this._pluginPaths.set(manifest.id, pluginPath); // Store the path for potential reload
Log.log(`Plugin ${manifest.id} is disabled, skipping load`);
PluginLog.log(`Plugin ${manifest.id} is disabled, skipping load`);
return placeholderInstance;
}
@ -465,14 +467,14 @@ export class PluginService implements OnDestroy {
// The enabled state will be persisted later when user explicitly enables/disables plugins
this._ensurePluginEnabledInMemory(manifest.id);
Log.log(`Plugin ${manifest.id} loaded successfully`);
PluginLog.log(`Plugin ${manifest.id} loaded successfully`);
} else {
Log.err(`Plugin ${manifest.id} failed to load:`, pluginInstance.error);
PluginLog.err(`Plugin ${manifest.id} failed to load:`, pluginInstance.error);
}
return pluginInstance;
} catch (error) {
Log.err(`Failed to load plugin from ${pluginPath}:`, error);
PluginLog.err(`Failed to load plugin from ${pluginPath}:`, error);
throw error;
}
}
@ -617,7 +619,7 @@ export class PluginService implements OnDestroy {
// Check if plugin exists in states
const state = this._pluginStates.get(pluginId);
if (!state) {
Log.err(`Plugin ${pluginId} not found`);
PluginLog.err(`Plugin ${pluginId} not found`);
this._activeSidePanelPlugin$.next(null);
return;
}
@ -634,11 +636,11 @@ export class PluginService implements OnDestroy {
if (instance) {
this._activeSidePanelPlugin$.next(instance);
} else {
Log.err(`Failed to activate plugin ${pluginId}`);
PluginLog.err(`Failed to activate plugin ${pluginId}`);
this._activeSidePanelPlugin$.next(null);
}
} catch (error) {
Log.err(`Error activating plugin ${pluginId}:`, error);
PluginLog.err(`Error activating plugin ${pluginId}:`, error);
this._activeSidePanelPlugin$.next(null);
}
}
@ -701,7 +703,7 @@ export class PluginService implements OnDestroy {
}
async loadPluginFromZip(file: File): Promise<PluginInstance> {
Log.log(`Starting plugin load from ZIP: ${file.name}`);
PluginLog.log(`Starting plugin load from ZIP: ${file.name}`);
// Import fflate dynamically for better bundle size
const { unzip } = await import('fflate');
@ -739,7 +741,7 @@ export class PluginService implements OnDestroy {
});
},
);
Log.log({ extractedFiles });
PluginLog.log({ extractedFiles });
// Find and extract manifest.json
if (!extractedFiles['manifest.json']) {
@ -820,7 +822,7 @@ export class PluginService implements OnDestroy {
iconContent = new TextDecoder().decode(iconBytes);
// Basic SVG validation
if (!iconContent.includes('<svg') || !iconContent.includes('</svg>')) {
Log.err(`Plugin icon ${manifest.icon} does not appear to be a valid SVG`);
PluginLog.err(`Plugin icon ${manifest.icon} does not appear to be a valid SVG`);
iconContent = null;
}
}
@ -828,10 +830,10 @@ export class PluginService implements OnDestroy {
// Analyze plugin code (informational only - KISS approach)
const codeAnalysis = this._pluginSecurity.analyzePluginCode(pluginCode, manifest);
if (codeAnalysis.warnings.length > 0) {
Log.err(`Plugin ${manifest.id} warnings:`, codeAnalysis.warnings);
PluginLog.err(`Plugin ${manifest.id} warnings:`, codeAnalysis.warnings);
}
if (codeAnalysis.info.length > 0) {
Log.info(`Plugin ${manifest.id} info:`, codeAnalysis.info);
PluginLog.info(`Plugin ${manifest.id} info:`, codeAnalysis.info);
}
// Check if plugin is enabled (default to true for new uploads)
@ -896,7 +898,7 @@ export class PluginService implements OnDestroy {
this._pluginStates.set(manifest.id, state);
this._updatePluginStates();
Log.log(
PluginLog.log(
`Uploaded plugin ${manifest.id} requires desktop version, creating placeholder`,
);
return placeholderInstance;
@ -934,7 +936,7 @@ export class PluginService implements OnDestroy {
this._pluginStates.set(manifest.id, state);
this._updatePluginStates();
Log.log(`Uploaded plugin ${manifest.id} is disabled, skipping load`);
PluginLog.log(`Uploaded plugin ${manifest.id} is disabled, skipping load`);
return placeholderInstance;
}
@ -973,9 +975,12 @@ export class PluginService implements OnDestroy {
this._pluginStates.set(manifest.id, state);
this._updatePluginStates();
Log.log(`Uploaded plugin ${manifest.id} loaded successfully`);
PluginLog.log(`Uploaded plugin ${manifest.id} loaded successfully`);
} else {
Log.err(`Uploaded plugin ${manifest.id} failed to load:`, pluginInstance.error);
PluginLog.err(
`Uploaded plugin ${manifest.id} failed to load:`,
pluginInstance.error,
);
// Add failed plugin to states as well
const state: PluginState = {
@ -993,7 +998,7 @@ export class PluginService implements OnDestroy {
return pluginInstance;
} catch (error) {
Log.err('Failed to load plugin from ZIP:', error);
PluginLog.err('Failed to load plugin from ZIP:', error);
// Create error instance for UI display
const errorInstance: PluginInstance = {
@ -1067,7 +1072,7 @@ export class PluginService implements OnDestroy {
this._pluginStates.delete(pluginId);
this._updatePluginStates();
Log.log(`Uploaded plugin ${pluginId} removed completely`);
PluginLog.log(`Uploaded plugin ${pluginId} removed completely`);
}
unloadPlugin(pluginId: string): boolean {
@ -1106,7 +1111,7 @@ export class PluginService implements OnDestroy {
// In lazy loading mode, unload and re-activate
const state = this._pluginStates.get(pluginId);
if (!state) {
Log.err(`Cannot reload plugin ${pluginId}: not found`);
PluginLog.err(`Cannot reload plugin ${pluginId}: not found`);
return false;
}
@ -1146,10 +1151,10 @@ export class PluginService implements OnDestroy {
// Analyze plugin code (informational only - KISS approach)
const codeAnalysis = this._pluginSecurity.analyzePluginCode(pluginCode, manifest);
if (codeAnalysis.warnings.length > 0) {
Log.err(`Plugin ${manifest.id} warnings:`, codeAnalysis.warnings);
PluginLog.err(`Plugin ${manifest.id} warnings:`, codeAnalysis.warnings);
}
if (codeAnalysis.info.length > 0) {
Log.info(`Plugin ${manifest.id} info:`, codeAnalysis.info);
PluginLog.info(`Plugin ${manifest.id} info:`, codeAnalysis.info);
}
// Check if plugin is enabled
@ -1165,7 +1170,7 @@ export class PluginService implements OnDestroy {
isEnabled: false,
error: undefined,
};
Log.log(`Uploaded plugin ${manifest.id} is disabled, skipping reload`);
PluginLog.log(`Uploaded plugin ${manifest.id} is disabled, skipping reload`);
return placeholderInstance;
}
@ -1189,14 +1194,17 @@ export class PluginService implements OnDestroy {
// Replace existing instance
this._loadedPlugins[existingIndex] = pluginInstance;
}
Log.log(`Uploaded plugin ${manifest.id} reloaded successfully`);
PluginLog.log(`Uploaded plugin ${manifest.id} reloaded successfully`);
} else {
Log.err(`Uploaded plugin ${manifest.id} failed to reload:`, pluginInstance.error);
PluginLog.err(
`Uploaded plugin ${manifest.id} failed to reload:`,
pluginInstance.error,
);
}
return pluginInstance;
} catch (error) {
Log.err(`Failed to reload uploaded plugin ${pluginId}:`, error);
PluginLog.err(`Failed to reload uploaded plugin ${pluginId}:`, error);
throw error;
}
}
@ -1212,7 +1220,7 @@ export class PluginService implements OnDestroy {
// Only check consent in Electron environment
if (!IS_ELECTRON) {
Log.err(
PluginLog.err(
`Plugin ${manifest.id} requires nodeExecution permission which is not available in web environment`,
);
return false;
@ -1269,13 +1277,13 @@ export class PluginService implements OnDestroy {
private _ensurePluginEnabledInMemory(pluginId: string): void {
// We only need to track this in memory for startup purposes
// The actual persistence will happen when user explicitly enables/disables plugins
Log.log(
PluginLog.log(
`Plugin ${pluginId} marked as enabled in memory (no pfapi write during startup)`,
);
}
ngOnDestroy(): void {
Log.log('PluginService: Cleaning up all resources');
PluginLog.log('PluginService: Cleaning up all resources');
// Complete the side panel subject
this._activeSidePanelPlugin$.complete();
@ -1286,7 +1294,7 @@ export class PluginService implements OnDestroy {
try {
this.unloadPlugin(pluginId);
} catch (error) {
Log.err(`Error unloading plugin ${pluginId} during cleanup:`, error);
PluginLog.err(`Error unloading plugin ${pluginId} during cleanup:`, error);
}
});
@ -1302,6 +1310,6 @@ export class PluginService implements OnDestroy {
// Clear loader caches
this._pluginLoader.clearAllCaches();
Log.log('PluginService: Cleanup complete');
PluginLog.log('PluginService: Cleanup complete');
}
}

View file

@ -13,7 +13,7 @@ import { DialogButtonCfg, DialogCfg } from '../../plugin-api.model';
import { PluginSecurityService } from '../../plugin-security';
import { TranslateService } from '@ngx-translate/core';
import { T } from '../../../t.const';
import { Log } from '../../../core/log';
import { PluginLog } from '../../../core/log';
@Component({
selector: 'plugin-dialog',
@ -115,7 +115,7 @@ export class PluginDialogComponent {
this._dialogRef.close(button.label);
}
} catch (error) {
Log.err('Plugin dialog button action failed:', error);
PluginLog.err('Plugin dialog button action failed:', error);
this._dialogRef.close('error');
}
}

View file

@ -33,7 +33,7 @@ import {
} from '@angular/material/card';
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
import { T } from '../../../t.const';
import { Log } from '../../../core/log';
import { PluginLog } from '../../../core/log';
@Component({
selector: 'plugin-index',
@ -103,7 +103,7 @@ export class PluginIndexComponent implements OnInit, OnDestroy {
try {
await this._loadPluginIndex(this.directPluginId);
} catch (err) {
Log.err('Failed to load plugin index:', err);
PluginLog.err('Failed to load plugin index:', err);
this.error.set(
err instanceof Error
? err.message
@ -117,7 +117,7 @@ export class PluginIndexComponent implements OnInit, OnDestroy {
// Subscribe to route parameter changes to handle navigation between plugins
this._routeSubscription = this._route.paramMap.subscribe(async (params) => {
const newPluginId = params.get('pluginId');
Log.log(
PluginLog.log(
'Route paramMap changed, newPluginId:',
newPluginId,
'currentPluginId:',
@ -132,11 +132,13 @@ export class PluginIndexComponent implements OnInit, OnDestroy {
// Skip if it's the same plugin (prevent unnecessary reloads)
if (this.pluginId() === newPluginId) {
Log.log('Same plugin ID, skipping reload');
PluginLog.log('Same plugin ID, skipping reload');
return;
}
Log.log(`Navigating from plugin "${this.pluginId()}" to plugin "${newPluginId}"`);
PluginLog.log(
`Navigating from plugin "${this.pluginId()}" to plugin "${newPluginId}"`,
);
// Clean up previous iframe communication BEFORE setting new plugin ID
this._cleanupIframeCommunication();
@ -152,7 +154,7 @@ export class PluginIndexComponent implements OnInit, OnDestroy {
try {
await this._loadPluginIndex(newPluginId);
} catch (err) {
Log.err('Failed to load plugin index:', err);
PluginLog.err('Failed to load plugin index:', err);
this.error.set(
err instanceof Error
? err.message
@ -170,7 +172,7 @@ export class PluginIndexComponent implements OnInit, OnDestroy {
try {
await this._pluginService.initializePlugins();
} catch (error) {
Log.err('Failed to initialize plugin system:', error);
PluginLog.err('Failed to initialize plugin system:', error);
throw new Error(
this._translateService.instant(T.PLUGINS.PLUGIN_SYSTEM_FAILED_INIT),
);
@ -190,12 +192,12 @@ export class PluginIndexComponent implements OnInit, OnDestroy {
}
private async _loadPluginIndex(pluginId: string): Promise<void> {
Log.log(`Loading plugin index for: ${pluginId}`);
PluginLog.log(`Loading plugin index for: ${pluginId}`);
// Get the plugin index.html content
const indexContent = this._pluginService.getPluginIndexHtml(pluginId);
if (!indexContent) {
Log.err(`No index.html content found for plugin: ${pluginId}`);
PluginLog.err(`No index.html content found for plugin: ${pluginId}`);
// Try to get the plugin instance to check if it should have an index.html
const plugins = await this._pluginService.getAllPlugins();
const plugin = plugins.find((p) => p.manifest.id === pluginId);
@ -244,30 +246,30 @@ export class PluginIndexComponent implements OnInit, OnDestroy {
// Create safe URL and set iframe source
const safeUrl = this._sanitizer.bypassSecurityTrustResourceUrl(iframeUrl);
Log.log(
PluginLog.log(
`Setting iframe src for plugin ${pluginId}:`,
iframeUrl.substring(0, 100) + '...',
);
this.iframeSrc.set(safeUrl);
this.isLoading.set(false);
Log.log(`Plugin ${pluginId} iframe src set, loading complete`);
PluginLog.log(`Plugin ${pluginId} iframe src set, loading complete`);
}
private _cleanupIframeCommunication(): void {
const currentPluginId = this.pluginId();
Log.log(`Cleaning up iframe communication for plugin: ${currentPluginId}`);
PluginLog.log(`Cleaning up iframe communication for plugin: ${currentPluginId}`);
// Remove message listener
if (this._messageListener) {
window.removeEventListener('message', this._messageListener);
this._messageListener = undefined;
Log.log(`Removed message listener for plugin: ${currentPluginId}`);
PluginLog.log(`Removed message listener for plugin: ${currentPluginId}`);
}
// Clear iframe reference from cleanup service (but don't remove from DOM)
if (currentPluginId) {
this._cleanupService.cleanupPlugin(currentPluginId);
Log.log(`Cleaned up plugin references for: ${currentPluginId}`);
PluginLog.log(`Cleaned up plugin references for: ${currentPluginId}`);
}
// Set iframe to empty data URL to stop execution but keep iframe in DOM
@ -276,11 +278,11 @@ export class PluginIndexComponent implements OnInit, OnDestroy {
'data:text/html,<html><body></body></html>',
),
);
Log.log(`Set iframe to empty data URL for plugin: ${currentPluginId}`);
PluginLog.log(`Set iframe to empty data URL for plugin: ${currentPluginId}`);
}
onIframeLoad(): void {
Log.log('Plugin iframe loaded for plugin:', this.pluginId());
PluginLog.log('Plugin iframe loaded for plugin:', this.pluginId());
// Register iframe with cleanup service
if (this.iframeRef?.nativeElement && this.pluginId()) {

View file

@ -31,7 +31,7 @@ import { PluginIconComponent } from '../plugin-icon/plugin-icon.component';
import { IS_ELECTRON } from '../../../app.constants';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DestroyRef } from '@angular/core';
import { Log } from '../../../core/log';
import { PluginLog } from '../../../core/log';
@Component({
selector: 'plugin-management',
@ -139,7 +139,7 @@ export class PluginManagementComponent implements OnInit {
}
private async enablePlugin(plugin: PluginInstance): Promise<void> {
Log.log('Enabling plugin:', plugin.manifest.id);
PluginLog.log('Enabling plugin:', plugin.manifest.id);
try {
// Check if plugin requires Node.js execution consent
@ -147,7 +147,7 @@ export class PluginManagementComponent implements OnInit {
plugin.manifest,
);
if (!hasConsent) {
Log.log(
PluginLog.log(
'User denied Node.js execution permission for plugin:',
plugin.manifest.id,
);
@ -165,18 +165,18 @@ export class PluginManagementComponent implements OnInit {
// Activate the plugin (lazy load if needed)
const instance = await this._pluginService.activatePlugin(plugin.manifest.id);
if (instance) {
Log.log('Plugin activated successfully:', plugin.manifest.id);
PluginLog.log('Plugin activated successfully:', plugin.manifest.id);
}
// Refresh UI
await this.loadPlugins();
} catch (error) {
Log.err('Failed to enable plugin:', error);
PluginLog.err('Failed to enable plugin:', error);
}
}
private async disablePlugin(plugin: PluginInstance): Promise<void> {
Log.log('Disabling plugin:', plugin.manifest.id);
PluginLog.log('Disabling plugin:', plugin.manifest.id);
try {
// Set plugin as disabled in persistence
@ -195,22 +195,22 @@ export class PluginManagementComponent implements OnInit {
// Reload plugins to get the updated state from the service
await this.loadPlugins();
} catch (error) {
Log.err('Failed to disable plugin:', error);
PluginLog.err('Failed to disable plugin:', error);
}
}
async reloadPlugin(plugin: PluginInstance): Promise<void> {
Log.log('Reloading plugin:', plugin.manifest.id);
PluginLog.log('Reloading plugin:', plugin.manifest.id);
try {
const success = await this._pluginService.reloadPlugin(plugin.manifest.id);
if (success) {
Log.log('Plugin reloaded successfully:', plugin.manifest.id);
PluginLog.log('Plugin reloaded successfully:', plugin.manifest.id);
} else {
Log.err('Failed to reload plugin:', plugin.manifest.id);
PluginLog.err('Failed to reload plugin:', plugin.manifest.id);
}
} catch (error) {
Log.err('Failed to reload plugin:', error);
PluginLog.err('Failed to reload plugin:', error);
}
// Refresh the UI
@ -299,7 +299,7 @@ export class PluginManagementComponent implements OnInit {
// Clear the input
input.value = '';
} catch (error) {
Log.err('Failed to load plugin from ZIP:', error);
PluginLog.err('Failed to load plugin from ZIP:', error);
this.uploadError.set(
error instanceof Error
? error.message
@ -318,9 +318,9 @@ export class PluginManagementComponent implements OnInit {
await this._pluginCacheService.clearCache();
await this.loadPlugins(); // Refresh the plugin list
Log.log('Plugin cache cleared successfully');
PluginLog.log('Plugin cache cleared successfully');
} catch (error) {
Log.err('Failed to clear plugin cache:', error);
PluginLog.err('Failed to clear plugin cache:', error);
this.uploadError.set(
error instanceof Error
? error.message
@ -358,9 +358,9 @@ export class PluginManagementComponent implements OnInit {
await this._pluginService.removeUploadedPlugin(plugin.manifest.id);
await this.loadPlugins(); // Refresh the plugin list
Log.log(`Plugin ${plugin.manifest.id} removed successfully`);
PluginLog.log(`Plugin ${plugin.manifest.id} removed successfully`);
} catch (error) {
Log.err('Failed to remove plugin:', error);
PluginLog.err('Failed to remove plugin:', error);
this.uploadError.set(
error instanceof Error
? error.message

View file

@ -12,7 +12,7 @@ import { Store } from '@ngrx/store';
import { selectActivePluginId } from '../../../core-ui/layout/store/layout.reducer';
import { PluginIndexComponent } from '../plugin-index/plugin-index.component';
import { CommonModule } from '@angular/common';
import { Log } from '../../../core/log';
import { PluginLog } from '../../../core/log';
/**
* Container component for rendering plugin iframes in the right panel.
@ -57,7 +57,7 @@ export class PluginPanelContainerComponent implements OnInit, OnDestroy {
.select(selectActivePluginId)
.pipe(filter((pluginId): pluginId is string => !!pluginId))
.subscribe((pluginId) => {
Log.log('Plugin panel container received active plugin ID:', pluginId);
PluginLog.log('Plugin panel container received active plugin ID:', pluginId);
this.activePluginId.set(pluginId);
}),
);

View file

@ -15,7 +15,7 @@ import {
import { toSignal } from '@angular/core/rxjs-interop';
import { filter, map } from 'rxjs/operators';
import { BreakpointObserver } from '@angular/cdk/layout';
import { Log } from '../../core/log';
import { PluginLog } from '../../core/log';
/**
* Component that renders side panel buttons for plugins in the main header.
@ -133,21 +133,21 @@ export class PluginSidePanelBtnsComponent {
});
onButtonClick(button: PluginSidePanelBtnCfg): void {
Log.log('Side panel button clicked:', button.pluginId, button.label);
PluginLog.log('Side panel button clicked:', button.pluginId, button.label);
// Prevent action if not in work view
if (!this.isWorkView()) {
Log.log('Not in work view, ignoring click');
PluginLog.log('Not in work view, ignoring click');
return;
}
Log.log('Dispatching togglePluginPanel action for:', button.pluginId);
PluginLog.log('Dispatching togglePluginPanel action for:', button.pluginId);
// Dispatch action to toggle the plugin panel
this._store.dispatch(togglePluginPanel(button.pluginId));
// Call the original onClick handler if provided
if (button.onClick) {
Log.log('Calling plugin onClick handler for:', button.pluginId);
PluginLog.log('Calling plugin onClick handler for:', button.pluginId);
button.onClick();
}
}

View file

@ -1,6 +1,6 @@
import { PluginBridgeService } from '../plugin-bridge.service';
import { PluginBaseCfg, PluginManifest } from '../plugin-api.model';
import { Log } from '../../core/log';
import { PluginLog } from '../../core/log';
/**
* Simplified plugin iframe utilities following KISS principles.
@ -150,7 +150,7 @@ export const createPluginApiScript = (config: PluginIframeConfig): string => {
try {
handler(data.payload);
} catch (error) {
Log.err('Hook handler error:', error);
PluginLog.err('Hook handler error:', error);
}
});
}
@ -372,7 +372,7 @@ export const handlePluginMessage = async (
// Special handling for registerHook - it needs pluginId as first parameter
if (args.length >= 2) {
const [hook, handlerPlaceholder] = args;
Log.log('Plugin iframe registerHook:', {
PluginLog.log('Plugin iframe registerHook:', {
hook,
handlerPlaceholder,
pluginId: config.pluginId,
@ -524,6 +524,6 @@ export const handlePluginMessage = async (
// Handle plugin ready
if (data.type === 'plugin-ready' && data.pluginId === config.pluginId) {
Log.log(`Plugin ${config.pluginId} is ready`);
PluginLog.log(`Plugin ${config.pluginId} is ready`);
}
};