@pooder/core 0.0.2 → 1.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pooder/core",
3
- "version": "0.0.2",
3
+ "version": "1.0.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
- import {Command, Editor} from "./types";
2
-
3
- export { CommandManager, DefaultCommandManager, CommandMap }
4
-
5
- interface CommandManager{
6
- register(name:string,command:Command):void
7
- unregister(name:string):void
8
- execute(name:string,...args:any[]):boolean
9
- get(name:string):Command | undefined
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
- }
61
- }
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>;
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
+ }
@@ -0,0 +1,3 @@
1
+ export default interface Disposable {
2
+ dispose(): void;
3
+ }
package/src/event.ts CHANGED
@@ -1,48 +1,53 @@
1
- import {EventHandler} from "./types";
2
-
3
- export { EventBus, EventListener }
4
-
5
- interface EventListener {
6
- handler:EventHandler;
7
- priority:number;
8
- }
9
- type EventListenersMap = Map<string, EventListener[]>
10
- class EventBus{
11
- private events: EventListenersMap = new Map()
12
-
13
- on(event:string,handler:EventHandler,priority:number=0){
14
- if(this.events.has(event)){
15
- const listeners=this.events.get(event)
16
- listeners!.push({handler, priority})
17
- listeners!.sort((a,b)=>b.priority-a.priority)
18
- }else {
19
- this.events.set(event,[{handler, priority}])
20
- }
21
- }
22
-
23
- off(event:string,handler:EventHandler){
24
- const listeners=this.events.get(event)
25
- if(!listeners)return
26
-
27
- const index=listeners.findIndex(l=>l.handler===handler)
28
- listeners.splice(index,1)
29
- }
30
-
31
- emit(event:string,...args:any[]){
32
- const listeners=this.events.get(event)
33
- if(!listeners)return
34
-
35
- for(const {handler} of listeners){
36
- const result=handler(...args)
37
- if(result===false)break
38
- }
39
- }
40
-
41
- clear(){
42
- this.events.clear()
43
- }
44
-
45
- count(event:string){
46
- return this.events.get(event)?.length ?? 0
47
- }
48
- }
1
+ type EventHandler = (...args: any[]) => void | boolean;
2
+ type EventListenersMap = Map<string, EventListener[]>;
3
+
4
+ interface Event {
5
+ name: string;
6
+ data: any;
7
+ }
8
+ interface EventListener {
9
+ handler: EventHandler;
10
+ priority: number;
11
+ }
12
+
13
+ class EventBus {
14
+ private events: EventListenersMap = new Map();
15
+
16
+ on(event: string, handler: EventHandler, priority: number = 0) {
17
+ if (this.events.has(event)) {
18
+ const listeners = this.events.get(event);
19
+ listeners!.push({ handler, priority });
20
+ listeners!.sort((a, b) => b.priority - a.priority);
21
+ } else {
22
+ this.events.set(event, [{ handler, priority }]);
23
+ }
24
+ }
25
+
26
+ off(event: string, handler: EventHandler) {
27
+ const listeners = this.events.get(event);
28
+ if (!listeners) return;
29
+
30
+ const index = listeners.findIndex((l) => l.handler === handler);
31
+ listeners.splice(index, 1);
32
+ }
33
+
34
+ emit(event: string, ...args: any[]) {
35
+ const listeners = this.events.get(event);
36
+ if (!listeners) return;
37
+
38
+ for (const { handler } of listeners) {
39
+ const result = handler(...args);
40
+ if (result === false) break;
41
+ }
42
+ }
43
+
44
+ clear() {
45
+ this.events.clear();
46
+ }
47
+
48
+ count(event: string) {
49
+ return this.events.get(event)?.length ?? 0;
50
+ }
51
+ }
52
+
53
+ export default EventBus;