@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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/buffer.d.ts +16 -0
  4. package/dist/buffer.d.ts.map +1 -0
  5. package/dist/buffer.js +114 -0
  6. package/dist/buffer.js.map +1 -0
  7. package/dist/index.d.ts +10 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +10 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/internals.d.ts +21 -0
  12. package/dist/internals.d.ts.map +1 -0
  13. package/dist/internals.js +27 -0
  14. package/dist/internals.js.map +1 -0
  15. package/dist/panel/Panel.d.ts +14 -0
  16. package/dist/panel/Panel.d.ts.map +1 -0
  17. package/dist/panel/Panel.js +191 -0
  18. package/dist/panel/Panel.js.map +1 -0
  19. package/dist/plugin.d.ts +59 -0
  20. package/dist/plugin.d.ts.map +1 -0
  21. package/dist/plugin.js +274 -0
  22. package/dist/plugin.js.map +1 -0
  23. package/dist/styles.d.ts +74 -0
  24. package/dist/styles.d.ts.map +1 -0
  25. package/dist/styles.js +351 -0
  26. package/dist/styles.js.map +1 -0
  27. package/dist/tabs/ComponentTree.d.ts +19 -0
  28. package/dist/tabs/ComponentTree.d.ts.map +1 -0
  29. package/dist/tabs/ComponentTree.js +189 -0
  30. package/dist/tabs/ComponentTree.js.map +1 -0
  31. package/dist/tabs/Performance.d.ts +19 -0
  32. package/dist/tabs/Performance.d.ts.map +1 -0
  33. package/dist/tabs/Performance.js +169 -0
  34. package/dist/tabs/Performance.js.map +1 -0
  35. package/dist/tabs/RouterInspector.d.ts +19 -0
  36. package/dist/tabs/RouterInspector.d.ts.map +1 -0
  37. package/dist/tabs/RouterInspector.js +169 -0
  38. package/dist/tabs/RouterInspector.js.map +1 -0
  39. package/dist/tabs/SignalInspector.d.ts +19 -0
  40. package/dist/tabs/SignalInspector.d.ts.map +1 -0
  41. package/dist/tabs/SignalInspector.js +202 -0
  42. package/dist/tabs/SignalInspector.js.map +1 -0
  43. package/dist/tabs/StoreExplorer.d.ts +21 -0
  44. package/dist/tabs/StoreExplorer.d.ts.map +1 -0
  45. package/dist/tabs/StoreExplorer.js +440 -0
  46. package/dist/tabs/StoreExplorer.js.map +1 -0
  47. package/dist/types.d.ts +169 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +5 -0
  50. package/dist/types.js.map +1 -0
  51. 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
@@ -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"}
@@ -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