@pooder/core 0.1.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +243 -134
- package/dist/index.d.ts +243 -134
- package/dist/index.js +442 -370
- package/dist/index.mjs +434 -367
- package/package.json +2 -4
- package/src/command.ts +9 -60
- package/src/context.ts +17 -0
- package/src/contribution/index.ts +12 -0
- package/src/contribution/points.ts +63 -0
- package/src/contribution/registry.ts +118 -0
- package/src/disposable.ts +3 -0
- package/src/event.ts +9 -4
- package/src/extension.ts +108 -171
- package/src/index.ts +140 -31
- package/src/run-test-full.ts +98 -0
- package/src/service.ts +25 -0
- package/src/services/CommandService.ts +79 -0
- package/src/services/ConfigurationService.ts +107 -0
- package/src/services/index.ts +4 -0
- package/src/test-extension-full.ts +79 -0
- package/tsconfig.test.json +7 -0
- package/src/canvas.ts +0 -3
- package/src/editor.ts +0 -226
- package/src/layer.ts +0 -13
- package/src/obj.ts +0 -7
- package/src/types.ts +0 -105
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pooder/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Core logic for Pooder editor",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -15,9 +15,7 @@
|
|
|
15
15
|
"tsup": "^8.0.0",
|
|
16
16
|
"typescript": "^5.0.0"
|
|
17
17
|
},
|
|
18
|
-
"dependencies": {
|
|
19
|
-
"fabric": "^7.0.0"
|
|
20
|
-
},
|
|
18
|
+
"dependencies": {},
|
|
21
19
|
"scripts": {
|
|
22
20
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
23
21
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch"
|
package/src/command.ts
CHANGED
|
@@ -1,61 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
has(name: string): boolean;
|
|
11
|
-
count(): number;
|
|
12
|
-
list(): string[];
|
|
13
|
-
clear(): void;
|
|
14
|
-
}
|
|
15
|
-
class CommandMap extends Map<string, Command> {}
|
|
16
|
-
class DefaultCommandManager implements CommandManager {
|
|
17
|
-
private editor: Editor;
|
|
18
|
-
constructor(editor: Editor) {
|
|
19
|
-
this.editor = editor;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
register(name: string, command: Command) {
|
|
23
|
-
if (this.editor.commands.has(name)) {
|
|
24
|
-
console.warn(`Command "${name}" already exists. It will be overwritten.`);
|
|
25
|
-
}
|
|
26
|
-
this.editor.commands.set(name, command);
|
|
27
|
-
}
|
|
28
|
-
unregister(name: string) {
|
|
29
|
-
this.editor.commands.delete(name);
|
|
30
|
-
}
|
|
31
|
-
execute(name: string, ...args: any[]) {
|
|
32
|
-
const command = this.editor.commands.get(name);
|
|
33
|
-
if (!command) {
|
|
34
|
-
console.warn(`Command "${name}" not found`);
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
return command.execute(...args);
|
|
40
|
-
} catch (e) {
|
|
41
|
-
console.error(`Error executing command "${name}":`, e);
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
get(name: string) {
|
|
47
|
-
return this.editor.commands.get(name);
|
|
48
|
-
}
|
|
49
|
-
has(name: string) {
|
|
50
|
-
return this.editor.commands.has(name);
|
|
51
|
-
}
|
|
52
|
-
count(): number {
|
|
53
|
-
return this.editor.commands.size;
|
|
54
|
-
}
|
|
55
|
-
list() {
|
|
56
|
-
return Array.from(this.editor.commands.keys());
|
|
57
|
-
}
|
|
58
|
-
clear() {
|
|
59
|
-
this.editor.commands.clear();
|
|
60
|
-
}
|
|
1
|
+
export interface CommandArgSchema {}
|
|
2
|
+
|
|
3
|
+
export interface Command {
|
|
4
|
+
id: string;
|
|
5
|
+
handler: (...args: any[]) => any;
|
|
6
|
+
title?: string;
|
|
7
|
+
category?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
schema?: Record<string, CommandArgSchema>;
|
|
61
10
|
}
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import EventBus from "./event";
|
|
2
|
+
import { Contribution } from "./contribution";
|
|
3
|
+
import { Service } from "./service";
|
|
4
|
+
import Disposable from "./disposable";
|
|
5
|
+
|
|
6
|
+
interface ExtensionContext {
|
|
7
|
+
readonly eventBus: EventBus;
|
|
8
|
+
readonly services: {
|
|
9
|
+
get<T extends Service>(serviceName: string): T | undefined;
|
|
10
|
+
};
|
|
11
|
+
readonly contributions: {
|
|
12
|
+
get<T>(pointId: string): Contribution<T>[];
|
|
13
|
+
register<T>(pointId: string, contribution: Contribution<T>): Disposable;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { ExtensionContext };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./points";
|
|
2
|
+
export * from "./registry";
|
|
3
|
+
|
|
4
|
+
export interface ContributionMetadata {
|
|
5
|
+
name: string;
|
|
6
|
+
extensionId: string;
|
|
7
|
+
}
|
|
8
|
+
export interface Contribution<T = any> {
|
|
9
|
+
id: string;
|
|
10
|
+
data: T;
|
|
11
|
+
metadata?: Partial<ContributionMetadata>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface ContributionPoint<T = any> {
|
|
2
|
+
id: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
validate?: (data: T) => boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Command Contribution Data Definition
|
|
9
|
+
*/
|
|
10
|
+
export interface CommandContribution {
|
|
11
|
+
id: string;
|
|
12
|
+
command: string;
|
|
13
|
+
title: string;
|
|
14
|
+
handler?: (...args: any[]) => any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Tool Contribution Data Definition
|
|
19
|
+
*/
|
|
20
|
+
export interface ToolContribution {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
parameters?: Record<string, any>; // JSON Schema for parameters
|
|
25
|
+
execute: (...args: any[]) => Promise<any>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* View Contribution Data Definition
|
|
30
|
+
*/
|
|
31
|
+
export interface ViewContribution {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
type: "sidebar" | "panel" | "editor" | "dialog" | "status-bar";
|
|
35
|
+
component: any; // The component implementation (e.g., React component or generic render function)
|
|
36
|
+
location?: string;
|
|
37
|
+
icon?: string;
|
|
38
|
+
priority?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Configuration Contribution Data Definition
|
|
43
|
+
*/
|
|
44
|
+
export interface ConfigurationContribution {
|
|
45
|
+
id: string;
|
|
46
|
+
type: "string" | "number" | "boolean" | "color" | "select" | "json";
|
|
47
|
+
label: string;
|
|
48
|
+
default?: any;
|
|
49
|
+
description?: string;
|
|
50
|
+
options?: string[]; // For select type
|
|
51
|
+
min?: number;
|
|
52
|
+
max?: number;
|
|
53
|
+
step?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Built-in Contribution Point IDs
|
|
57
|
+
export const ContributionPointIds = {
|
|
58
|
+
CONTRIBUTIONS: "contribution.point.contributions",
|
|
59
|
+
COMMANDS: "contribution.point.commands",
|
|
60
|
+
TOOLS: "contribution.point.tools",
|
|
61
|
+
VIEWS: "contribution.point.views",
|
|
62
|
+
CONFIGURATIONS: "contribution.point.configurations",
|
|
63
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Contribution, ContributionPoint, ContributionPointIds } from "./index";
|
|
2
|
+
import Disposable from "../disposable";
|
|
3
|
+
|
|
4
|
+
export class ContributionRegistry {
|
|
5
|
+
private points: Map<string, ContributionPoint> = new Map();
|
|
6
|
+
private contributions = {
|
|
7
|
+
byId: new Map<string, Contribution>(),
|
|
8
|
+
byPointId: new Map<string, Contribution[]>(),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Register a new contribution point
|
|
13
|
+
*/
|
|
14
|
+
registerPoint<T>(point: ContributionPoint<T>): void {
|
|
15
|
+
if (this.points.has(point.id)) {
|
|
16
|
+
console.warn(
|
|
17
|
+
`Contribution point ${point.id} already exists. Overwriting definitions may cause issues.`,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
this.points.set(point.id, point);
|
|
21
|
+
if (!this.contributions.byPointId.has(point.id)) {
|
|
22
|
+
this.contributions.byPointId.set(point.id, []);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Register a contribution to a specific point
|
|
28
|
+
* @returns Disposable to unregister the contribution
|
|
29
|
+
*/
|
|
30
|
+
register<T>(pointId: string, contribution: Contribution<T>): Disposable {
|
|
31
|
+
if (this.contributions.byId.has(contribution.id)) {
|
|
32
|
+
console.warn(
|
|
33
|
+
`Contribution with ID "${contribution.id}" is already registered. Overwriting.`,
|
|
34
|
+
);
|
|
35
|
+
// We could choose to throw, or overwrite. Let's overwrite for now but warn.
|
|
36
|
+
// If we overwrite, we should probably remove the old one from the list first to avoid duplicates.
|
|
37
|
+
this.unregister(pointId, contribution.id);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!this.points.has(pointId)) {
|
|
41
|
+
console.warn(
|
|
42
|
+
`Contribution point ${pointId} does not exist. The contribution ${contribution.id} will be queued but may not be valid.`,
|
|
43
|
+
);
|
|
44
|
+
if (!this.contributions.byPointId.has(pointId)) {
|
|
45
|
+
this.contributions.byPointId.set(pointId, []);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const point = this.points.get(pointId);
|
|
50
|
+
if (point?.validate) {
|
|
51
|
+
try {
|
|
52
|
+
if (!point.validate(contribution.data)) {
|
|
53
|
+
console.error(
|
|
54
|
+
`Contribution ${contribution.id} failed validation for point ${pointId}.`,
|
|
55
|
+
);
|
|
56
|
+
return { dispose: () => {} };
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error(
|
|
60
|
+
`Validation error for contribution ${contribution.id}:`,
|
|
61
|
+
e,
|
|
62
|
+
);
|
|
63
|
+
return { dispose: () => {} };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const arr = this.contributions.byPointId.get(pointId)!;
|
|
68
|
+
|
|
69
|
+
arr.push(contribution);
|
|
70
|
+
this.contributions.byId.set(contribution.id, contribution);
|
|
71
|
+
|
|
72
|
+
// Auto-register if this is a contribution point contribution
|
|
73
|
+
if (pointId === ContributionPointIds.CONTRIBUTIONS) {
|
|
74
|
+
this.registerPoint(contribution.data as ContributionPoint);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
dispose: () => {
|
|
79
|
+
this.unregister(pointId, contribution.id);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get all contributions for a given point
|
|
86
|
+
*/
|
|
87
|
+
get<T>(pointId: string): Contribution<T>[] {
|
|
88
|
+
return Array.from(
|
|
89
|
+
this.contributions.byPointId.get(pointId)?.values() || [],
|
|
90
|
+
) as Contribution<T>[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get a specific contribution by ID
|
|
95
|
+
*/
|
|
96
|
+
getById<T>(id: string): Contribution<T> | undefined {
|
|
97
|
+
return this.contributions.byId.get(id) as Contribution<T> | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the contribution point definition
|
|
102
|
+
*/
|
|
103
|
+
getPoint(pointId: string): ContributionPoint | undefined {
|
|
104
|
+
return this.points.get(pointId);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Unregister a contribution
|
|
109
|
+
*/
|
|
110
|
+
private unregister(pointId: string, contributionId: string): void {
|
|
111
|
+
const arr = this.contributions.byPointId.get(pointId);
|
|
112
|
+
arr?.splice(
|
|
113
|
+
arr.findIndex((c) => c.id === contributionId),
|
|
114
|
+
1,
|
|
115
|
+
);
|
|
116
|
+
this.contributions.byId.delete(contributionId);
|
|
117
|
+
}
|
|
118
|
+
}
|
package/src/event.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export { EventBus, EventListener };
|
|
1
|
+
type EventHandler = (...args: any[]) => void | boolean;
|
|
2
|
+
type EventListenersMap = Map<string, EventListener[]>;
|
|
4
3
|
|
|
4
|
+
interface Event {
|
|
5
|
+
name: string;
|
|
6
|
+
data: any;
|
|
7
|
+
}
|
|
5
8
|
interface EventListener {
|
|
6
9
|
handler: EventHandler;
|
|
7
10
|
priority: number;
|
|
8
11
|
}
|
|
9
|
-
|
|
12
|
+
|
|
10
13
|
class EventBus {
|
|
11
14
|
private events: EventListenersMap = new Map();
|
|
12
15
|
|
|
@@ -46,3 +49,5 @@ class EventBus {
|
|
|
46
49
|
return this.events.get(event)?.length ?? 0;
|
|
47
50
|
}
|
|
48
51
|
}
|
|
52
|
+
|
|
53
|
+
export default EventBus;
|
package/src/extension.ts
CHANGED
|
@@ -1,227 +1,164 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
disable(name: string): void;
|
|
10
|
-
get(name: string): Extension | undefined;
|
|
11
|
-
has(name: string): boolean;
|
|
12
|
-
count(): number;
|
|
13
|
-
list(): Extension[];
|
|
14
|
-
mount(): void;
|
|
15
|
-
unmount(): void;
|
|
16
|
-
update(): void;
|
|
17
|
-
destroy(): void;
|
|
1
|
+
import { ExtensionContext } from "./context";
|
|
2
|
+
import { Contribution, ContributionPointIds } from "./contribution";
|
|
3
|
+
import Disposable from "./disposable";
|
|
4
|
+
import CommandService from "./services/CommandService";
|
|
5
|
+
import { ConfigurationService } from "./services";
|
|
6
|
+
|
|
7
|
+
interface ExtensionMetadata {
|
|
8
|
+
name: string;
|
|
18
9
|
}
|
|
19
|
-
class ExtensionMap extends Map<string, Extension> {}
|
|
20
|
-
class DefaultExtensionManager implements ExtensionManager {
|
|
21
|
-
private readonly editor: Editor;
|
|
22
|
-
private mounted: boolean = false;
|
|
23
10
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
interface Extension {
|
|
12
|
+
id: string;
|
|
13
|
+
metadata?: Partial<ExtensionMetadata>;
|
|
27
14
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.editor.registerCommand(commandName, command);
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
|
15
|
+
activate(context: ExtensionContext): void;
|
|
16
|
+
deactivate(context: ExtensionContext): void;
|
|
17
|
+
contribute?(): Record<string, any[]>;
|
|
18
|
+
}
|
|
36
19
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
20
|
+
class ExtensionRegistry extends Map<string, Extension> {}
|
|
21
|
+
|
|
22
|
+
class ExtensionManager {
|
|
23
|
+
private readonly context: ExtensionContext;
|
|
24
|
+
private extensionRegistry: ExtensionRegistry = new ExtensionRegistry();
|
|
25
|
+
private extensionDisposables: Map<string, Disposable[]> = new Map();
|
|
26
|
+
|
|
27
|
+
constructor(context: ExtensionContext) {
|
|
28
|
+
this.context = context;
|
|
44
29
|
}
|
|
45
30
|
|
|
46
31
|
register(extension: Extension) {
|
|
47
|
-
if (this.
|
|
32
|
+
if (this.extensionRegistry.has(extension.id)) {
|
|
48
33
|
console.warn(
|
|
49
|
-
`Plugin "${extension.
|
|
34
|
+
`Plugin "${extension.id}" already registered. It will be overwritten.`,
|
|
50
35
|
);
|
|
51
36
|
}
|
|
52
37
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
// Initialize disposables for this extension
|
|
39
|
+
this.extensionDisposables.set(extension.id, []);
|
|
40
|
+
const disposables = this.extensionDisposables.get(extension.id)!;
|
|
41
|
+
|
|
42
|
+
// Process declarative contributions
|
|
43
|
+
if (extension.contribute) {
|
|
44
|
+
for (const [pointId, items] of Object.entries(extension.contribute())) {
|
|
45
|
+
if (Array.isArray(items)) {
|
|
46
|
+
items.forEach((item, index) => {
|
|
47
|
+
const contributionId =
|
|
48
|
+
item.id ||
|
|
49
|
+
(item.command
|
|
50
|
+
? item.command
|
|
51
|
+
: `${extension.id}.${pointId}.${index}`);
|
|
52
|
+
const contribution: Contribution = {
|
|
53
|
+
id: contributionId,
|
|
54
|
+
metadata: {
|
|
55
|
+
extensionId: extension.id,
|
|
56
|
+
...item?.metadata,
|
|
57
|
+
},
|
|
58
|
+
data: item,
|
|
59
|
+
};
|
|
60
|
+
const disposable = this.context.contributions.register(
|
|
61
|
+
pointId,
|
|
62
|
+
contribution,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Track contribution registration to unregister later
|
|
66
|
+
disposables.push(disposable);
|
|
67
|
+
|
|
68
|
+
const dispose = this.collectContribution(pointId, contribution);
|
|
69
|
+
if (dispose) {
|
|
70
|
+
disposables.push(dispose);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
56
74
|
}
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
this.extensionRegistry.set(extension.id, extension);
|
|
79
|
+
this.context.eventBus.emit("extension:register", extension);
|
|
59
80
|
} catch (error) {
|
|
60
81
|
console.error(
|
|
61
|
-
`Error in onCreate hook for plugin "${extension.
|
|
82
|
+
`Error in onCreate hook for plugin "${extension.id}":`,
|
|
62
83
|
error,
|
|
63
84
|
);
|
|
64
85
|
}
|
|
65
86
|
|
|
66
|
-
|
|
67
|
-
this.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
try {
|
|
88
|
+
extension.activate(this.context);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error(
|
|
91
|
+
`Error in onActivate hook for plugin "${extension.id}":`,
|
|
92
|
+
error,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`Plugin "${extension.id}" registered successfully`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
collectContribution(pointId: string, item: any): Disposable | undefined {
|
|
100
|
+
// If registering configurations, update ConfigurationService defaults
|
|
101
|
+
if (pointId === ContributionPointIds.CONFIGURATIONS) {
|
|
102
|
+
const configService = this.context.services.get<ConfigurationService>(
|
|
103
|
+
"ConfigurationService",
|
|
104
|
+
);
|
|
105
|
+
configService?.initializeDefaults([item.data]);
|
|
79
106
|
}
|
|
107
|
+
if (pointId === ContributionPointIds.COMMANDS && item.data.handler) {
|
|
108
|
+
const commandService =
|
|
109
|
+
this.context.services.get<CommandService>("CommandService")!;
|
|
80
110
|
|
|
81
|
-
|
|
111
|
+
return commandService.registerCommand(item.id, item.data.handler);
|
|
112
|
+
}
|
|
82
113
|
}
|
|
83
114
|
|
|
84
115
|
unregister(name: string) {
|
|
85
|
-
const extension = this.
|
|
116
|
+
const extension = this.extensionRegistry.get(name);
|
|
86
117
|
if (!extension) {
|
|
87
118
|
console.warn(`Plugin "${name}" not found.`);
|
|
88
119
|
return;
|
|
89
120
|
}
|
|
90
121
|
|
|
91
|
-
if (this.mounted && extension.enabled) {
|
|
92
|
-
try {
|
|
93
|
-
extension.onUnmount?.(this.editor);
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.error(`Error in onUnmount hook for plugin "${name}":`, error);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
122
|
try {
|
|
100
|
-
extension.
|
|
123
|
+
extension.deactivate(this.context);
|
|
101
124
|
} catch (error) {
|
|
102
|
-
console.error(`Error in
|
|
125
|
+
console.error(`Error in deactivate for plugin "${name}":`, error);
|
|
103
126
|
}
|
|
104
127
|
|
|
105
|
-
this
|
|
128
|
+
// Dispose all resources associated with this extension
|
|
129
|
+
const disposables = this.extensionDisposables.get(name);
|
|
130
|
+
if (disposables) {
|
|
131
|
+
disposables.forEach((d) => d.dispose());
|
|
132
|
+
this.extensionDisposables.delete(name);
|
|
133
|
+
}
|
|
106
134
|
|
|
107
|
-
this.
|
|
135
|
+
this.extensionRegistry.delete(name);
|
|
108
136
|
console.log(`Plugin "${name}" unregistered`);
|
|
109
137
|
return true;
|
|
110
138
|
}
|
|
111
139
|
|
|
112
140
|
enable(name: string) {
|
|
113
|
-
const extension = this.get(name);
|
|
141
|
+
const extension = this.extensionRegistry.get(name);
|
|
114
142
|
if (!extension) {
|
|
115
143
|
console.warn(`Plugin "${name}" not found.`);
|
|
116
144
|
return;
|
|
117
145
|
}
|
|
118
|
-
if (extension.enabled) return;
|
|
119
|
-
|
|
120
|
-
extension.enabled = true;
|
|
121
|
-
this._registerCommands(extension);
|
|
122
|
-
|
|
123
|
-
if (this.mounted) {
|
|
124
|
-
try {
|
|
125
|
-
extension.onMount?.(this.editor);
|
|
126
|
-
} catch (error) {
|
|
127
|
-
console.error(`Error in onMount hook for plugin "${name}":`, error);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
146
|
}
|
|
131
147
|
|
|
132
148
|
disable(name: string) {
|
|
133
|
-
const extension = this.get(name);
|
|
149
|
+
const extension = this.extensionRegistry.get(name);
|
|
134
150
|
if (!extension) {
|
|
135
151
|
console.warn(`Plugin "${name}" not found.`);
|
|
136
152
|
return;
|
|
137
153
|
}
|
|
138
|
-
if (!extension.enabled) return;
|
|
139
|
-
|
|
140
|
-
extension.enabled = false;
|
|
141
|
-
this._unregisterCommands(extension);
|
|
142
|
-
|
|
143
|
-
if (this.mounted) {
|
|
144
|
-
try {
|
|
145
|
-
extension.onUnmount?.(this.editor);
|
|
146
|
-
} catch (error) {
|
|
147
|
-
console.error(`Error in onUnmount hook for plugin "${name}":`, error);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
|
|
153
|
-
return this.editor.extensions.get(name);
|
|
154
|
-
}
|
|
155
|
-
has(name: string) {
|
|
156
|
-
return this.editor.extensions.has(name);
|
|
157
|
-
}
|
|
158
|
-
count() {
|
|
159
|
-
return this.editor.extensions.size;
|
|
160
|
-
}
|
|
161
|
-
list() {
|
|
162
|
-
return Array.from(this.editor.extensions.values());
|
|
163
|
-
}
|
|
164
|
-
mount() {
|
|
165
|
-
if (this.mounted) return;
|
|
166
|
-
|
|
167
|
-
this.editor.extensions.forEach((extension) => {
|
|
168
|
-
if (extension.enabled) {
|
|
169
|
-
try {
|
|
170
|
-
console.log(`Mounting plugin "${extension.name}"`);
|
|
171
|
-
extension.onMount?.(this.editor);
|
|
172
|
-
} catch (e) {
|
|
173
|
-
console.error(
|
|
174
|
-
`Error in onMount hook for plugin "${extension.name}":`,
|
|
175
|
-
e,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
console.log(`Plugins mounted`);
|
|
181
|
-
|
|
182
|
-
this.mounted = true;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
unmount() {
|
|
186
|
-
if (!this.mounted) return;
|
|
187
|
-
|
|
188
|
-
this.editor.extensions.forEach((extension) => {
|
|
189
|
-
if (extension.enabled) {
|
|
190
|
-
try {
|
|
191
|
-
extension.onUnmount?.(this.editor);
|
|
192
|
-
} catch (e) {
|
|
193
|
-
console.error(
|
|
194
|
-
`Error in onUnmount hook for plugin "${extension.name}":`,
|
|
195
|
-
e,
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
console.log(`Plugins unmounted`);
|
|
201
|
-
|
|
202
|
-
this.mounted = false;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
update() {
|
|
206
|
-
const state = this.editor.getState();
|
|
207
|
-
|
|
208
|
-
this.editor.extensions.forEach((extension) => {
|
|
209
|
-
if (extension.enabled) {
|
|
210
|
-
try {
|
|
211
|
-
extension.onUpdate?.(this.editor, state);
|
|
212
|
-
} catch (e) {
|
|
213
|
-
console.error(
|
|
214
|
-
`Error in onUpdate hook for plugin "${extension.name}":`,
|
|
215
|
-
e,
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
}
|
|
156
|
+
update() {}
|
|
221
157
|
|
|
222
158
|
destroy() {
|
|
223
|
-
const extensionNames = Array.from(this.
|
|
159
|
+
const extensionNames = Array.from(this.extensionRegistry.keys());
|
|
224
160
|
extensionNames.forEach((name) => this.unregister(name));
|
|
225
|
-
this.mounted = false;
|
|
226
161
|
}
|
|
227
162
|
}
|
|
163
|
+
|
|
164
|
+
export { Extension, ExtensionMetadata, ExtensionRegistry, ExtensionManager };
|