@liteforge/devtools 0.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/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/buffer.d.ts +16 -0
- package/dist/buffer.d.ts.map +1 -0
- package/dist/buffer.js +114 -0
- package/dist/buffer.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/internals.d.ts +21 -0
- package/dist/internals.d.ts.map +1 -0
- package/dist/internals.js +27 -0
- package/dist/internals.js.map +1 -0
- package/dist/panel/Panel.d.ts +14 -0
- package/dist/panel/Panel.d.ts.map +1 -0
- package/dist/panel/Panel.js +191 -0
- package/dist/panel/Panel.js.map +1 -0
- package/dist/plugin.d.ts +59 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +274 -0
- package/dist/plugin.js.map +1 -0
- package/dist/styles.d.ts +74 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +351 -0
- package/dist/styles.js.map +1 -0
- package/dist/tabs/ComponentTree.d.ts +19 -0
- package/dist/tabs/ComponentTree.d.ts.map +1 -0
- package/dist/tabs/ComponentTree.js +189 -0
- package/dist/tabs/ComponentTree.js.map +1 -0
- package/dist/tabs/Performance.d.ts +19 -0
- package/dist/tabs/Performance.d.ts.map +1 -0
- package/dist/tabs/Performance.js +169 -0
- package/dist/tabs/Performance.js.map +1 -0
- package/dist/tabs/RouterInspector.d.ts +19 -0
- package/dist/tabs/RouterInspector.d.ts.map +1 -0
- package/dist/tabs/RouterInspector.js +169 -0
- package/dist/tabs/RouterInspector.js.map +1 -0
- package/dist/tabs/SignalInspector.d.ts +19 -0
- package/dist/tabs/SignalInspector.d.ts.map +1 -0
- package/dist/tabs/SignalInspector.js +202 -0
- package/dist/tabs/SignalInspector.js.map +1 -0
- package/dist/tabs/StoreExplorer.d.ts +21 -0
- package/dist/tabs/StoreExplorer.d.ts.map +1 -0
- package/dist/tabs/StoreExplorer.js +440 -0
- package/dist/tabs/StoreExplorer.js.map +1 -0
- package/dist/types.d.ts +169 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SchildW3rk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# @liteforge/devtools
|
|
2
|
+
|
|
3
|
+
Debug panel for LiteForge applications with signal inspection, store time-travel, and performance monitoring.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @liteforge/devtools @liteforge/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependency: `@liteforge/core >= 0.1.0`
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createApp } from '@liteforge/runtime'
|
|
17
|
+
import { devtoolsPlugin } from '@liteforge/devtools'
|
|
18
|
+
|
|
19
|
+
createApp({
|
|
20
|
+
plugins: [
|
|
21
|
+
devtoolsPlugin({
|
|
22
|
+
shortcut: 'ctrl+shift+d', // Toggle shortcut
|
|
23
|
+
position: 'bottom', // 'bottom' | 'right' | 'left'
|
|
24
|
+
height: 300, // Panel height in pixels
|
|
25
|
+
defaultTab: 'signals' // Initial tab
|
|
26
|
+
})
|
|
27
|
+
]
|
|
28
|
+
}).mount(App)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
The devtools panel has five tabs:
|
|
34
|
+
|
|
35
|
+
### Signals Tab
|
|
36
|
+
|
|
37
|
+
- View all active signals and their current values
|
|
38
|
+
- See update counts for each signal
|
|
39
|
+
- Track dependency relationships
|
|
40
|
+
- Filter by signal name
|
|
41
|
+
|
|
42
|
+
### Stores Tab
|
|
43
|
+
|
|
44
|
+
- Inspect all registered stores
|
|
45
|
+
- View current state tree
|
|
46
|
+
- **Time-travel debugging** — restore any previous state
|
|
47
|
+
- Track state changes over time
|
|
48
|
+
|
|
49
|
+
### Router Tab
|
|
50
|
+
|
|
51
|
+
- View navigation history
|
|
52
|
+
- See guard execution results
|
|
53
|
+
- Monitor route timing
|
|
54
|
+
- Inspect current route params and query
|
|
55
|
+
|
|
56
|
+
### Components Tab
|
|
57
|
+
|
|
58
|
+
- Component tree visualization
|
|
59
|
+
- Mount/unmount tracking
|
|
60
|
+
- Component instance counts
|
|
61
|
+
- Lifecycle timing
|
|
62
|
+
|
|
63
|
+
### Performance Tab
|
|
64
|
+
|
|
65
|
+
- Signal updates per second
|
|
66
|
+
- Effect executions
|
|
67
|
+
- Component mount/unmount rate
|
|
68
|
+
- Memory usage indicators
|
|
69
|
+
|
|
70
|
+
## API
|
|
71
|
+
|
|
72
|
+
### devtoolsPlugin
|
|
73
|
+
|
|
74
|
+
Creates the devtools plugin for `createApp`.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { devtoolsPlugin } from '@liteforge/devtools'
|
|
78
|
+
|
|
79
|
+
devtoolsPlugin({
|
|
80
|
+
// Keyboard shortcut to toggle panel
|
|
81
|
+
shortcut: 'ctrl+shift+d',
|
|
82
|
+
|
|
83
|
+
// Panel position
|
|
84
|
+
position: 'bottom', // 'bottom' | 'right' | 'left'
|
|
85
|
+
|
|
86
|
+
// Panel size
|
|
87
|
+
height: 300, // For bottom position
|
|
88
|
+
width: 400, // For left/right position
|
|
89
|
+
|
|
90
|
+
// Initial tab
|
|
91
|
+
defaultTab: 'signals',
|
|
92
|
+
|
|
93
|
+
// Enable in production (default: false)
|
|
94
|
+
enableInProduction: false,
|
|
95
|
+
|
|
96
|
+
// Buffer size for events
|
|
97
|
+
bufferSize: 1000
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### createDevTools
|
|
102
|
+
|
|
103
|
+
For standalone usage without `createApp`:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { createDevTools } from '@liteforge/devtools'
|
|
107
|
+
import { storeRegistry } from '@liteforge/store'
|
|
108
|
+
|
|
109
|
+
const devtools = createDevTools({
|
|
110
|
+
stores: storeRegistry,
|
|
111
|
+
shortcut: 'ctrl+shift+d'
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// Manual control
|
|
115
|
+
devtools.show()
|
|
116
|
+
devtools.hide()
|
|
117
|
+
devtools.toggle()
|
|
118
|
+
devtools.destroy()
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Keyboard Shortcuts
|
|
122
|
+
|
|
123
|
+
| Shortcut | Action |
|
|
124
|
+
|----------|--------|
|
|
125
|
+
| `Ctrl+Shift+D` | Toggle panel (default) |
|
|
126
|
+
| `Escape` | Close panel |
|
|
127
|
+
| `1-5` | Switch tabs (when panel focused) |
|
|
128
|
+
|
|
129
|
+
## Time-Travel Debugging
|
|
130
|
+
|
|
131
|
+
The Stores tab supports time-travel debugging:
|
|
132
|
+
|
|
133
|
+
1. Make changes to store state
|
|
134
|
+
2. Open the Stores tab
|
|
135
|
+
3. See state history with timestamps
|
|
136
|
+
4. Click any history entry to restore that state
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
// State changes are automatically recorded
|
|
140
|
+
userStore.actions.login(user)
|
|
141
|
+
userStore.actions.updateProfile(profile)
|
|
142
|
+
|
|
143
|
+
// In devtools, you can:
|
|
144
|
+
// - See each state change
|
|
145
|
+
// - Click to restore any previous state
|
|
146
|
+
// - Continue from that point
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Conditional Loading
|
|
150
|
+
|
|
151
|
+
Only load devtools in development:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { createApp } from '@liteforge/runtime'
|
|
155
|
+
|
|
156
|
+
const plugins = []
|
|
157
|
+
|
|
158
|
+
if (import.meta.env.DEV) {
|
|
159
|
+
const { devtoolsPlugin } = await import('@liteforge/devtools')
|
|
160
|
+
plugins.push(devtoolsPlugin())
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
createApp({ plugins }).mount(App)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Custom Panels
|
|
167
|
+
|
|
168
|
+
Extend devtools with custom panels (advanced):
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { createDevTools } from '@liteforge/devtools'
|
|
172
|
+
|
|
173
|
+
const devtools = createDevTools({
|
|
174
|
+
stores: storeRegistry,
|
|
175
|
+
customPanels: [
|
|
176
|
+
{
|
|
177
|
+
id: 'network',
|
|
178
|
+
label: 'Network',
|
|
179
|
+
render: () => {
|
|
180
|
+
// Return DOM element
|
|
181
|
+
const div = document.createElement('div')
|
|
182
|
+
div.innerHTML = '<h3>Network Requests</h3>'
|
|
183
|
+
return div
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
})
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Types
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import type {
|
|
194
|
+
DevToolsConfig,
|
|
195
|
+
DevToolsInstance,
|
|
196
|
+
PanelPosition,
|
|
197
|
+
TabId,
|
|
198
|
+
PanelState,
|
|
199
|
+
SignalInfo,
|
|
200
|
+
StoreInfo,
|
|
201
|
+
StoreHistoryEntry,
|
|
202
|
+
NavigationInfo,
|
|
203
|
+
ComponentInfo,
|
|
204
|
+
PerformanceCounters
|
|
205
|
+
} from '@liteforge/devtools'
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
MIT
|
package/dist/buffer.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteForge DevTools Event Buffer
|
|
3
|
+
*
|
|
4
|
+
* A circular buffer (ring buffer) for storing debug events.
|
|
5
|
+
* IMPORTANT: This module must NOT use LiteForge signals to avoid
|
|
6
|
+
* infinite loops (buffer receives signal events → creates more signals).
|
|
7
|
+
*/
|
|
8
|
+
import type { EventBuffer } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Create a circular event buffer with a fixed maximum size.
|
|
11
|
+
*
|
|
12
|
+
* @param maxSize - Maximum number of events to store
|
|
13
|
+
* @returns An event buffer instance
|
|
14
|
+
*/
|
|
15
|
+
export declare function createEventBuffer(maxSize: number): EventBuffer;
|
|
16
|
+
//# sourceMappingURL=buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buffer.d.ts","sourceRoot":"","sources":["../src/buffer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,YAAY,CAAC;AAM3D;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAyG9D"}
|
package/dist/buffer.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteForge DevTools Event Buffer
|
|
3
|
+
*
|
|
4
|
+
* A circular buffer (ring buffer) for storing debug events.
|
|
5
|
+
* IMPORTANT: This module must NOT use LiteForge signals to avoid
|
|
6
|
+
* infinite loops (buffer receives signal events → creates more signals).
|
|
7
|
+
*/
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Circular Buffer Implementation
|
|
10
|
+
// ============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Create a circular event buffer with a fixed maximum size.
|
|
13
|
+
*
|
|
14
|
+
* @param maxSize - Maximum number of events to store
|
|
15
|
+
* @returns An event buffer instance
|
|
16
|
+
*/
|
|
17
|
+
export function createEventBuffer(maxSize) {
|
|
18
|
+
// Internal storage
|
|
19
|
+
const events = [];
|
|
20
|
+
let nextId = 0;
|
|
21
|
+
let startIndex = 0;
|
|
22
|
+
let count = 0;
|
|
23
|
+
// Subscriber callbacks (no signals to avoid debug event loops)
|
|
24
|
+
const subscribers = new Set();
|
|
25
|
+
/**
|
|
26
|
+
* Add an event to the buffer.
|
|
27
|
+
* If the buffer is full, the oldest event is overwritten.
|
|
28
|
+
*/
|
|
29
|
+
function push(event) {
|
|
30
|
+
const storedEvent = {
|
|
31
|
+
id: nextId++,
|
|
32
|
+
event,
|
|
33
|
+
};
|
|
34
|
+
if (count < maxSize) {
|
|
35
|
+
// Buffer not full yet, just append
|
|
36
|
+
events.push(storedEvent);
|
|
37
|
+
count++;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// Buffer is full, overwrite oldest
|
|
41
|
+
const insertIndex = (startIndex + count) % maxSize;
|
|
42
|
+
events[insertIndex] = storedEvent;
|
|
43
|
+
startIndex = (startIndex + 1) % maxSize;
|
|
44
|
+
}
|
|
45
|
+
// Notify subscribers
|
|
46
|
+
for (const callback of subscribers) {
|
|
47
|
+
try {
|
|
48
|
+
callback(storedEvent);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error('[DevTools] Error in event subscriber:', error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get all stored events in chronological order (oldest first).
|
|
57
|
+
*/
|
|
58
|
+
function getAll() {
|
|
59
|
+
if (count < maxSize) {
|
|
60
|
+
// Buffer not full yet, events are in order
|
|
61
|
+
return [...events];
|
|
62
|
+
}
|
|
63
|
+
// Buffer is full, need to reorder from ring buffer
|
|
64
|
+
const result = [];
|
|
65
|
+
for (let i = 0; i < count; i++) {
|
|
66
|
+
const index = (startIndex + i) % maxSize;
|
|
67
|
+
const event = events[index];
|
|
68
|
+
if (event) {
|
|
69
|
+
result.push(event);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the last N events (newest first).
|
|
76
|
+
*/
|
|
77
|
+
function getLast(n) {
|
|
78
|
+
const all = getAll();
|
|
79
|
+
return all.slice(-n).reverse();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Clear all events from the buffer.
|
|
83
|
+
*/
|
|
84
|
+
function clear() {
|
|
85
|
+
events.length = 0;
|
|
86
|
+
startIndex = 0;
|
|
87
|
+
count = 0;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get the current number of events in the buffer.
|
|
91
|
+
*/
|
|
92
|
+
function size() {
|
|
93
|
+
return count;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Subscribe to new events.
|
|
97
|
+
* Returns an unsubscribe function.
|
|
98
|
+
*/
|
|
99
|
+
function subscribe(callback) {
|
|
100
|
+
subscribers.add(callback);
|
|
101
|
+
return () => {
|
|
102
|
+
subscribers.delete(callback);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
push,
|
|
107
|
+
getAll,
|
|
108
|
+
getLast,
|
|
109
|
+
clear,
|
|
110
|
+
size,
|
|
111
|
+
subscribe,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buffer.js","sourceRoot":"","sources":["../src/buffer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,+EAA+E;AAC/E,iCAAiC;AACjC,+EAA+E;AAE/E;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,mBAAmB;IACnB,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,+DAA+D;IAC/D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAgC,CAAC;IAE5D;;;OAGG;IACH,SAAS,IAAI,CAAC,KAAiB;QAC7B,MAAM,WAAW,GAAgB;YAC/B,EAAE,EAAE,MAAM,EAAE;YACZ,KAAK;SACN,CAAC;QAEF,IAAI,KAAK,GAAG,OAAO,EAAE,CAAC;YACpB,mCAAmC;YACnC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACzB,KAAK,EAAE,CAAC;QACV,CAAC;aAAM,CAAC;YACN,mCAAmC;YACnC,MAAM,WAAW,GAAG,CAAC,UAAU,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC;YACnD,MAAM,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC;YAClC,UAAU,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC;QAC1C,CAAC;QAED,qBAAqB;QACrB,KAAK,MAAM,QAAQ,IAAI,WAAW,EAAE,CAAC;YACnC,IAAI,CAAC;gBACH,QAAQ,CAAC,WAAW,CAAC,CAAC;YACxB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,SAAS,MAAM;QACb,IAAI,KAAK,GAAG,OAAO,EAAE,CAAC;YACpB,2CAA2C;YAC3C,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;QACrB,CAAC;QAED,mDAAmD;QACnD,MAAM,MAAM,GAAkB,EAAE,CAAC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC;YACzC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,SAAS,OAAO,CAAC,CAAS;QACxB,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;QACrB,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,SAAS,KAAK;QACZ,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAClB,UAAU,GAAG,CAAC,CAAC;QACf,KAAK,GAAG,CAAC,CAAC;IACZ,CAAC;IAED;;OAEG;IACH,SAAS,IAAI;QACX,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;OAGG;IACH,SAAS,SAAS,CAAC,QAAsC;QACvD,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1B,OAAO,GAAG,EAAE;YACV,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI;QACJ,MAAM;QACN,OAAO;QACP,KAAK;QACL,IAAI;QACJ,SAAS;KACV,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @liteforge/devtools
|
|
3
|
+
*
|
|
4
|
+
* DevTools panel for debugging LiteForge applications.
|
|
5
|
+
*/
|
|
6
|
+
export { devtoolsPlugin, createDevTools } from './plugin.js';
|
|
7
|
+
export type { DevToolsStore, DevToolsStoreMap, StandaloneDevToolsConfig, } from './plugin.js';
|
|
8
|
+
export type { DevToolsConfig, ResolvedDevToolsConfig, DevToolsInstance, PanelPosition, TabId, PanelState, EventBuffer, StoredEvent, SignalInfo, StoreInfo, StoreHistoryEntry, NavigationInfo, ComponentInfo, PerformanceCounters, } from './types.js';
|
|
9
|
+
export { createEventBuffer } from './buffer.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7D,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,aAAa,EACb,KAAK,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @liteforge/devtools
|
|
3
|
+
*
|
|
4
|
+
* DevTools panel for debugging LiteForge applications.
|
|
5
|
+
*/
|
|
6
|
+
// Plugin
|
|
7
|
+
export { devtoolsPlugin, createDevTools } from './plugin.js';
|
|
8
|
+
// Buffer (for advanced usage)
|
|
9
|
+
export { createEventBuffer } from './buffer.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,SAAS;AACT,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA2B7D,8BAA8B;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal reactive primitives for DevTools.
|
|
3
|
+
*
|
|
4
|
+
* These wrappers automatically set __internal: true to prevent
|
|
5
|
+
* debug event emission, avoiding infinite loops where DevTools
|
|
6
|
+
* would receive events from its own signals/effects/computeds.
|
|
7
|
+
*/
|
|
8
|
+
import type { Signal, ReadonlySignal, SignalOptions, EffectFn, EffectOptions, DisposeFn, ComputeFn, ComputedOptions } from '@liteforge/core';
|
|
9
|
+
/**
|
|
10
|
+
* Create an internal signal that doesn't emit debug events.
|
|
11
|
+
*/
|
|
12
|
+
export declare function signal<T>(initialValue: T, options?: Omit<SignalOptions, '__internal'>): Signal<T>;
|
|
13
|
+
/**
|
|
14
|
+
* Create an internal effect that doesn't emit debug events.
|
|
15
|
+
*/
|
|
16
|
+
export declare function effect(fn: EffectFn, options?: Omit<EffectOptions, '__internal'>): DisposeFn;
|
|
17
|
+
/**
|
|
18
|
+
* Create an internal computed that doesn't emit debug events.
|
|
19
|
+
*/
|
|
20
|
+
export declare function computed<T>(fn: ComputeFn<T>, options?: Omit<ComputedOptions, '__internal'>): ReadonlySignal<T>;
|
|
21
|
+
//# sourceMappingURL=internals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internals.d.ts","sourceRoot":"","sources":["../src/internals.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EACV,MAAM,EACN,cAAc,EACd,aAAa,EACb,QAAQ,EACR,aAAa,EACb,SAAS,EACT,SAAS,EACT,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,EACtB,YAAY,EAAE,CAAC,EACf,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,GAC1C,MAAM,CAAC,CAAC,CAAC,CAEX;AAED;;GAEG;AACH,wBAAgB,MAAM,CACpB,EAAE,EAAE,QAAQ,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,GAC1C,SAAS,CAEX;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,EAChB,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,GAC5C,cAAc,CAAC,CAAC,CAAC,CAEnB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal reactive primitives for DevTools.
|
|
3
|
+
*
|
|
4
|
+
* These wrappers automatically set __internal: true to prevent
|
|
5
|
+
* debug event emission, avoiding infinite loops where DevTools
|
|
6
|
+
* would receive events from its own signals/effects/computeds.
|
|
7
|
+
*/
|
|
8
|
+
import { signal as coreSignal, effect as coreEffect, computed as coreComputed, } from '@liteforge/core';
|
|
9
|
+
/**
|
|
10
|
+
* Create an internal signal that doesn't emit debug events.
|
|
11
|
+
*/
|
|
12
|
+
export function signal(initialValue, options) {
|
|
13
|
+
return coreSignal(initialValue, { ...options, __internal: true });
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create an internal effect that doesn't emit debug events.
|
|
17
|
+
*/
|
|
18
|
+
export function effect(fn, options) {
|
|
19
|
+
return coreEffect(fn, { ...options, __internal: true });
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create an internal computed that doesn't emit debug events.
|
|
23
|
+
*/
|
|
24
|
+
export function computed(fn, options) {
|
|
25
|
+
return coreComputed(fn, { ...options, __internal: true });
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=internals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internals.js","sourceRoot":"","sources":["../src/internals.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,MAAM,IAAI,UAAU,EACpB,MAAM,IAAI,UAAU,EACpB,QAAQ,IAAI,YAAY,GACzB,MAAM,iBAAiB,CAAC;AAYzB;;GAEG;AACH,MAAM,UAAU,MAAM,CACpB,YAAe,EACf,OAA2C;IAE3C,OAAO,UAAU,CAAC,YAAY,EAAE,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;AACpE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,MAAM,CACpB,EAAY,EACZ,OAA2C;IAE3C,OAAO,UAAU,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CACtB,EAAgB,EAChB,OAA6C;IAE7C,OAAO,YAAY,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteForge DevTools Panel
|
|
3
|
+
*
|
|
4
|
+
* Main panel container with header, tabs, and content area.
|
|
5
|
+
* Built using LiteForge signals and direct DOM manipulation.
|
|
6
|
+
*/
|
|
7
|
+
import type { Signal } from '@liteforge/core';
|
|
8
|
+
import type { ResolvedDevToolsConfig, PanelState, EventBuffer } from '../types.js';
|
|
9
|
+
import type { DevToolsStoreMap } from '../plugin.js';
|
|
10
|
+
/**
|
|
11
|
+
* Create the main DevTools panel element.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createPanel(config: ResolvedDevToolsConfig, state: Signal<PanelState>, buffer: EventBuffer, stores: DevToolsStoreMap): HTMLElement;
|
|
14
|
+
//# sourceMappingURL=Panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Panel.d.ts","sourceRoot":"","sources":["../../src/panel/Panel.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAW9C,OAAO,KAAK,EACV,sBAAsB,EACtB,UAAU,EAEV,WAAW,EACZ,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AA6BrD;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,sBAAsB,EAC9B,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,EACzB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,gBAAgB,GACvB,WAAW,CA0Eb"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteForge DevTools Panel
|
|
3
|
+
*
|
|
4
|
+
* Main panel container with header, tabs, and content area.
|
|
5
|
+
* Built using LiteForge signals and direct DOM manipulation.
|
|
6
|
+
*/
|
|
7
|
+
import { effect } from '../internals.js';
|
|
8
|
+
import { getPanelStyles, headerStyles, tabBarStyles, getTabStyles, controlsStyles, buttonStyles, contentStyles, } from '../styles.js';
|
|
9
|
+
import { createSignalInspector } from '../tabs/SignalInspector.js';
|
|
10
|
+
import { createStoreExplorer } from '../tabs/StoreExplorer.js';
|
|
11
|
+
import { createRouterInspector } from '../tabs/RouterInspector.js';
|
|
12
|
+
import { createComponentTree } from '../tabs/ComponentTree.js';
|
|
13
|
+
import { createPerformanceTab } from '../tabs/Performance.js';
|
|
14
|
+
const TABS = [
|
|
15
|
+
{ id: 'signals', label: 'Signals', icon: 'S' },
|
|
16
|
+
{ id: 'stores', label: 'Stores', icon: 'St' },
|
|
17
|
+
{ id: 'router', label: 'Router', icon: 'R' },
|
|
18
|
+
{ id: 'components', label: 'Comps', icon: 'C' },
|
|
19
|
+
{ id: 'performance', label: 'Perf', icon: 'P' },
|
|
20
|
+
];
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Panel Creation
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Create the main DevTools panel element.
|
|
26
|
+
*/
|
|
27
|
+
export function createPanel(config, state, buffer, stores) {
|
|
28
|
+
// Create panel container
|
|
29
|
+
const panel = document.createElement('div');
|
|
30
|
+
panel.id = 'liteforge-devtools';
|
|
31
|
+
panel.setAttribute('style', getPanelStyles(config.position, config.width, config.height, false));
|
|
32
|
+
// Create header
|
|
33
|
+
const header = createHeader(state, config);
|
|
34
|
+
panel.appendChild(header);
|
|
35
|
+
// Create content area
|
|
36
|
+
const content = document.createElement('div');
|
|
37
|
+
content.setAttribute('style', contentStyles);
|
|
38
|
+
panel.appendChild(content);
|
|
39
|
+
// Track tab content containers and their dispose functions
|
|
40
|
+
const tabContents = new Map();
|
|
41
|
+
const tabDisposers = new Map();
|
|
42
|
+
// Track last active tab for cleanup
|
|
43
|
+
let lastActiveTab = null;
|
|
44
|
+
// Create tab content lazily (only when first viewed)
|
|
45
|
+
function ensureTabContent(tabId) {
|
|
46
|
+
if (tabContents.has(tabId))
|
|
47
|
+
return;
|
|
48
|
+
const tabResult = createTabContent(tabId, buffer, state, stores);
|
|
49
|
+
tabResult.element.style.display = 'none';
|
|
50
|
+
content.appendChild(tabResult.element);
|
|
51
|
+
tabContents.set(tabId, tabResult.element);
|
|
52
|
+
tabDisposers.set(tabId, tabResult.dispose);
|
|
53
|
+
}
|
|
54
|
+
// Effect to switch tabs and update styles
|
|
55
|
+
effect(() => {
|
|
56
|
+
const currentState = state();
|
|
57
|
+
// Update panel styles
|
|
58
|
+
panel.setAttribute('style', getPanelStyles(config.position, currentState.width, currentState.height, currentState.isOpen));
|
|
59
|
+
const activeTab = currentState.activeTab;
|
|
60
|
+
// Dispose previous tab's effects when switching away
|
|
61
|
+
if (lastActiveTab !== null && lastActiveTab !== activeTab) {
|
|
62
|
+
const disposer = tabDisposers.get(lastActiveTab);
|
|
63
|
+
if (disposer) {
|
|
64
|
+
disposer();
|
|
65
|
+
// Remove the disposed tab so it will be recreated fresh next time
|
|
66
|
+
const oldContent = tabContents.get(lastActiveTab);
|
|
67
|
+
if (oldContent && oldContent.parentNode) {
|
|
68
|
+
oldContent.parentNode.removeChild(oldContent);
|
|
69
|
+
}
|
|
70
|
+
tabContents.delete(lastActiveTab);
|
|
71
|
+
tabDisposers.delete(lastActiveTab);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Ensure active tab content exists
|
|
75
|
+
ensureTabContent(activeTab);
|
|
76
|
+
// Show/hide tab contents
|
|
77
|
+
for (const [tabId, tabContent] of tabContents) {
|
|
78
|
+
tabContent.style.display = tabId === activeTab ? 'flex' : 'none';
|
|
79
|
+
}
|
|
80
|
+
lastActiveTab = activeTab;
|
|
81
|
+
});
|
|
82
|
+
return panel;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create the panel header with tabs and controls.
|
|
86
|
+
*/
|
|
87
|
+
function createHeader(state, config) {
|
|
88
|
+
const header = document.createElement('div');
|
|
89
|
+
header.setAttribute('style', headerStyles);
|
|
90
|
+
// Tab bar
|
|
91
|
+
const tabBar = document.createElement('div');
|
|
92
|
+
tabBar.setAttribute('style', tabBarStyles);
|
|
93
|
+
// Create tab buttons
|
|
94
|
+
const tabButtons = [];
|
|
95
|
+
for (const tab of TABS) {
|
|
96
|
+
const button = document.createElement('button');
|
|
97
|
+
button.textContent = tab.label;
|
|
98
|
+
button.title = tab.label;
|
|
99
|
+
button.onclick = () => {
|
|
100
|
+
state.update(s => ({ ...s, activeTab: tab.id }));
|
|
101
|
+
};
|
|
102
|
+
tabBar.appendChild(button);
|
|
103
|
+
tabButtons.push(button);
|
|
104
|
+
}
|
|
105
|
+
// Effect to update tab button styles
|
|
106
|
+
effect(() => {
|
|
107
|
+
const currentState = state();
|
|
108
|
+
for (let i = 0; i < TABS.length; i++) {
|
|
109
|
+
const tab = TABS[i];
|
|
110
|
+
const button = tabButtons[i];
|
|
111
|
+
if (tab && button) {
|
|
112
|
+
button.setAttribute('style', getTabStyles(currentState.activeTab === tab.id));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
header.appendChild(tabBar);
|
|
117
|
+
// Controls
|
|
118
|
+
const controls = document.createElement('div');
|
|
119
|
+
controls.setAttribute('style', controlsStyles);
|
|
120
|
+
// Pause/Resume button
|
|
121
|
+
const pauseButton = document.createElement('button');
|
|
122
|
+
pauseButton.setAttribute('style', buttonStyles);
|
|
123
|
+
pauseButton.onclick = () => {
|
|
124
|
+
state.update(s => ({ ...s, isPaused: !s.isPaused }));
|
|
125
|
+
};
|
|
126
|
+
effect(() => {
|
|
127
|
+
pauseButton.textContent = state().isPaused ? 'Resume' : 'Pause';
|
|
128
|
+
});
|
|
129
|
+
controls.appendChild(pauseButton);
|
|
130
|
+
// Clear button
|
|
131
|
+
const clearButton = document.createElement('button');
|
|
132
|
+
clearButton.setAttribute('style', buttonStyles);
|
|
133
|
+
clearButton.textContent = 'Clear';
|
|
134
|
+
clearButton.onclick = () => {
|
|
135
|
+
// Dispatch custom event for tabs to handle
|
|
136
|
+
const event = new CustomEvent('devtools:clear');
|
|
137
|
+
header.dispatchEvent(event);
|
|
138
|
+
};
|
|
139
|
+
controls.appendChild(clearButton);
|
|
140
|
+
// Close button
|
|
141
|
+
const closeButton = document.createElement('button');
|
|
142
|
+
closeButton.setAttribute('style', buttonStyles);
|
|
143
|
+
closeButton.textContent = 'X';
|
|
144
|
+
closeButton.title = `Close (${config.shortcut})`;
|
|
145
|
+
closeButton.onclick = () => {
|
|
146
|
+
state.update(s => ({ ...s, isOpen: false }));
|
|
147
|
+
};
|
|
148
|
+
closeButton.style.padding = '4px 6px';
|
|
149
|
+
closeButton.style.fontWeight = 'bold';
|
|
150
|
+
controls.appendChild(closeButton);
|
|
151
|
+
header.appendChild(controls);
|
|
152
|
+
return header;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Create content for a specific tab.
|
|
156
|
+
* Returns the container element and a dispose function.
|
|
157
|
+
*/
|
|
158
|
+
function createTabContent(tabId, buffer, state, stores) {
|
|
159
|
+
const container = document.createElement('div');
|
|
160
|
+
container.setAttribute('style', `${contentStyles} flex-direction: column;`);
|
|
161
|
+
let tabResult;
|
|
162
|
+
switch (tabId) {
|
|
163
|
+
case 'signals':
|
|
164
|
+
tabResult = createSignalInspector(buffer, state);
|
|
165
|
+
container.appendChild(tabResult.element);
|
|
166
|
+
break;
|
|
167
|
+
case 'stores':
|
|
168
|
+
tabResult = createStoreExplorer(buffer, state, stores);
|
|
169
|
+
container.appendChild(tabResult.element);
|
|
170
|
+
break;
|
|
171
|
+
case 'router':
|
|
172
|
+
tabResult = createRouterInspector(buffer, state);
|
|
173
|
+
container.appendChild(tabResult.element);
|
|
174
|
+
break;
|
|
175
|
+
case 'components':
|
|
176
|
+
tabResult = createComponentTree(buffer, state);
|
|
177
|
+
container.appendChild(tabResult.element);
|
|
178
|
+
break;
|
|
179
|
+
case 'performance':
|
|
180
|
+
tabResult = createPerformanceTab(buffer, state);
|
|
181
|
+
container.appendChild(tabResult.element);
|
|
182
|
+
break;
|
|
183
|
+
default:
|
|
184
|
+
tabResult = { element: document.createElement('div'), dispose: () => { } };
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
element: container,
|
|
188
|
+
dispose: tabResult.dispose,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=Panel.js.map
|