@pooder/core 0.0.2

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/dist/index.mjs ADDED
@@ -0,0 +1,396 @@
1
+ // src/event.ts
2
+ var EventBus = class {
3
+ constructor() {
4
+ this.events = /* @__PURE__ */ new Map();
5
+ }
6
+ on(event, handler, priority = 0) {
7
+ if (this.events.has(event)) {
8
+ const listeners = this.events.get(event);
9
+ listeners.push({ handler, priority });
10
+ listeners.sort((a, b) => b.priority - a.priority);
11
+ } else {
12
+ this.events.set(event, [{ handler, priority }]);
13
+ }
14
+ }
15
+ off(event, handler) {
16
+ const listeners = this.events.get(event);
17
+ if (!listeners) return;
18
+ const index = listeners.findIndex((l) => l.handler === handler);
19
+ listeners.splice(index, 1);
20
+ }
21
+ emit(event, ...args) {
22
+ const listeners = this.events.get(event);
23
+ if (!listeners) return;
24
+ for (const { handler } of listeners) {
25
+ const result = handler(...args);
26
+ if (result === false) break;
27
+ }
28
+ }
29
+ clear() {
30
+ this.events.clear();
31
+ }
32
+ count(event) {
33
+ var _a, _b;
34
+ return (_b = (_a = this.events.get(event)) == null ? void 0 : _a.length) != null ? _b : 0;
35
+ }
36
+ };
37
+
38
+ // src/command.ts
39
+ var DefaultCommandManager = class {
40
+ constructor(editor) {
41
+ this.editor = editor;
42
+ }
43
+ register(name, command) {
44
+ if (this.editor.commands.has(name)) {
45
+ console.warn(`Command "${name}" already exists. It will be overwritten.`);
46
+ }
47
+ this.editor.commands.set(name, command);
48
+ }
49
+ unregister(name) {
50
+ this.editor.commands.delete(name);
51
+ }
52
+ execute(name, ...args) {
53
+ const command = this.editor.commands.get(name);
54
+ if (!command) {
55
+ console.warn(`Command "${name}" not found`);
56
+ return false;
57
+ }
58
+ try {
59
+ return command.execute(...args);
60
+ } catch (e) {
61
+ console.error(`Error executing command "${name}":`, e);
62
+ return false;
63
+ }
64
+ }
65
+ get(name) {
66
+ return this.editor.commands.get(name);
67
+ }
68
+ has(name) {
69
+ return this.editor.commands.has(name);
70
+ }
71
+ count() {
72
+ return this.editor.commands.size;
73
+ }
74
+ list() {
75
+ return Array.from(this.editor.commands.keys());
76
+ }
77
+ clear() {
78
+ this.editor.commands.clear();
79
+ }
80
+ };
81
+
82
+ // src/extension.ts
83
+ var DefaultExtensionManager = class {
84
+ constructor(editor) {
85
+ this.mounted = false;
86
+ this.editor = editor;
87
+ }
88
+ _registerCommands(extension) {
89
+ if (extension.commands) {
90
+ Object.entries(extension.commands).forEach(([name, command]) => {
91
+ const commandName = `${extension.name}.${name}`;
92
+ this.editor.registerCommand(commandName, command);
93
+ });
94
+ }
95
+ }
96
+ _unregisterCommands(extension) {
97
+ if (extension.commands) {
98
+ Object.keys(extension.commands).forEach((name) => {
99
+ const commandName = `${extension.name}.${name}`;
100
+ this.editor.unregisterCommand(commandName);
101
+ });
102
+ }
103
+ }
104
+ register(extension) {
105
+ var _a, _b;
106
+ if (this.editor.extensions.has(extension.name)) {
107
+ console.warn(`Plugin "${extension.name}" already registered. It will be overwritten.`);
108
+ }
109
+ try {
110
+ if (extension.enabled === void 0) {
111
+ extension.enabled = true;
112
+ }
113
+ this.editor.extensions.set(extension.name, extension);
114
+ (_a = extension.onCreate) == null ? void 0 : _a.call(extension, this.editor);
115
+ } catch (error) {
116
+ console.error(`Error in onCreate hook for plugin "${extension.name}":`, error);
117
+ }
118
+ if (extension.enabled) {
119
+ this._registerCommands(extension);
120
+ if (this.mounted) {
121
+ try {
122
+ (_b = extension.onMount) == null ? void 0 : _b.call(extension, this.editor);
123
+ } catch (error) {
124
+ console.error(`Error in onMount hook for plugin "${extension.name}":`, error);
125
+ }
126
+ }
127
+ }
128
+ console.log(`Plugin "${extension.name}" registered successfully`);
129
+ }
130
+ unregister(name) {
131
+ var _a, _b;
132
+ const extension = this.editor.extensions.get(name);
133
+ if (!extension) {
134
+ console.warn(`Plugin "${name}" not found.`);
135
+ return;
136
+ }
137
+ if (this.mounted && extension.enabled) {
138
+ try {
139
+ (_a = extension.onUnmount) == null ? void 0 : _a.call(extension, this.editor);
140
+ } catch (error) {
141
+ console.error(`Error in onUnmount hook for plugin "${name}":`, error);
142
+ }
143
+ }
144
+ try {
145
+ (_b = extension.onDestroy) == null ? void 0 : _b.call(extension, this.editor);
146
+ } catch (error) {
147
+ console.error(`Error in onDestroy hook for plugin "${name}":`, error);
148
+ }
149
+ this._unregisterCommands(extension);
150
+ this.editor.extensions.delete(name);
151
+ console.log(`Plugin "${name}" unregistered`);
152
+ return true;
153
+ }
154
+ enable(name) {
155
+ var _a;
156
+ const extension = this.get(name);
157
+ if (!extension) {
158
+ console.warn(`Plugin "${name}" not found.`);
159
+ return;
160
+ }
161
+ if (extension.enabled) return;
162
+ extension.enabled = true;
163
+ this._registerCommands(extension);
164
+ if (this.mounted) {
165
+ try {
166
+ (_a = extension.onMount) == null ? void 0 : _a.call(extension, this.editor);
167
+ } catch (error) {
168
+ console.error(`Error in onMount hook for plugin "${name}":`, error);
169
+ }
170
+ }
171
+ }
172
+ disable(name) {
173
+ var _a;
174
+ const extension = this.get(name);
175
+ if (!extension) {
176
+ console.warn(`Plugin "${name}" not found.`);
177
+ return;
178
+ }
179
+ if (!extension.enabled) return;
180
+ extension.enabled = false;
181
+ this._unregisterCommands(extension);
182
+ if (this.mounted) {
183
+ try {
184
+ (_a = extension.onUnmount) == null ? void 0 : _a.call(extension, this.editor);
185
+ } catch (error) {
186
+ console.error(`Error in onUnmount hook for plugin "${name}":`, error);
187
+ }
188
+ }
189
+ }
190
+ get(name) {
191
+ return this.editor.extensions.get(name);
192
+ }
193
+ has(name) {
194
+ return this.editor.extensions.has(name);
195
+ }
196
+ count() {
197
+ return this.editor.extensions.size;
198
+ }
199
+ list() {
200
+ return Array.from(this.editor.extensions.values());
201
+ }
202
+ mount() {
203
+ if (this.mounted) return;
204
+ this.editor.extensions.forEach((extension) => {
205
+ var _a;
206
+ if (extension.enabled) {
207
+ try {
208
+ (_a = extension.onMount) == null ? void 0 : _a.call(extension, this.editor);
209
+ } catch (e) {
210
+ console.error(`Error in onMount hook for plugin "${extension.name}":`, e);
211
+ }
212
+ }
213
+ });
214
+ this.mounted = true;
215
+ }
216
+ update() {
217
+ const state = this.editor.getState();
218
+ this.editor.extensions.forEach((extension) => {
219
+ var _a;
220
+ if (extension.enabled) {
221
+ try {
222
+ (_a = extension.onUpdate) == null ? void 0 : _a.call(extension, this.editor, state);
223
+ } catch (e) {
224
+ console.error(`Error in onUpdate hook for plugin "${extension.name}":`, e);
225
+ }
226
+ }
227
+ });
228
+ }
229
+ destroy() {
230
+ const extensionNames = Array.from(this.editor.extensions.keys());
231
+ extensionNames.forEach((name) => this.unregister(name));
232
+ this.mounted = false;
233
+ }
234
+ };
235
+
236
+ // src/canvas.ts
237
+ import { Canvas } from "fabric";
238
+
239
+ // src/editor.ts
240
+ var PooderEditor = class {
241
+ constructor(el, options = {}) {
242
+ this.extensions = /* @__PURE__ */ new Map();
243
+ this.commands = /* @__PURE__ */ new Map();
244
+ this.destroyed = false;
245
+ this.state = {
246
+ width: options.width || 800,
247
+ height: options.height || 600
248
+ };
249
+ this.canvas = new Canvas(el, { width: this.state.width, height: this.state.height });
250
+ this.eventBus = new EventBus();
251
+ this.commandManager = new DefaultCommandManager(this);
252
+ this.extensionManager = new DefaultExtensionManager(this);
253
+ if (options.extensions && options.extensions.length > 0) {
254
+ options.extensions.forEach(this.extensionManager.register);
255
+ }
256
+ this.extensionManager.mount();
257
+ console.log("Editor initialized with", this.extensionManager.count(), "plugins");
258
+ }
259
+ use(extension) {
260
+ if (this.destroyed) {
261
+ throw new Error("Cannot register plugin: Editor is destroyed");
262
+ }
263
+ this.extensionManager.register(extension);
264
+ this.emit("update", this.state);
265
+ }
266
+ unuse(name) {
267
+ return this.extensionManager.unregister(name);
268
+ }
269
+ getExtension(name) {
270
+ return this.extensionManager.get(name);
271
+ }
272
+ getExtensions() {
273
+ return this.extensionManager.list();
274
+ }
275
+ enableExtension(name) {
276
+ this.extensionManager.enable(name);
277
+ this.emit("update", this.state);
278
+ }
279
+ disableExtension(name) {
280
+ this.extensionManager.disable(name);
281
+ this.emit("update", this.state);
282
+ }
283
+ registerCommand(name, command) {
284
+ this.commandManager.register(name, command);
285
+ }
286
+ unregisterCommand(name) {
287
+ this.commandManager.unregister(name);
288
+ }
289
+ executeCommand(name, ...args) {
290
+ if (this.destroyed) {
291
+ console.warn("Cannot execute command: Editor is destroyed");
292
+ return false;
293
+ }
294
+ this.emit("beforeCommand", name, ...args);
295
+ const result = this.commandManager.execute(name, ...args);
296
+ this.emit("afterCommand", name, args, result);
297
+ return result;
298
+ }
299
+ on(event, handler, priority) {
300
+ this.eventBus.on(event, handler, priority);
301
+ }
302
+ off(event, handler) {
303
+ this.eventBus.off(event, handler);
304
+ }
305
+ emit(event, ...args) {
306
+ this.eventBus.emit(event, ...args);
307
+ }
308
+ getObjects() {
309
+ if (this.destroyed) {
310
+ throw new Error("Cannot get objects: Editor is destroyed");
311
+ }
312
+ return this.canvas.getObjects();
313
+ }
314
+ getObject(id, layerId) {
315
+ var _a;
316
+ let objs;
317
+ if (layerId) {
318
+ objs = (_a = this.getLayer(layerId)) == null ? void 0 : _a.getObjects();
319
+ } else {
320
+ objs = this.getObjects();
321
+ }
322
+ return objs == null ? void 0 : objs.find((obj) => {
323
+ var _a2;
324
+ return ((_a2 = obj == null ? void 0 : obj.data) == null ? void 0 : _a2.id) === id;
325
+ });
326
+ }
327
+ getLayers() {
328
+ return this.getObjects().filter((obj) => obj.type === "group");
329
+ }
330
+ getLayer(id) {
331
+ return this.getLayers().find((obj) => {
332
+ var _a;
333
+ return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id) === id;
334
+ });
335
+ }
336
+ updateState(updater) {
337
+ if (this.destroyed) {
338
+ console.warn("Cannot update state: Editor is destroyed");
339
+ return;
340
+ }
341
+ this.state = updater(this.state);
342
+ this.extensionManager.update();
343
+ this.emit("update", this.state);
344
+ }
345
+ toJSON() {
346
+ return {
347
+ width: this.state.width,
348
+ height: this.state.height,
349
+ metadata: this.state.metadata
350
+ };
351
+ }
352
+ async loadFromJSON(json) {
353
+ }
354
+ getState() {
355
+ return { ...this.state };
356
+ }
357
+ destroy() {
358
+ if (this.destroyed) return;
359
+ this.emit("beforeDestroy");
360
+ this.canvas.dispose();
361
+ this.extensionManager.destroy();
362
+ this.eventBus.clear();
363
+ this.commandManager.clear();
364
+ this.destroyed = true;
365
+ console.log("Editor destroyed");
366
+ }
367
+ isDestroyed() {
368
+ return this.destroyed;
369
+ }
370
+ };
371
+
372
+ // src/layer.ts
373
+ import { Group } from "fabric";
374
+
375
+ // src/obj.ts
376
+ import { FabricObject } from "fabric";
377
+
378
+ // src/index.ts
379
+ import { FabricImage, Ellipse, Rect, Circle, Line, Text, Group as Group2, Path, Point, Pattern, filters } from "fabric";
380
+ export {
381
+ Circle,
382
+ Ellipse,
383
+ Group2 as Group,
384
+ FabricImage as Image,
385
+ Line,
386
+ Path,
387
+ Pattern,
388
+ Point,
389
+ Canvas as PooderCanvas,
390
+ PooderEditor,
391
+ Group as PooderLayer,
392
+ FabricObject as PooderObject,
393
+ Rect,
394
+ Text,
395
+ filters
396
+ };
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@pooder/core",
3
+ "version": "0.0.2",
4
+ "description": "Core logic for Pooder editor",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "keywords": [],
9
+ "author": "",
10
+ "license": "ISC",
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "devDependencies": {
15
+ "tsup": "^8.0.0",
16
+ "typescript": "^5.0.0"
17
+ },
18
+ "dependencies": {
19
+ "fabric": "^7.0.0"
20
+ },
21
+ "scripts": {
22
+ "build": "tsup src/index.ts --format cjs,esm --dts",
23
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
24
+ }
25
+ }
package/src/canvas.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { Canvas } from "fabric";
2
+
3
+ export { Canvas as PooderCanvas }
package/src/command.ts ADDED
@@ -0,0 +1,61 @@
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
+ }
package/src/editor.ts ADDED
@@ -0,0 +1,171 @@
1
+ import {Command, Editor, EditorState, EventHandler, Extension} from "./types";
2
+ import {EventBus} from "./event";
3
+ import {CommandManager, CommandMap, DefaultCommandManager} from "./command";
4
+ import {DefaultExtensionManager, ExtensionManager, ExtensionMap} from "./extension";
5
+ import {PooderCanvas} from "./canvas";
6
+ import {PooderObject} from "./obj";
7
+ import {PooderLayer} from "./layer";
8
+
9
+ export class PooderEditor implements Editor {
10
+ public state: EditorState;
11
+ public canvas: PooderCanvas;
12
+ public extensions: ExtensionMap = new Map()
13
+ public commands: CommandMap = new Map()
14
+
15
+ private eventBus: EventBus;
16
+ private commandManager: CommandManager;
17
+ private extensionManager: ExtensionManager;
18
+
19
+ private destroyed: boolean = false;
20
+
21
+ constructor(el:HTMLCanvasElement, options:{
22
+ width?: number,
23
+ height?: number,
24
+ extensions?: Extension[],
25
+ }={}) {
26
+ this.state = {
27
+ width: options.width || 800,
28
+ height: options.height || 600,
29
+ };
30
+ this.canvas = new PooderCanvas(el, {width: this.state.width, height: this.state.height});
31
+ this.eventBus=new EventBus()
32
+ this.commandManager = new DefaultCommandManager(this);
33
+
34
+ this.extensionManager = new DefaultExtensionManager(this);
35
+ if(options.extensions && options.extensions.length>0){
36
+ options.extensions.forEach(this.extensionManager.register)
37
+ }
38
+ this.extensionManager.mount();
39
+
40
+ console.log('Editor initialized with', this.extensionManager.count(), 'plugins');
41
+ }
42
+
43
+ use(extension: Extension){
44
+ if(this.destroyed){
45
+ throw new Error('Cannot register plugin: Editor is destroyed');
46
+ }
47
+
48
+ this.extensionManager.register(extension)
49
+
50
+ this.emit('update', this.state);
51
+ }
52
+ unuse(name: string) {
53
+ return this.extensionManager.unregister(name)
54
+ }
55
+ getExtension(name: string): Extension | undefined {
56
+ return this.extensionManager.get(name)
57
+ }
58
+ getExtensions():Extension[]{
59
+ return this.extensionManager.list()
60
+ }
61
+ enableExtension(name: string) {
62
+ this.extensionManager.enable(name);
63
+ this.emit('update', this.state);
64
+ }
65
+ disableExtension(name: string) {
66
+ this.extensionManager.disable(name);
67
+ this.emit('update', this.state);
68
+ }
69
+
70
+ registerCommand(name: string, command: Command) {
71
+ this.commandManager.register(name, command)
72
+ }
73
+ unregisterCommand(name: string) {
74
+ this.commandManager.unregister(name)
75
+ }
76
+ executeCommand(name: string, ...args:any[]) {
77
+ if(this.destroyed){
78
+ console.warn('Cannot execute command: Editor is destroyed');
79
+ return false;
80
+ }
81
+
82
+ this.emit('beforeCommand', name, ...args)
83
+
84
+ const result = this.commandManager.execute(name, ...args)
85
+
86
+ this.emit('afterCommand', name, args,result)
87
+
88
+ return result
89
+ }
90
+
91
+
92
+ on(event:string, handler:EventHandler,priority?:number):void{
93
+ this.eventBus.on(event, handler, priority)
94
+ }
95
+ off(event:string, handler:EventHandler):void{
96
+ this.eventBus.off(event, handler)
97
+ }
98
+ emit(event:string, ...args:any[]):void{
99
+ this.eventBus.emit(event, ...args)
100
+ }
101
+
102
+ getObjects():PooderObject[] {
103
+ if(this.destroyed){
104
+ throw new Error('Cannot get objects: Editor is destroyed');
105
+ }
106
+
107
+ return this.canvas.getObjects()
108
+ }
109
+ getObject(id:string,layerId?:string):PooderObject | undefined {
110
+ let objs;
111
+ if(layerId){
112
+ objs=this.getLayer(layerId)?.getObjects()
113
+ }else {
114
+ objs=this.getObjects()
115
+ }
116
+ return objs?.find(obj => obj?.data?.id === id)
117
+ }
118
+ getLayers():PooderLayer[] {
119
+ return this.getObjects().filter(obj => obj.type==="group") as PooderLayer[]
120
+ }
121
+ getLayer(id:string):PooderLayer | undefined {
122
+ return this.getLayers().find(obj => obj?.data?.id === id)
123
+ }
124
+
125
+ updateState(updater:(state:EditorState)=>EditorState) {
126
+ if(this.destroyed){
127
+ console.warn('Cannot update state: Editor is destroyed');
128
+ return;
129
+ }
130
+
131
+ this.state = updater(this.state)
132
+
133
+ this.extensionManager.update()
134
+ this.emit('update', this.state)
135
+ }
136
+
137
+ toJSON() {
138
+ return {
139
+ width: this.state.width,
140
+ height: this.state.height,
141
+ metadata: this.state.metadata,
142
+ }
143
+ }
144
+
145
+ async loadFromJSON(json: any) {}
146
+ getState(): EditorState {
147
+ return { ...this.state }
148
+ }
149
+
150
+ destroy():void{
151
+ if(this.destroyed)return
152
+
153
+ this.emit('beforeDestroy')
154
+
155
+ this.canvas.dispose()
156
+
157
+ this.extensionManager.destroy()
158
+
159
+ this.eventBus.clear()
160
+
161
+ this.commandManager.clear()
162
+
163
+ this.destroyed=true
164
+
165
+ console.log('Editor destroyed');
166
+ }
167
+
168
+ isDestroyed():boolean{
169
+ return this.destroyed
170
+ }
171
+ }
package/src/event.ts ADDED
@@ -0,0 +1,48 @@
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
+ }