@cpreston321/reactools-sdk 0.1.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/README.md +117 -0
- package/dist/api/createPlugin.d.ts +41 -0
- package/dist/api/createPlugin.d.ts.map +1 -0
- package/dist/api/registerTab.d.ts +26 -0
- package/dist/api/registerTab.d.ts.map +1 -0
- package/dist/hooks/useDevTools.d.ts +28 -0
- package/dist/hooks/useDevTools.d.ts.map +1 -0
- package/dist/hooks/useRegisterTab.d.ts +22 -0
- package/dist/hooks/useRegisterTab.d.ts.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +198 -0
- package/dist/registry.d.ts +79 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @cpreston321/reactools-sdk
|
|
2
|
+
|
|
3
|
+
SDK for creating custom tabs and plugins for Reactools DevTools.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @cpreston321/reactools-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Creating Custom Tabs
|
|
12
|
+
|
|
13
|
+
### Using `registerTab`
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { registerTab } from '@cpreston321/reactools-sdk';
|
|
17
|
+
import { Database } from 'lucide-react';
|
|
18
|
+
|
|
19
|
+
const { unregister } = registerTab({
|
|
20
|
+
id: 'my-custom-tab',
|
|
21
|
+
title: 'My Tab',
|
|
22
|
+
icon: Database,
|
|
23
|
+
category: 'app',
|
|
24
|
+
view: {
|
|
25
|
+
type: 'component',
|
|
26
|
+
component: MyTabContent,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Later: unregister()
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Using the Hook
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { useRegisterTab } from '@cpreston321/reactools-sdk';
|
|
37
|
+
|
|
38
|
+
function MyComponent() {
|
|
39
|
+
useRegisterTab({
|
|
40
|
+
id: 'my-tab',
|
|
41
|
+
title: 'My Tab',
|
|
42
|
+
icon: 'Code',
|
|
43
|
+
view: {
|
|
44
|
+
type: 'component',
|
|
45
|
+
component: MyTabContent,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return <div>...</div>;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Creating Plugins
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { createPlugin } from '@cpreston321/reactools-sdk';
|
|
57
|
+
|
|
58
|
+
const myPlugin = createPlugin({
|
|
59
|
+
id: 'my-plugin',
|
|
60
|
+
name: 'My Plugin',
|
|
61
|
+
version: '1.0.0',
|
|
62
|
+
tabs: [
|
|
63
|
+
{
|
|
64
|
+
id: 'overview',
|
|
65
|
+
title: 'Overview',
|
|
66
|
+
icon: 'LayoutDashboard',
|
|
67
|
+
view: { type: 'component', component: OverviewTab },
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
setup: (ctx) => {
|
|
71
|
+
ctx.on('panel:open', () => console.log('Panel opened!'));
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## API Reference
|
|
77
|
+
|
|
78
|
+
### Types
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
interface TabDefinition {
|
|
82
|
+
id: string;
|
|
83
|
+
title: string;
|
|
84
|
+
icon: string | ComponentType<{ className?: string }>;
|
|
85
|
+
category?: 'pinned' | 'app' | 'modules' | 'custom';
|
|
86
|
+
order?: number;
|
|
87
|
+
badge?: number | string | (() => number | string | null);
|
|
88
|
+
show?: () => boolean;
|
|
89
|
+
view: TabViewType;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type TabViewType =
|
|
93
|
+
| { type: 'component'; component: ComponentType }
|
|
94
|
+
| { type: 'iframe'; src: string; permissions?: string[] }
|
|
95
|
+
| { type: 'launch'; label: string; action: () => void };
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Hooks
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { useDevTools, useRegisterTab } from '@cpreston321/reactools-sdk';
|
|
102
|
+
|
|
103
|
+
const { tabs, tabsByCategory, context } = useDevTools();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Events
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { tabRegistry } from '@cpreston321/reactools-sdk';
|
|
110
|
+
|
|
111
|
+
tabRegistry.on('panel:open', () => {});
|
|
112
|
+
tabRegistry.on('panel:close', () => {});
|
|
113
|
+
tabRegistry.on('tab:change', ({ tabId }) => {});
|
|
114
|
+
tabRegistry.on('position:change', ({ position }) => {});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
See the [main documentation](https://github.com/your-username/reactools) for full details.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { PluginDefinition, RegisterDefinition } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Create and register a Reactools plugin
|
|
4
|
+
*
|
|
5
|
+
* Plugins allow you to bundle multiple tabs and setup logic together.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { createPlugin } from '@cpreston321/reactools-sdk';
|
|
10
|
+
*
|
|
11
|
+
* const plugin = createPlugin({
|
|
12
|
+
* id: 'my-plugin',
|
|
13
|
+
* name: 'My Plugin',
|
|
14
|
+
* version: '1.0.0',
|
|
15
|
+
* tabs: [
|
|
16
|
+
* {
|
|
17
|
+
* id: 'overview',
|
|
18
|
+
* title: 'Overview',
|
|
19
|
+
* icon: 'LayoutDashboard',
|
|
20
|
+
* view: { type: 'component', component: OverviewTab },
|
|
21
|
+
* },
|
|
22
|
+
* {
|
|
23
|
+
* id: 'settings',
|
|
24
|
+
* title: 'Settings',
|
|
25
|
+
* icon: 'Settings',
|
|
26
|
+
* view: { type: 'component', component: SettingsTab },
|
|
27
|
+
* },
|
|
28
|
+
* ],
|
|
29
|
+
* setup: (ctx) => {
|
|
30
|
+
* ctx.on('panel:open', () => console.log('Panel opened!'));
|
|
31
|
+
* },
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* // Later: plugin.unregister();
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @param plugin - Plugin definition object
|
|
38
|
+
* @returns Function to unregister the plugin and all its tabs
|
|
39
|
+
*/
|
|
40
|
+
export declare function createPlugin(plugin: PluginDefinition): RegisterDefinition;
|
|
41
|
+
//# sourceMappingURL=createPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createPlugin.d.ts","sourceRoot":"","sources":["../../src/api/createPlugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,gBAAgB,GAAG,kBAAkB,CAEzE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { RegisterDefinition, TabDefinition } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Register a custom tab in the Reactools panel
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { registerTab } from '@cpreston321/reactools-sdk';
|
|
8
|
+
*
|
|
9
|
+
* const tab = registerTab({
|
|
10
|
+
* id: 'my-custom-tab',
|
|
11
|
+
* title: 'My Tab',
|
|
12
|
+
* icon: 'Settings',
|
|
13
|
+
* view: {
|
|
14
|
+
* type: 'component',
|
|
15
|
+
* component: MyTabContent,
|
|
16
|
+
* },
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Later: tab.unregister();
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @param tab - Tab definition object
|
|
23
|
+
* @returns Function to unregister the tab
|
|
24
|
+
*/
|
|
25
|
+
export declare function registerTab(tab: TabDefinition): RegisterDefinition;
|
|
26
|
+
//# sourceMappingURL=registerTab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registerTab.d.ts","sourceRoot":"","sources":["../../src/api/registerTab.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,kBAAkB,CAElE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DevToolsContext, RegisterDefinition, TabDefinition } from '../types';
|
|
2
|
+
export interface UseDevToolsReturn {
|
|
3
|
+
/** All registered tabs */
|
|
4
|
+
tabs: TabDefinition[];
|
|
5
|
+
/** Tabs grouped by category */
|
|
6
|
+
tabsByCategory: Record<string, TabDefinition[]>;
|
|
7
|
+
/** Register a new tab */
|
|
8
|
+
registerTab: (tab: TabDefinition) => RegisterDefinition;
|
|
9
|
+
/** Current devtools context */
|
|
10
|
+
context: DevToolsContext;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Hook to access the devtools state and register tabs
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* function MyComponent() {
|
|
18
|
+
* const { tabs, context, registerTab } = useDevTools();
|
|
19
|
+
*
|
|
20
|
+
* console.log('Panel is open:', context.isOpen);
|
|
21
|
+
* console.log('Active tab:', context.activeTabId);
|
|
22
|
+
*
|
|
23
|
+
* return <div>...</div>;
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function useDevTools(): UseDevToolsReturn;
|
|
28
|
+
//# sourceMappingURL=useDevTools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDevTools.d.ts","sourceRoot":"","sources":["../../src/hooks/useDevTools.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,aAAa,EACd,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,iBAAiB;IAChC,0BAA0B;IAC1B,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;IAChD,yBAAyB;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,kBAAkB,CAAC;IACxD,+BAA+B;IAC/B,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,IAAI,iBAAiB,CA6B/C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TabDefinition } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Hook to register a tab that auto-unregisters on unmount
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* function MyFeature() {
|
|
8
|
+
* useRegisterTab({
|
|
9
|
+
* id: 'my-feature-tab',
|
|
10
|
+
* title: 'My Feature',
|
|
11
|
+
* icon: 'Sparkles',
|
|
12
|
+
* view: { type: 'component', component: MyFeaturePanel },
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* return <div>Feature content...</div>;
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @param tab - Tab definition object
|
|
20
|
+
*/
|
|
21
|
+
export declare function useRegisterTab(tab: TabDefinition): void;
|
|
22
|
+
//# sourceMappingURL=useRegisterTab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRegisterTab.d.ts","sourceRoot":"","sources":["../../src/hooks/useRegisterTab.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAiBvD"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { tabRegistry } from './registry';
|
|
2
|
+
export type { DevToolsContext, DevToolsEvents, PanelPosition, PluginContext, PluginDefinition, TabCategory, TabDefinition, TabViewType, UnregisterFn, } from './types';
|
|
3
|
+
export { registerTab } from './api/registerTab';
|
|
4
|
+
export { createPlugin } from './api/createPlugin';
|
|
5
|
+
export { useDevTools } from './hooks/useDevTools';
|
|
6
|
+
export { useRegisterTab } from './hooks/useRegisterTab';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,YAAY,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,WAAW,EACX,YAAY,GACb,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
var h = Object.defineProperty;
|
|
2
|
+
var b = (i, t, e) => t in i ? h(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e;
|
|
3
|
+
var o = (i, t, e) => b(i, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
import { useSyncExternalStore as a, useCallback as u, useRef as d, useEffect as g } from "react";
|
|
5
|
+
class l {
|
|
6
|
+
constructor() {
|
|
7
|
+
o(this, "tabs", /* @__PURE__ */ new Map());
|
|
8
|
+
o(this, "plugins", /* @__PURE__ */ new Map());
|
|
9
|
+
o(this, "listeners", /* @__PURE__ */ new Set());
|
|
10
|
+
o(this, "eventHandlers", /* @__PURE__ */ new Map());
|
|
11
|
+
// State that gets connected to the panel store
|
|
12
|
+
o(this, "_isOpen", !1);
|
|
13
|
+
o(this, "_position", "bottom");
|
|
14
|
+
o(this, "_activeTabId", null);
|
|
15
|
+
// Cached snapshots for useSyncExternalStore (must return same reference if unchanged)
|
|
16
|
+
o(this, "_cachedTabs", []);
|
|
17
|
+
o(this, "_cachedTabsByCategory", {});
|
|
18
|
+
o(this, "_cachedContext", {
|
|
19
|
+
isOpen: !1,
|
|
20
|
+
position: "bottom",
|
|
21
|
+
activeTabId: null
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Register a new tab
|
|
26
|
+
* @returns Unregister function to remove the tab
|
|
27
|
+
*/
|
|
28
|
+
registerTab(t) {
|
|
29
|
+
return this.tabs.has(t.id) && console.warn(
|
|
30
|
+
`[reactools] Tab with id "${t.id}" already exists. Overwriting.`
|
|
31
|
+
), this.tabs.set(t.id, {
|
|
32
|
+
category: "custom",
|
|
33
|
+
order: 100,
|
|
34
|
+
...t
|
|
35
|
+
}), this.notify(), {
|
|
36
|
+
tab: t,
|
|
37
|
+
unregister: () => {
|
|
38
|
+
this.tabs.delete(t.id), this.notify();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Register a plugin with multiple tabs
|
|
44
|
+
* @returns Unregister function to remove the plugin and its tabs
|
|
45
|
+
*/
|
|
46
|
+
registerPlugin(t) {
|
|
47
|
+
var s;
|
|
48
|
+
this.plugins.set(t.id, t);
|
|
49
|
+
const e = ((s = t.tabs) == null ? void 0 : s.map(
|
|
50
|
+
(n) => this.registerTab({ ...n, id: `${t.id}:${n.id}` })
|
|
51
|
+
)) ?? [];
|
|
52
|
+
if (t.setup) {
|
|
53
|
+
const n = this.createPluginContext(t.id);
|
|
54
|
+
Promise.resolve(t.setup(n)).catch((c) => {
|
|
55
|
+
console.error(`[reactools] Plugin "${t.id}" setup failed:`, c);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
plugin: t,
|
|
60
|
+
unregister: () => {
|
|
61
|
+
for (const n of e)
|
|
62
|
+
n.unregister();
|
|
63
|
+
this.plugins.delete(t.id), this.notify();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get all registered tabs, filtered by visibility
|
|
69
|
+
* Returns cached array for useSyncExternalStore stability
|
|
70
|
+
*/
|
|
71
|
+
getTabs() {
|
|
72
|
+
return this._cachedTabs;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get tabs grouped by category
|
|
76
|
+
* Returns cached object for useSyncExternalStore stability
|
|
77
|
+
*/
|
|
78
|
+
getTabsByCategory() {
|
|
79
|
+
return this._cachedTabsByCategory;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get a tab by ID (including hidden tabs)
|
|
83
|
+
*/
|
|
84
|
+
getTabById(t) {
|
|
85
|
+
return this.tabs.get(t);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Rebuild cached tabs (called on changes)
|
|
89
|
+
*/
|
|
90
|
+
rebuildCache() {
|
|
91
|
+
this._cachedTabs = Array.from(this.tabs.values()).filter((t) => !t.show || t.show()).sort((t, e) => (t.order ?? 100) - (e.order ?? 100)), this._cachedTabsByCategory = this._cachedTabs.reduce(
|
|
92
|
+
(t, e) => {
|
|
93
|
+
const s = e.category ?? "custom";
|
|
94
|
+
return t[s] || (t[s] = []), t[s].push(e), t;
|
|
95
|
+
},
|
|
96
|
+
{}
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Subscribe to registry changes
|
|
101
|
+
*/
|
|
102
|
+
subscribe(t) {
|
|
103
|
+
return this.listeners.add(t), () => this.listeners.delete(t);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Subscribe to a specific event
|
|
107
|
+
*/
|
|
108
|
+
on(t, e) {
|
|
109
|
+
return this.eventHandlers.has(t) || this.eventHandlers.set(t, /* @__PURE__ */ new Set()), this.eventHandlers.get(t).add(e), () => {
|
|
110
|
+
var s;
|
|
111
|
+
return (s = this.eventHandlers.get(t)) == null ? void 0 : s.delete(e);
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Emit an event to all subscribers
|
|
116
|
+
*/
|
|
117
|
+
emit(t, e) {
|
|
118
|
+
const s = this.eventHandlers.get(t);
|
|
119
|
+
if (s)
|
|
120
|
+
for (const n of s)
|
|
121
|
+
n(e);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get the current devtools context
|
|
125
|
+
* Returns cached object for useSyncExternalStore stability
|
|
126
|
+
*/
|
|
127
|
+
getContext() {
|
|
128
|
+
return this._cachedContext;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Update the context (called by the panel store)
|
|
132
|
+
*/
|
|
133
|
+
setContext(t) {
|
|
134
|
+
const e = this._isOpen, s = this._position, n = this._activeTabId;
|
|
135
|
+
t.isOpen !== void 0 && (this._isOpen = t.isOpen), t.position !== void 0 && (this._position = t.position), t.activeTabId !== void 0 && (this._activeTabId = t.activeTabId), (e !== this._isOpen || s !== this._position || n !== this._activeTabId) && (this._cachedContext = {
|
|
136
|
+
isOpen: this._isOpen,
|
|
137
|
+
position: this._position,
|
|
138
|
+
activeTabId: this._activeTabId
|
|
139
|
+
}), e !== this._isOpen && this.emit(this._isOpen ? "panel:open" : "panel:close", void 0), s !== this._position && this.emit("position:change", { position: this._position }), n !== this._activeTabId && this._activeTabId && this.emit("tab:change", { tabId: this._activeTabId });
|
|
140
|
+
}
|
|
141
|
+
notify() {
|
|
142
|
+
this.rebuildCache();
|
|
143
|
+
for (const t of this.listeners)
|
|
144
|
+
t();
|
|
145
|
+
}
|
|
146
|
+
createPluginContext(t) {
|
|
147
|
+
return {
|
|
148
|
+
registerTab: (e) => this.registerTab({ ...e, id: `${t}:${e.id}` }),
|
|
149
|
+
getContext: () => this.getContext(),
|
|
150
|
+
on: (e, s) => this.on(e, s)
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const r = new l();
|
|
155
|
+
function f(i) {
|
|
156
|
+
return r.registerTab(i);
|
|
157
|
+
}
|
|
158
|
+
function _(i) {
|
|
159
|
+
return r.registerPlugin(i);
|
|
160
|
+
}
|
|
161
|
+
function y() {
|
|
162
|
+
const i = a(
|
|
163
|
+
r.subscribe.bind(r),
|
|
164
|
+
r.getTabs.bind(r),
|
|
165
|
+
r.getTabs.bind(r)
|
|
166
|
+
), t = a(
|
|
167
|
+
r.subscribe.bind(r),
|
|
168
|
+
r.getTabsByCategory.bind(r),
|
|
169
|
+
r.getTabsByCategory.bind(r)
|
|
170
|
+
), e = a(
|
|
171
|
+
r.subscribe.bind(r),
|
|
172
|
+
r.getContext.bind(r),
|
|
173
|
+
r.getContext.bind(r)
|
|
174
|
+
), s = u((n) => r.registerTab(n), []);
|
|
175
|
+
return {
|
|
176
|
+
tabs: i,
|
|
177
|
+
tabsByCategory: t,
|
|
178
|
+
registerTab: s,
|
|
179
|
+
context: e
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function v(i) {
|
|
183
|
+
const t = d(null), e = d(i.id);
|
|
184
|
+
g(() => {
|
|
185
|
+
var s;
|
|
186
|
+
return e.current !== i.id && ((s = t.current) == null || s.call(t), e.current = i.id), t.current = r.registerTab(i).unregister, () => {
|
|
187
|
+
var n;
|
|
188
|
+
(n = t.current) == null || n.call(t);
|
|
189
|
+
};
|
|
190
|
+
}, [i.id, i.title, i.icon, i.category, i.order]);
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
_ as createPlugin,
|
|
194
|
+
f as registerTab,
|
|
195
|
+
r as tabRegistry,
|
|
196
|
+
y as useDevTools,
|
|
197
|
+
v as useRegisterTab
|
|
198
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { DevToolsContext, DevToolsEvents, PluginDefinition, TabDefinition } from './types';
|
|
2
|
+
type EventHandler<T> = (payload: T) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Central registry for tabs and plugins
|
|
5
|
+
* This is a singleton that manages all tab registrations and plugin lifecycle
|
|
6
|
+
*/
|
|
7
|
+
declare class TabRegistry {
|
|
8
|
+
private tabs;
|
|
9
|
+
private plugins;
|
|
10
|
+
private listeners;
|
|
11
|
+
private eventHandlers;
|
|
12
|
+
private _isOpen;
|
|
13
|
+
private _position;
|
|
14
|
+
private _activeTabId;
|
|
15
|
+
private _cachedTabs;
|
|
16
|
+
private _cachedTabsByCategory;
|
|
17
|
+
private _cachedContext;
|
|
18
|
+
/**
|
|
19
|
+
* Register a new tab
|
|
20
|
+
* @returns Unregister function to remove the tab
|
|
21
|
+
*/
|
|
22
|
+
registerTab(tab: TabDefinition): {
|
|
23
|
+
tab: TabDefinition;
|
|
24
|
+
unregister: () => void;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Register a plugin with multiple tabs
|
|
28
|
+
* @returns Unregister function to remove the plugin and its tabs
|
|
29
|
+
*/
|
|
30
|
+
registerPlugin(plugin: PluginDefinition): {
|
|
31
|
+
plugin: PluginDefinition;
|
|
32
|
+
unregister: () => void;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Get all registered tabs, filtered by visibility
|
|
36
|
+
* Returns cached array for useSyncExternalStore stability
|
|
37
|
+
*/
|
|
38
|
+
getTabs(): TabDefinition[];
|
|
39
|
+
/**
|
|
40
|
+
* Get tabs grouped by category
|
|
41
|
+
* Returns cached object for useSyncExternalStore stability
|
|
42
|
+
*/
|
|
43
|
+
getTabsByCategory(): Record<string, TabDefinition[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Get a tab by ID (including hidden tabs)
|
|
46
|
+
*/
|
|
47
|
+
getTabById(id: string): TabDefinition | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Rebuild cached tabs (called on changes)
|
|
50
|
+
*/
|
|
51
|
+
private rebuildCache;
|
|
52
|
+
/**
|
|
53
|
+
* Subscribe to registry changes
|
|
54
|
+
*/
|
|
55
|
+
subscribe(listener: () => void): () => void;
|
|
56
|
+
/**
|
|
57
|
+
* Subscribe to a specific event
|
|
58
|
+
*/
|
|
59
|
+
on<T extends keyof DevToolsEvents>(event: T, handler: EventHandler<DevToolsEvents[T]>): () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Emit an event to all subscribers
|
|
62
|
+
*/
|
|
63
|
+
emit<T extends keyof DevToolsEvents>(event: T, payload: DevToolsEvents[T]): void;
|
|
64
|
+
/**
|
|
65
|
+
* Get the current devtools context
|
|
66
|
+
* Returns cached object for useSyncExternalStore stability
|
|
67
|
+
*/
|
|
68
|
+
getContext(): DevToolsContext;
|
|
69
|
+
/**
|
|
70
|
+
* Update the context (called by the panel store)
|
|
71
|
+
*/
|
|
72
|
+
setContext(context: Partial<DevToolsContext>): void;
|
|
73
|
+
private notify;
|
|
74
|
+
private createPluginContext;
|
|
75
|
+
}
|
|
76
|
+
/** Singleton instance of the tab registry */
|
|
77
|
+
export declare const tabRegistry: TabRegistry;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAGd,gBAAgB,EAChB,aAAa,EACd,MAAM,SAAS,CAAC;AAEjB,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;AAE5C;;;GAGG;AACH,cAAM,WAAW;IACf,OAAO,CAAC,IAAI,CAAoC;IAChD,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,aAAa,CAAiD;IAGtE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,YAAY,CAAuB;IAG3C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,qBAAqB,CAAuC;IACpE,OAAO,CAAC,cAAc,CAIpB;IAEF;;;OAGG;IACH,WAAW,CAAC,GAAG,EAAE,aAAa;;;;IAuB9B;;;OAGG;IACH,cAAc,CAAC,MAAM,EAAE,gBAAgB;;;;IA6BvC;;;OAGG;IACH,OAAO,IAAI,aAAa,EAAE;IAI1B;;;OAGG;IACH,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC;IAIpD;;OAEG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIjD;;OAEG;IACH,OAAO,CAAC,YAAY;IAgBpB;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAK3C;;OAEG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,cAAc,EAC/B,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,YAAY,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,GACvC,MAAM,IAAI;IASb;;OAEG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,cAAc,EACjC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GACzB,IAAI;IASP;;;OAGG;IACH,UAAU,IAAI,eAAe;IAI7B;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IAmCnD,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,mBAAmB;CAQ5B;AAED,6CAA6C;AAC7C,eAAO,MAAM,WAAW,aAAoB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
/** Tab categories for organizing tabs in the sidebar */
|
|
3
|
+
export type TabCategory = "pinned" | "app" | "modules" | "custom";
|
|
4
|
+
/** View types for rendering tab content */
|
|
5
|
+
export type TabViewType = {
|
|
6
|
+
type: "component";
|
|
7
|
+
component: ComponentType;
|
|
8
|
+
} | {
|
|
9
|
+
type: "iframe";
|
|
10
|
+
src: string;
|
|
11
|
+
permissions?: string[];
|
|
12
|
+
} | {
|
|
13
|
+
type: "launch";
|
|
14
|
+
label: string;
|
|
15
|
+
action: () => void | Promise<void>;
|
|
16
|
+
};
|
|
17
|
+
/** Definition for a tab in the devtools panel */
|
|
18
|
+
export interface TabDefinition {
|
|
19
|
+
/** Unique identifier for the tab */
|
|
20
|
+
id: string;
|
|
21
|
+
/** Display name shown in tab bar */
|
|
22
|
+
title: string;
|
|
23
|
+
/** Icon - accepts Lucide icon name or React component */
|
|
24
|
+
icon: string | ComponentType<{
|
|
25
|
+
className?: string;
|
|
26
|
+
}>;
|
|
27
|
+
/** Category for grouping tabs (default: 'custom') */
|
|
28
|
+
category?: TabCategory;
|
|
29
|
+
/** View configuration */
|
|
30
|
+
view: TabViewType;
|
|
31
|
+
/** Order priority within category (lower = higher priority, default: 100) */
|
|
32
|
+
order?: number;
|
|
33
|
+
/** Show badge with count or indicator */
|
|
34
|
+
badge?: number | string | (() => number | string | null);
|
|
35
|
+
/** Conditional rendering - return false to hide tab */
|
|
36
|
+
show?: () => boolean;
|
|
37
|
+
}
|
|
38
|
+
export type RegisterDefinition = {
|
|
39
|
+
plugin: PluginDefinition;
|
|
40
|
+
unregister: UnregisterFn;
|
|
41
|
+
} | {
|
|
42
|
+
tab: TabDefinition;
|
|
43
|
+
unregister: UnregisterFn;
|
|
44
|
+
};
|
|
45
|
+
/** Context provided to plugin setup functions */
|
|
46
|
+
export interface PluginContext {
|
|
47
|
+
/** Register a new tab */
|
|
48
|
+
registerTab: (tab: TabDefinition) => RegisterDefinition;
|
|
49
|
+
/** Get the current devtools context */
|
|
50
|
+
getContext: () => DevToolsContext;
|
|
51
|
+
/** Subscribe to devtools events */
|
|
52
|
+
on: <T extends keyof DevToolsEvents>(event: T, handler: (payload: DevToolsEvents[T]) => void) => () => void;
|
|
53
|
+
}
|
|
54
|
+
/** Definition for a devtools plugin */
|
|
55
|
+
export interface PluginDefinition {
|
|
56
|
+
/** Unique plugin identifier */
|
|
57
|
+
id: string;
|
|
58
|
+
/** Plugin display name */
|
|
59
|
+
name: string;
|
|
60
|
+
/** Plugin version */
|
|
61
|
+
version: string;
|
|
62
|
+
/** Tabs contributed by this plugin */
|
|
63
|
+
tabs?: TabDefinition[];
|
|
64
|
+
/** Setup function called when plugin is registered */
|
|
65
|
+
setup?: (context: PluginContext) => void | Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
/** Current devtools context/state */
|
|
68
|
+
export interface DevToolsContext {
|
|
69
|
+
/** Whether the panel is currently open */
|
|
70
|
+
isOpen: boolean;
|
|
71
|
+
/** Current panel position */
|
|
72
|
+
position: PanelPosition;
|
|
73
|
+
/** Currently active tab ID */
|
|
74
|
+
activeTabId: string | null;
|
|
75
|
+
}
|
|
76
|
+
/** Panel position options */
|
|
77
|
+
export type PanelPosition = "top" | "bottom" | "left" | "right" | "floating";
|
|
78
|
+
/** Events emitted by the devtools panel */
|
|
79
|
+
export interface DevToolsEvents {
|
|
80
|
+
"panel:open": undefined;
|
|
81
|
+
"panel:close": undefined;
|
|
82
|
+
"tab:change": {
|
|
83
|
+
tabId: string;
|
|
84
|
+
};
|
|
85
|
+
"position:change": {
|
|
86
|
+
position: PanelPosition;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Unregister function returned by registerTab(), createPlugin(), or integration setup functions.
|
|
91
|
+
* Pass these to the integrations array in ReactoolsProvider.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```tsx
|
|
95
|
+
* import { setupReactQueryIntegration } from '@cpreston321/reactools-integrations/react-query';
|
|
96
|
+
* import { registerTab, createPlugin } from '@cpreston321/reactools-sdk';
|
|
97
|
+
*
|
|
98
|
+
* <ReactoolsProvider config={{
|
|
99
|
+
* integrations: [
|
|
100
|
+
* // Integration setup functions (called)
|
|
101
|
+
* setupReactQueryIntegration(),
|
|
102
|
+
* setupReactRouterIntegration(),
|
|
103
|
+
*
|
|
104
|
+
* // Direct tab registration
|
|
105
|
+
* registerTab({ id: 'my-tab', title: 'My Tab', icon: 'Code', view: { type: 'component', component: MyTab } }),
|
|
106
|
+
*
|
|
107
|
+
* // Direct plugin registration
|
|
108
|
+
* createPlugin({ id: 'my-plugin', name: 'My Plugin', version: '1.0.0', tabs: [...] }),
|
|
109
|
+
* ]
|
|
110
|
+
* }}>
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export type UnregisterFn = () => void;
|
|
114
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cpreston321/reactools-sdk",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./types": {
|
|
14
|
+
"types": "./dist/types.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/react": "^18.3.12",
|
|
25
|
+
"react": "^18.3.1",
|
|
26
|
+
"vite": "^6.0.3",
|
|
27
|
+
"vite-plugin-dts": "^4.3.0"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"dev": "vite build --watch",
|
|
31
|
+
"build": "vite build",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"clean": "rm -rf dist"
|
|
34
|
+
}
|
|
35
|
+
}
|