@shane_il/pulse 0.1.2 → 0.3.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/README.md +20 -167
- package/dist/connect.d.ts +1 -0
- package/dist/devtools/core.d.ts +47 -0
- package/dist/devtools/index.d.ts +9 -0
- package/dist/devtools/panel/actions-tab.d.ts +10 -0
- package/dist/devtools/panel/components-tab.d.ts +5 -0
- package/dist/devtools/panel/panel.d.ts +24 -0
- package/dist/devtools/panel/stores-tab.d.ts +7 -0
- package/dist/devtools/panel/styles.d.ts +41 -0
- package/dist/devtools/time-travel.d.ts +11 -0
- package/dist/devtools.cjs +2 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.js +1218 -0
- package/dist/devtools.js.map +1 -0
- package/dist/diff.d.ts +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/middleware.d.ts +36 -0
- package/dist/patch.d.ts +2 -2
- package/dist/pulse.cjs +1 -1
- package/dist/pulse.cjs.map +1 -1
- package/dist/pulse.js +472 -366
- package/dist/pulse.js.map +1 -1
- package/dist/store.d.ts +4 -0
- package/package.json +14 -2
package/README.md
CHANGED
|
@@ -10,7 +10,9 @@ A render-driven UI framework with virtual DOM and immutable stores. Like React,
|
|
|
10
10
|
- **Stores are first-class.** Create, import, and share stores anywhere. They're framework-agnostic.
|
|
11
11
|
- **Render-driven.** Describe what the UI looks like for a given state. Pulse handles the rest.
|
|
12
12
|
- **Built-in routing.** Store-based client-side router — routes are just state.
|
|
13
|
-
- **
|
|
13
|
+
- **Middleware.** Pluggable middleware for logging, action history, and custom logic.
|
|
14
|
+
- **Devtools.** Built-in browser devtools panel — store inspector, action replay, time-travel.
|
|
15
|
+
- **Tiny.** ~6 KB gzipped core, ~9 KB devtools. Zero runtime dependencies.
|
|
14
16
|
|
|
15
17
|
## Quick Start
|
|
16
18
|
|
|
@@ -41,7 +43,6 @@ const counterStore = createStore({
|
|
|
41
43
|
actions: {
|
|
42
44
|
increment: (state) => ({ ...state, count: state.count + 1 }),
|
|
43
45
|
decrement: (state) => ({ ...state, count: state.count - 1 }),
|
|
44
|
-
set: (state, value) => ({ ...state, count: value }),
|
|
45
46
|
},
|
|
46
47
|
});
|
|
47
48
|
|
|
@@ -65,186 +66,38 @@ const ConnectedCounter = connect({
|
|
|
65
66
|
render(<ConnectedCounter />, document.getElementById('app'));
|
|
66
67
|
```
|
|
67
68
|
|
|
68
|
-
##
|
|
69
|
-
|
|
70
|
-
### `createStore({ state, actions })`
|
|
71
|
-
|
|
72
|
-
Creates an immutable state store.
|
|
73
|
-
|
|
74
|
-
```js
|
|
75
|
-
const store = createStore({
|
|
76
|
-
state: { count: 0 },
|
|
77
|
-
actions: {
|
|
78
|
-
increment: (state) => ({ ...state, count: state.count + 1 }),
|
|
79
|
-
set: (state, value) => ({ ...state, count: value }),
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
store.getState(); // { count: 0 }
|
|
84
|
-
store.dispatch('increment'); // state is now { count: 1 }
|
|
85
|
-
store.dispatch('set', 42); // state is now { count: 42 }
|
|
86
|
-
|
|
87
|
-
const unsub = store.subscribe((newState) => {
|
|
88
|
-
console.log('State changed:', newState);
|
|
89
|
-
});
|
|
90
|
-
unsub(); // unsubscribe
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Actions are pure functions: `(state, payload) => newState`. If an action returns the same reference (`===`), subscribers are not notified.
|
|
94
|
-
|
|
95
|
-
Stores are standalone — they work outside of Pulse and can be shared across your app.
|
|
96
|
-
|
|
97
|
-
### `connect(bindings, lifecycle?)(Component)`
|
|
98
|
-
|
|
99
|
-
Connects a store's state to a component via selectors, with optional lifecycle callbacks.
|
|
100
|
-
|
|
101
|
-
```js
|
|
102
|
-
const Connected = connect({
|
|
103
|
-
count: counterStore.select((state) => state.count),
|
|
104
|
-
name: userStore.select((state) => state.name),
|
|
105
|
-
})(MyComponent);
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
- Selected values are merged into the component's props.
|
|
109
|
-
- Re-renders only when selected values change (shallow equality).
|
|
110
|
-
- Subscriptions are automatically managed (mount/unmount).
|
|
111
|
-
- Multiple stores can be bound to a single component.
|
|
112
|
-
|
|
113
|
-
#### Lifecycle Callbacks
|
|
114
|
-
|
|
115
|
-
The optional second argument adds lifecycle hooks:
|
|
116
|
-
|
|
117
|
-
```jsx
|
|
118
|
-
const Timer = connect(
|
|
119
|
-
{ elapsed: timerStore.select((s) => s.elapsed) },
|
|
120
|
-
{
|
|
121
|
-
onMount: ({ dom, props }) => {
|
|
122
|
-
// Called once after first render. DOM element is available.
|
|
123
|
-
const id = setInterval(() => timerStore.dispatch('tick'), 1000);
|
|
124
|
-
return () => clearInterval(id); // cleanup — called on destroy
|
|
125
|
-
},
|
|
126
|
-
onUpdate: ({ dom, props }) => {
|
|
127
|
-
// Called after every store-driven re-render (not on initial mount).
|
|
128
|
-
console.log('Timer updated:', dom.textContent);
|
|
129
|
-
},
|
|
130
|
-
onDestroy: ({ props }) => {
|
|
131
|
-
// Called when component is removed from the DOM.
|
|
132
|
-
console.log('Timer removed');
|
|
133
|
-
},
|
|
134
|
-
onError: ({ error, props }) => {
|
|
135
|
-
// Called when the component throws during render. Return fallback VNode.
|
|
136
|
-
return h('div', { className: 'error' }, `Error: ${error.message}`);
|
|
137
|
-
},
|
|
138
|
-
}
|
|
139
|
-
)(TimerView);
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
- **`onMount({ dom, props })`** — fires once after first render. `dom` is the rendered DOM element. Can return a cleanup function that runs on destroy.
|
|
143
|
-
- **`onUpdate({ dom, props })`** — fires after every store-driven re-render (not on initial mount). Useful for DOM measurement, animations, or logging.
|
|
144
|
-
- **`onDestroy({ props })`** — fires when the component is removed, after cleanup.
|
|
145
|
-
- **`onError({ error, props })`** — catches errors thrown during render. Return a fallback VNode (or `null`). The component stays subscribed and recovers on the next successful re-render.
|
|
146
|
-
- Re-renders do **not** re-trigger `onMount`.
|
|
147
|
-
- For components that only need lifecycle (no store bindings), pass empty bindings: `connect({}, { onMount })(Component)`.
|
|
148
|
-
|
|
149
|
-
### `render(vnode, container)`
|
|
150
|
-
|
|
151
|
-
Mounts a virtual DOM tree into a real DOM container. Subsequent calls to `render` with the same container diff and patch.
|
|
152
|
-
|
|
153
|
-
```js
|
|
154
|
-
render(<App />, document.getElementById('app'));
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### `h(type, props, ...children)` / `createElement`
|
|
158
|
-
|
|
159
|
-
JSX pragma. You don't call this directly — your JSX compiler transforms `<div>` into `h('div', null)`.
|
|
160
|
-
|
|
161
|
-
### `Fragment`
|
|
162
|
-
|
|
163
|
-
Groups children without adding a wrapper DOM node.
|
|
164
|
-
|
|
165
|
-
```jsx
|
|
166
|
-
<>
|
|
167
|
-
<span>a</span>
|
|
168
|
-
<span>b</span>
|
|
169
|
-
</>
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### `createRouter({ routes, initialPath? })`
|
|
173
|
-
|
|
174
|
-
Creates a store-based client-side router.
|
|
175
|
-
|
|
176
|
-
```jsx
|
|
177
|
-
import { h, createRouter, render } from '@shane_il/pulse';
|
|
178
|
-
|
|
179
|
-
const router = createRouter({
|
|
180
|
-
routes: [
|
|
181
|
-
{ path: '/' },
|
|
182
|
-
{ path: '/users/:id' },
|
|
183
|
-
{ path: '*' },
|
|
184
|
-
],
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
const { Route, Link } = router;
|
|
188
|
-
|
|
189
|
-
function App() {
|
|
190
|
-
return (
|
|
191
|
-
<div>
|
|
192
|
-
<nav>
|
|
193
|
-
<Link to="/">Home</Link>
|
|
194
|
-
<Link to="/users/1">User 1</Link>
|
|
195
|
-
</nav>
|
|
196
|
-
<Route path="/" component={Home} />
|
|
197
|
-
<Route path="/users/:id" component={UserProfile} />
|
|
198
|
-
<Route path="*" component={NotFound} />
|
|
199
|
-
</div>
|
|
200
|
-
);
|
|
201
|
-
}
|
|
69
|
+
## How It Works
|
|
202
70
|
|
|
203
|
-
render(<App />, document.getElementById('app'));
|
|
204
71
|
```
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
- **`store`** — a Pulse store holding `{ path, params, query, matched }`. Connect any component to route state.
|
|
209
|
-
- **`navigate(path)`** — push a new history entry and update the store.
|
|
210
|
-
- **`redirect(path)`** — replace the current history entry (back button skips it).
|
|
211
|
-
- **`back()` / `forward()`** — browser history navigation.
|
|
212
|
-
- **`Route`** — connected component that renders its `component` prop when the path matches. Passes `params` to the component.
|
|
213
|
-
- **`Link`** — renders an `<a>` with SPA navigation on click. Modifier clicks (Ctrl, Cmd) open in a new tab.
|
|
214
|
-
- **`Redirect`** — performs a redirect when rendered.
|
|
215
|
-
- **`destroy()`** — removes the `popstate` listener (for cleanup in tests).
|
|
216
|
-
|
|
217
|
-
Path patterns support static paths (`/about`), dynamic params (`/users/:id`), wildcard suffixes (`/dashboard/*`), and catch-all (`*`).
|
|
218
|
-
|
|
219
|
-
### `flushSync()`
|
|
220
|
-
|
|
221
|
-
Synchronously flushes all pending store-triggered re-renders. Primarily useful for testing.
|
|
222
|
-
|
|
223
|
-
```js
|
|
224
|
-
store.dispatch('increment');
|
|
225
|
-
flushSync();
|
|
226
|
-
// DOM is now updated
|
|
72
|
+
Store dispatch → Notify subscribers → Schedule re-render → Expand components
|
|
73
|
+
→ Diff VDOM → Patch DOM (single paint)
|
|
227
74
|
```
|
|
228
75
|
|
|
229
|
-
## How It Works
|
|
230
|
-
|
|
231
76
|
1. **Stores** hold immutable state. Actions produce new state via pure functions.
|
|
232
77
|
2. **`connect()`** subscribes components to store slices via selectors.
|
|
233
78
|
3. When a store changes, connected components whose selected values differ are scheduled for re-render.
|
|
234
79
|
4. The **scheduler** batches multiple store updates in the same tick into a single render pass.
|
|
235
80
|
5. The **VDOM engine** diffs the old and new virtual trees and patches only the changed DOM nodes.
|
|
236
81
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
82
|
+
## Documentation
|
|
83
|
+
|
|
84
|
+
- **[Getting Started](docs/getting-started.md)** — installation, JSX setup, first app, project structure
|
|
85
|
+
- **[Stores](docs/stores.md)** — `createStore`, actions, selectors, subscriptions, derived state
|
|
86
|
+
- **[Components](docs/components.md)** — pure components, `connect()`, keyed lists, error boundaries
|
|
87
|
+
- **[Lifecycle](docs/lifecycle.md)** — `onMount`, `onUpdate`, `onDestroy`, `onError`, cleanup functions
|
|
88
|
+
- **[Routing](docs/routing.md)** — `createRouter`, Route/Link/Redirect, path matching, nested routes
|
|
89
|
+
- **[Middleware](docs/middleware.md)** — `logger`, `actionHistory`, `createAsyncAction`, custom middleware
|
|
90
|
+
- **[Devtools](docs/devtools.md)** — browser panel, store inspector, time-travel, component tracking
|
|
91
|
+
- **[Architecture](docs/architecture.md)** — how the VDOM engine works under the hood
|
|
241
92
|
|
|
242
93
|
## Development
|
|
243
94
|
|
|
244
95
|
```bash
|
|
245
96
|
npm install
|
|
246
|
-
npm test #
|
|
247
|
-
npm run
|
|
97
|
+
npm test # 231 tests (vitest)
|
|
98
|
+
npm run typecheck # tsc --noEmit
|
|
99
|
+
npm run lint # eslint
|
|
100
|
+
npm run build # vite lib mode → dist/
|
|
248
101
|
```
|
|
249
102
|
|
|
250
103
|
## License
|
package/dist/connect.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { VNode, Bindings, Lifecycle, ComponentFunction } from './vnode';
|
|
2
2
|
export declare const CONNECTED: unique symbol;
|
|
3
|
+
export declare function __setComponentHooks(onMount: ((instance: ComponentInstance) => void) | null, onUnmount: ((instance: ComponentInstance) => void) | null): void;
|
|
3
4
|
export declare function connect(bindings: Bindings | null | undefined, lifecycle?: Lifecycle): (Component: ComponentFunction) => {
|
|
4
5
|
(props: Record<string, any>): VNode | null;
|
|
5
6
|
displayName: string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Store } from '../store';
|
|
2
|
+
import type { ActionEntry, Middleware } from '../middleware';
|
|
3
|
+
export type DevtoolsEventType = 'store-registered' | 'action-dispatched' | 'state-replaced' | 'component-mounted' | 'component-unmounted' | 'time-travel';
|
|
4
|
+
export interface DevtoolsEvent {
|
|
5
|
+
type: DevtoolsEventType;
|
|
6
|
+
storeName?: string;
|
|
7
|
+
data?: any;
|
|
8
|
+
}
|
|
9
|
+
export interface TrackedStore {
|
|
10
|
+
store: Store<any>;
|
|
11
|
+
history: ActionEntry[];
|
|
12
|
+
name: string;
|
|
13
|
+
}
|
|
14
|
+
export interface TrackedComponent {
|
|
15
|
+
id: number;
|
|
16
|
+
displayName: string;
|
|
17
|
+
storeNames: string[];
|
|
18
|
+
}
|
|
19
|
+
type DevtoolsListener = (event: DevtoolsEvent) => void;
|
|
20
|
+
export declare class PulseDevtools {
|
|
21
|
+
private stores;
|
|
22
|
+
private components;
|
|
23
|
+
private listeners;
|
|
24
|
+
registerStore(store: Store<any>, history: ActionEntry[], name?: string): void;
|
|
25
|
+
getStoreNames(): string[];
|
|
26
|
+
getStoreState(name: string): any;
|
|
27
|
+
getTrackedStore(name: string): TrackedStore | undefined;
|
|
28
|
+
getHistory(name?: string): ActionEntry[];
|
|
29
|
+
trackComponent(displayName: string, storeNames: string[]): number;
|
|
30
|
+
untrackComponent(id: number): void;
|
|
31
|
+
getComponents(): TrackedComponent[];
|
|
32
|
+
on(listener: DevtoolsListener): () => void;
|
|
33
|
+
emit(event: DevtoolsEvent): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Convenience: wraps createStore with actionHistory middleware and registers with devtools.
|
|
37
|
+
*/
|
|
38
|
+
export declare function instrumentStore<S>(devtools: PulseDevtools, config: {
|
|
39
|
+
state: S;
|
|
40
|
+
actions: Record<string, (state: S, payload?: any) => S>;
|
|
41
|
+
name?: string;
|
|
42
|
+
middleware?: Middleware<S>[];
|
|
43
|
+
}): {
|
|
44
|
+
store: Store<S>;
|
|
45
|
+
history: ActionEntry[];
|
|
46
|
+
};
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PulseDevtools } from './core';
|
|
2
|
+
declare const devtools: PulseDevtools;
|
|
3
|
+
export declare function _markInternalStore(store: any): void;
|
|
4
|
+
export declare function openPanel(): void;
|
|
5
|
+
export declare function closePanel(): void;
|
|
6
|
+
export declare function togglePanel(): void;
|
|
7
|
+
export { PulseDevtools, instrumentStore } from './core';
|
|
8
|
+
export { travelTo, replayFrom } from './time-travel';
|
|
9
|
+
export { devtools };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { VNode } from '../../vnode';
|
|
2
|
+
import type { ActionEntry } from '../../middleware';
|
|
3
|
+
export declare function ActionsTab({ actionLog, timeTravelIndex, filter, onFilterChange, onTravelTo, onSliderChange, }: {
|
|
4
|
+
actionLog: ActionEntry[];
|
|
5
|
+
timeTravelIndex: number;
|
|
6
|
+
filter: string;
|
|
7
|
+
onFilterChange: (value: string) => void;
|
|
8
|
+
onTravelTo: (index: number) => void;
|
|
9
|
+
onSliderChange: (index: number) => void;
|
|
10
|
+
}): VNode;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { PulseDevtools } from '../core';
|
|
2
|
+
import type { ActionEntry } from '../../middleware';
|
|
3
|
+
interface PanelState {
|
|
4
|
+
open: boolean;
|
|
5
|
+
activeTab: 'stores' | 'actions' | 'components';
|
|
6
|
+
selectedStore: string | null;
|
|
7
|
+
timeTravelIndex: number;
|
|
8
|
+
storeNames: string[];
|
|
9
|
+
storeStates: Record<string, any>;
|
|
10
|
+
actionLog: ActionEntry[];
|
|
11
|
+
filter: string;
|
|
12
|
+
components: {
|
|
13
|
+
id: number;
|
|
14
|
+
displayName: string;
|
|
15
|
+
storeNames: string[];
|
|
16
|
+
}[];
|
|
17
|
+
}
|
|
18
|
+
export declare function createPanel(devtools: PulseDevtools, markInternal?: (store: any) => void): {
|
|
19
|
+
openPanel: () => void;
|
|
20
|
+
closePanel: () => void;
|
|
21
|
+
togglePanel: () => void;
|
|
22
|
+
panelStore: import("../..").Store<PanelState>;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { VNode } from '../../vnode';
|
|
2
|
+
export declare function StoresTab({ storeNames, selectedStore, storeStates, onSelectStore, }: {
|
|
3
|
+
storeNames: string[];
|
|
4
|
+
selectedStore: string | null;
|
|
5
|
+
storeStates: Record<string, any>;
|
|
6
|
+
onSelectStore: (name: string) => void;
|
|
7
|
+
}): VNode;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare const colors: {
|
|
2
|
+
base: string;
|
|
3
|
+
surface0: string;
|
|
4
|
+
surface1: string;
|
|
5
|
+
surface2: string;
|
|
6
|
+
overlay0: string;
|
|
7
|
+
text: string;
|
|
8
|
+
subtext0: string;
|
|
9
|
+
subtext1: string;
|
|
10
|
+
blue: string;
|
|
11
|
+
green: string;
|
|
12
|
+
red: string;
|
|
13
|
+
yellow: string;
|
|
14
|
+
mauve: string;
|
|
15
|
+
teal: string;
|
|
16
|
+
peach: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const panelRoot: Record<string, string>;
|
|
19
|
+
export declare const tabBar: Record<string, string>;
|
|
20
|
+
export declare const tabButton: (active: boolean) => Record<string, string>;
|
|
21
|
+
export declare const closeButton: Record<string, string>;
|
|
22
|
+
export declare const tabContent: Record<string, string>;
|
|
23
|
+
export declare const splitPane: Record<string, string>;
|
|
24
|
+
export declare const paneLeft: Record<string, string>;
|
|
25
|
+
export declare const paneRight: Record<string, string>;
|
|
26
|
+
export declare const storeItem: (selected: boolean) => Record<string, string>;
|
|
27
|
+
export declare const treeKey: Record<string, string>;
|
|
28
|
+
export declare const treeValue: Record<string, string>;
|
|
29
|
+
export declare const treeString: Record<string, string>;
|
|
30
|
+
export declare const treeBool: Record<string, string>;
|
|
31
|
+
export declare const treeNull: Record<string, string>;
|
|
32
|
+
export declare const actionEntry: (active: boolean) => Record<string, string>;
|
|
33
|
+
export declare const actionName: Record<string, string>;
|
|
34
|
+
export declare const actionTime: Record<string, string>;
|
|
35
|
+
export declare const slider: Record<string, string>;
|
|
36
|
+
export declare const filterInput: Record<string, string>;
|
|
37
|
+
export declare const componentItem: Record<string, string>;
|
|
38
|
+
export declare const componentName: Record<string, string>;
|
|
39
|
+
export declare const componentStores: Record<string, string>;
|
|
40
|
+
export declare const badge: Record<string, string>;
|
|
41
|
+
export declare const title: Record<string, string>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PulseDevtools } from './core';
|
|
2
|
+
/**
|
|
3
|
+
* Jump to a specific point in a store's action history.
|
|
4
|
+
* Uses __devtools_replace__ to force-set the store's state.
|
|
5
|
+
*/
|
|
6
|
+
export declare function travelTo(devtools: PulseDevtools, storeName: string, entryIndex: number): void;
|
|
7
|
+
/**
|
|
8
|
+
* Replay actions from a given index forward, starting from that entry's prevState.
|
|
9
|
+
* Useful for re-executing the action chain after modifying an earlier action.
|
|
10
|
+
*/
|
|
11
|
+
export declare function replayFrom(devtools: PulseDevtools, storeName: string, fromIndex: number): void;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var lt=Object.defineProperty;var at=(t,e,n)=>e in t?lt(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var S=(t,e,n)=>at(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let A=!1;const R=new Set;function pt(t){R.add(t),A||(A=!0,queueMicrotask(ut))}function ut(){const t=[...R];R.clear(),A=!1;for(const e of t)e()}const q=Symbol("PULSE_CONNECTED"),F=globalThis;F.__PULSE_HOOKS__||(F.__PULSE_HOOKS__={onMount:null,onUnmount:null});const T=F.__PULSE_HOOKS__;function ft(t,e){T.onMount=t,T.onUnmount=e}function dt(t,e){return function(o){const s=t||{};function r(i){const a={};for(const p in s){const{store:l,selector:h}=s[p];a[p]=h(l.getState())}return o({...a,...i})}return r[q]=!0,r._bindings=s,r._innerComponent=o,r.displayName=`Connected(${o.displayName||o.name||"Anonymous"})`,r}}class ht{constructor(e,n){S(this,"connectedFn");S(this,"props");S(this,"prevSelected");S(this,"unsubscribers");S(this,"lastVTree");S(this,"parentDom");S(this,"_renderCallback");S(this,"_mountCleanup");this.connectedFn=e,this.props=n,this.prevSelected={},this.unsubscribers=[],this.lastVTree=null,this.parentDom=null,this._renderCallback=null,this._mountCleanup=null}mount(e,n){var r;this.parentDom=e,this._renderCallback=n;const o=this.connectedFn._bindings;for(const i in o){const{store:a,selector:p}=o[i];this.prevSelected[i]=p(a.getState())}for(const i in o){const{store:a}=o[i],p=a.subscribe(()=>{this._onStoreChange()});this.unsubscribers.push(p)}const s=this.connectedFn._lifecycle;if(s!=null&&s.onMount){const i=s.onMount({dom:(r=this.lastVTree)==null?void 0:r._dom,props:this.props});typeof i=="function"&&(this._mountCleanup=i)}T.onMount&&T.onMount(this)}_onStoreChange(){const e=this.connectedFn._bindings;let n=!1;for(const o in e){const{store:s,selector:r}=e[o],i=r(s.getState());if(!mt(i,this.prevSelected[o])){n=!0;break}}n&&pt(this._renderCallback)}updateSelected(){const e=this.connectedFn._bindings;for(const n in e){const{store:o,selector:s}=e[n];this.prevSelected[n]=s(o.getState())}}unmount(){T.onUnmount&&T.onUnmount(this),this._mountCleanup&&(this._mountCleanup(),this._mountCleanup=null);const e=this.connectedFn._lifecycle;e!=null&&e.onDestroy&&e.onDestroy({props:this.props});for(const n of this.unsubscribers)n();this.unsubscribers=[],this._renderCallback=null}}function mt(t,e){if(Object.is(t,e))return!0;if(typeof t!="object"||typeof e!="object"||t===null||e===null)return!1;const n=Object.keys(t),o=Object.keys(e);if(n.length!==o.length)return!1;for(const s of n)if(!Object.prototype.hasOwnProperty.call(e,s)||!Object.is(t[s],e[s]))return!1;return!0}function J(t){let e=t.state;const n=t.actions,o=new Set,s=t.middleware;function r(){return e}function i(){for(const f of o)f(e)}function a(f,m){const b=n[f];if(!b)throw new Error(`[pulse] Unknown action: "${f}"`);const _=b(e,m);_!==e&&(e=_,i())}function p(f,m){if(f==="__devtools_replace__"){e=m,i();return}const b=n[f];if(!b)throw new Error(`[pulse] Unknown action: "${f}"`);const _={store:c,actionName:f,payload:m,prevState:e,nextState:void 0};let w=0;function G(){if(w<s.length){const C=s[w++];C(_,G)}else{const C=b(_.prevState,_.payload);_.nextState=C,C!==e&&(e=C,i())}}G()}const l=s&&s.length>0?p:a;function h(f){return o.add(f),()=>{o.delete(f)}}function g(f){return{store:c,selector:f}}const c={getState:r,dispatch:l,subscribe:h,select:g};return t.name&&(c.name=t.name),c}function yt(t,e){return(o,s)=>{s(),t.push({actionName:o.actionName,payload:o.payload,prevState:o.prevState,nextState:o.nextState??o.prevState,timestamp:Date.now()}),t.length>1/0&&t.splice(0,t.length-1/0)}}let gt=1;class Q{constructor(){S(this,"stores",new Map);S(this,"components",new Map);S(this,"listeners",new Set)}registerStore(e,n,o){const s=o||e.name||`store_${this.stores.size}`;this.stores.set(s,{store:e,history:n,name:s}),e.subscribe(()=>{this.emit({type:"action-dispatched",storeName:s})}),this.emit({type:"store-registered",storeName:s})}getStoreNames(){return Array.from(this.stores.keys())}getStoreState(e){var n;return(n=this.stores.get(e))==null?void 0:n.store.getState()}getTrackedStore(e){return this.stores.get(e)}getHistory(e){var o;if(e)return((o=this.stores.get(e))==null?void 0:o.history)??[];const n=[];for(const s of this.stores.values())n.push(...s.history);return n.sort((s,r)=>s.timestamp-r.timestamp)}trackComponent(e,n){const o=gt++;return this.components.set(o,{id:o,displayName:e,storeNames:n}),this.emit({type:"component-mounted",data:{id:o,displayName:e,storeNames:n}}),o}untrackComponent(e){const n=this.components.get(e);n&&(this.components.delete(e),this.emit({type:"component-unmounted",data:{id:e,displayName:n.displayName}}))}getComponents(){return Array.from(this.components.values())}on(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}emit(e){for(const n of this.listeners)n(e)}}function St(t,e){const n=[],o=yt(n),s=J({...e,middleware:[o,...e.middleware||[]]});return t.registerStore(s,n,e.name),{store:s,history:n}}const B=Symbol("TEXT_NODE"),bt=Symbol("FRAGMENT");function z(t){return{type:B,props:{nodeValue:String(t)},children:[],key:null}}function _t(t){return t==null||typeof t=="boolean"?null:typeof t=="string"||typeof t=="number"?z(t):t}function Y(t){const e=[];for(const n of t)if(Array.isArray(n))e.push(...Y(n));else{const o=_t(n);o!==null&&e.push(o)}return e}function u(t,e,...n){e=e||{};const o=e.key??null;e.key!==void 0&&(e={...e},delete e.key);const s=Y(n);return{type:t,props:e,children:s,key:o}}const y={CREATE:"CREATE",REMOVE:"REMOVE",REPLACE:"REPLACE",UPDATE:"UPDATE",TEXT:"TEXT",MOVE:"MOVE",CHILDREN:"CHILDREN"};function x(t,e){if(e==null&&t==null)return[];if(e==null)return[{type:y.REMOVE,target:t}];if(t==null)return[{type:y.CREATE,newVNode:e}];if(t.type!==e.type)return[{type:y.REPLACE,oldVNode:t,newVNode:e}];if(e._dom=t._dom,t.type===B)return t.props.nodeValue!==e.props.nodeValue?[{type:y.TEXT,oldVNode:t,newVNode:e}]:[];const n=[],o=xt(t.props,e.props);o&&n.push({type:y.UPDATE,target:t,propPatches:o});const s=Et(t.children,e.children);return s.length&&n.push({type:y.CHILDREN,parent:t,childPatches:s}),n}function xt(t,e){const n={},o=[];let s=!1;for(const r in e)r!=="children"&&t[r]!==e[r]&&(n[r]=e[r],s=!0);for(const r in t)r!=="children"&&(r in e||(o.push(r),s=!0));return s?{set:n,remove:o}:null}function O(t,e){return t==null||e==null?!1:t.type===e.type&&t.key===e.key}function K(t,e){const n=new Set;let o=0,s=0;for(const r of t)r!=null&&(r.key!=null?(o++,n.has(r.key)&&console.warn(`[pulse] Duplicate key "${String(r.key)}" in ${e} children. Keys must be unique among siblings.`),n.add(r.key)):s++);o>0&&s>0&&console.warn(`[pulse] Mixed keyed and unkeyed children in ${e} list (${o} keyed, ${s} unkeyed). Either all children should have keys or none should.`)}function Et(t,e){var g;process.env.NODE_ENV!=="production"&&(K(t,"old"),K(e,"new"));const n=[];let o=0,s=t.length-1,r=0,i=e.length-1,a=t[o],p=t[s],l=e[r],h=e[i];for(;o<=s&&r<=i;){if(a==null){a=t[++o];continue}if(p==null){p=t[--s];continue}if(O(a,l))n.push(...x(a,l)),a=t[++o],l=e[++r];else if(O(p,h))n.push(...x(p,h)),p=t[--s],h=e[--i];else if(O(a,h))n.push({type:y.MOVE,vnode:a,anchor:t[s+1]||null,childPatches:x(a,h)}),a=t[++o],h=e[--i];else if(O(p,l))n.push({type:y.MOVE,vnode:p,anchor:a,childPatches:x(p,l)}),p=t[--s],l=e[++r];else break}if(o<=s&&r<=i){const c=new Map;for(let f=o;f<=s;f++){const m=(g=t[f])==null?void 0:g.key;m!=null&&c.set(m,f)}for(;r<=i;){l=e[r];const f=l.key!=null?c.get(l.key):void 0;if(f!==void 0){const m=t[f];n.push({type:y.MOVE,vnode:m,anchor:t[o]||null,childPatches:x(m,l)}),t[f]=null,c.delete(l.key)}else n.push({type:y.CREATE,newVNode:l,anchor:t[o]||null});r++}for(let f=o;f<=s;f++)t[f]!=null&&n.push({type:y.REMOVE,target:t[f]})}if(o>s){const c=e[i+1]||null;for(let f=r;f<=i;f++)n.push({type:y.CREATE,newVNode:e[f],anchor:c})}else if(r>i)for(let c=o;c<=s;c++)t[c]!=null&&n.push({type:y.REMOVE,target:t[c]});return n}const Z="http://www.w3.org/2000/svg";function v(t,e){if(t.type===B){const o=document.createTextNode(t.props.nodeValue);return t._dom=o,o}if(t.type===bt){const o=document.createDocumentFragment();for(const s of t.children)o.appendChild(v(s,e));return t._dom=o,o}t.type==="svg"?e=Z:t.type==="foreignObject"&&(e=void 0);const n=e?document.createElementNS(e,t.type):document.createElement(t.type);Tt(n,{},t.props);for(const o of t.children)n.appendChild(v(o,e));return t._dom=n,n}function Tt(t,e,n){for(const o in e)o==="children"||o==="key"||o in n||et(t,o,e[o]);for(const o in n)o==="children"||o==="key"||e[o]!==n[o]&&tt(t,o,n[o],e[o])}function tt(t,e,n,o){if(e.startsWith("on")){const s=e.slice(2).toLowerCase();o&&t.removeEventListener(s,o),n&&t.addEventListener(s,n)}else if(e==="className"||e==="class")t instanceof SVGElement?t.setAttribute("class",n||""):t.className=n||"";else if(e==="style"&&typeof n=="object"){if(typeof o=="object"&&o)for(const s in o)s in n||(t.style[s]="");Object.assign(t.style,n)}else if(e==="dangerouslySetInnerHTML")n&&typeof n.__html=="string"&&(t.innerHTML=n.__html);else if(e==="ref")typeof n=="function"&&n(t);else if(e in t&&!(t instanceof SVGElement))try{t[e]=n??""}catch{t.setAttribute(e,n)}else n===!0?t.setAttribute(e,""):n===!1||n==null?t.removeAttribute(e):t.setAttribute(e,n)}function et(t,e,n){if(e.startsWith("on"))t.removeEventListener(e.slice(2).toLowerCase(),n);else if(e==="dangerouslySetInnerHTML")t.innerHTML="";else if(e==="className"||e==="class")t instanceof SVGElement?t.removeAttribute("class"):t.className="";else if(e in t&&!(t instanceof SVGElement))try{t[e]=""}catch{t.removeAttribute(e)}else t.removeAttribute(e)}function W(t){return t instanceof SVGElement?Z:void 0}function P(t,e){var n,o,s;for(const r of e)switch(r.type){case y.CREATE:{const i=v(r.newVNode,W(t));(n=r.anchor)!=null&&n._dom?t.insertBefore(i,r.anchor._dom):t.appendChild(i);break}case y.REMOVE:{const i=r.target._dom;i!=null&&i.parentNode&&i.parentNode.removeChild(i);break}case y.REPLACE:{const i=r.oldVNode._dom,a=i==null?void 0:i.parentNode,p=v(r.newVNode,a?W(a):void 0);a&&a.replaceChild(p,i);break}case y.UPDATE:{const i=r.target._dom,{set:a,remove:p}=r.propPatches;for(const l of p)et(i,l,r.target.props[l]);for(const l in a)tt(i,l,a[l],r.target.props[l]);break}case y.TEXT:{const i=r.oldVNode._dom;i&&(i.nodeValue=r.newVNode.props.nodeValue);break}case y.MOVE:{const i=r.vnode._dom;i&&((o=r.anchor)!=null&&o._dom?t.insertBefore(i,r.anchor._dom):t.appendChild(i)),(s=r.childPatches)!=null&&s.length&&i&&P(i,r.childPatches);break}case y.CHILDREN:{const i=r.parent._dom;i&&r.childPatches.length&&P(i,r.childPatches);break}}}const $=new WeakMap;function kt(t,e){const n=$.get(e);if(n){const o=E(t,e),s=[];M(n.vTree,s);const r=x(n.vTree,o);P(e,r);const i=[];o&&M(o,i);const a=new Set(i);for(const l of s)a.has(l)||l.unmount();const p=new Set(s);for(const l of i)p.has(l)||l.mount(e,()=>U(l,e));$.set(e,{vTree:o})}else{const o=E(t,e);if(!o)return;const s=v(o);e.appendChild(s);const r=[];M(o,r);for(const i of r)i.mount(e,()=>U(i,e));$.set(e,{vTree:o})}}function E(t,e){var n;if(t==null)return null;if(typeof t.type=="function"){if(t.type[q]){const s=t.type._lifecycle;try{const r=new ht(t.type,t.props),i=t.type(t.props),p=E(i,e)??z("");if(p._instance){const l={type:"div",props:{style:{display:"contents"}},children:[p],key:t.key};return l._instance=r,r.lastVTree=l,l}return p._instance=r,r.lastVTree=p,p}catch(r){if(s!=null&&s.onError){const i=s.onError({error:r,props:t.props});return E(i,e)}throw r}}const o=t.type({...t.props,children:t.children});return E(o,e)}return(n=t.children)!=null&&n.length&&(t.children=t.children.map(o=>E(o,e)).filter(o=>o!=null)),t}function U(t,e){var s,r;if(!t._renderCallback)return;const n=t.connectedFn,o=n._lifecycle;try{const i=n(t.props),p=E(i,e)??z("");let l;if(p._instance&&p._instance!==t?(l={type:"div",props:{style:{display:"contents"}},children:[p],key:null},l._instance=t):(p._instance=t,l=p),t.lastVTree){H(t.lastVTree,t);const m=x(t.lastVTree,l),b=((s=t.lastVTree._dom)==null?void 0:s.parentNode)||e;P(b,m),l._dom||(l._dom=t.lastVTree._dom)}const h=[];L(t.lastVTree,h,t);const g=[];L(l,g,t);const c=new Set(g);for(const m of h)c.has(m)||m.unmount();t.lastVTree=l;const f=new Set(h);for(const m of g)f.has(m)||m.mount(e,()=>U(m,e));o!=null&&o.onUpdate&&o.onUpdate({dom:l==null?void 0:l._dom,props:t.props}),t.updateSelected()}catch(i){if(o!=null&&o.onError){const a=o.onError({error:i,props:t.props}),p=E(a,e);if(t.lastVTree&&p){H(t.lastVTree,t);const h=x(t.lastVTree,p),g=((r=t.lastVTree._dom)==null?void 0:r.parentNode)||e;P(g,h),p._dom||(p._dom=t.lastVTree._dom)}const l=[];L(t.lastVTree,l,t);for(const h of l)h.unmount();t.lastVTree=p,t.updateSelected()}else throw i}}function H(t,e){if(!(!t||!t.children))for(let n=0;n<t.children.length;n++){const o=t.children[n];o._instance&&o._instance!==e&&o._instance.lastVTree&&o._instance.lastVTree!==o&&(t.children[n]=o._instance.lastVTree),H(t.children[n],e)}}function M(t,e){if(t&&(t._instance&&e.push(t._instance),t.children))for(const n of t.children)M(n,e)}function L(t,e,n){if(t&&(t._instance&&t._instance!==n&&e.push(t._instance),t.children))for(const o of t.children)L(o,e,n)}function j(t,e,n){const o=t.getTrackedStore(e);if(!o)throw new Error(`[pulse-devtools] Unknown store: "${e}"`);const{store:s,history:r}=o;if(n<0||n>=r.length)throw new Error(`[pulse-devtools] Index ${n} out of range (0..${r.length-1})`);const i=r[n].nextState;s.dispatch("__devtools_replace__",i),t.emit({type:"time-travel",storeName:e,data:{entryIndex:n}})}function wt(t,e,n){const o=t.getTrackedStore(e);if(!o)throw new Error(`[pulse-devtools] Unknown store: "${e}"`);const{store:s,history:r}=o;if(n<0||n>=r.length)throw new Error(`[pulse-devtools] Index ${n} out of range (0..${r.length-1})`);const i=r.slice(n),a=r[n].prevState;s.dispatch("__devtools_replace__",a);for(const p of i)s.dispatch(p.actionName,p.payload);t.emit({type:"time-travel",storeName:e,data:{replayFrom:n}})}const d={base:"#1e1e2e",surface0:"#313244",surface1:"#45475a",overlay0:"#6c7086",text:"#cdd6f4",subtext0:"#a6adc8",blue:"#89b4fa",green:"#a6e3a1",yellow:"#f9e2af",mauve:"#cba6f7",teal:"#94e2d5",peach:"#fab387"},Ct={position:"fixed",bottom:"0",left:"0",right:"0",height:"320px",backgroundColor:d.base,color:d.text,fontFamily:"'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace",fontSize:"12px",lineHeight:"1.5",zIndex:"2147483647",display:"flex",flexDirection:"column",borderTop:`2px solid ${d.mauve}`,overflow:"hidden"},Nt={display:"flex",alignItems:"center",backgroundColor:d.surface0,borderBottom:`1px solid ${d.surface1}`,padding:"0 8px",height:"32px",flexShrink:"0"},vt=t=>({background:"none",border:"none",color:t?d.mauve:d.subtext0,fontFamily:"inherit",fontSize:"12px",padding:"6px 12px",cursor:"pointer",borderBottom:t?`2px solid ${d.mauve}`:"2px solid transparent",marginBottom:"-1px"}),Pt={background:"none",border:"none",color:d.overlay0,fontSize:"16px",cursor:"pointer",marginLeft:"auto",padding:"4px 8px",fontFamily:"inherit"},Vt={flex:"1",overflow:"auto",padding:"8px 12px"},Ot={display:"flex",height:"100%",gap:"1px"},It={width:"200px",flexShrink:"0",borderRight:`1px solid ${d.surface1}`,overflow:"auto",padding:"4px 0"},Mt={flex:"1",overflow:"auto",padding:"8px"},Lt=t=>({padding:"4px 12px",cursor:"pointer",backgroundColor:t?d.surface1:"transparent",color:t?d.blue:d.text}),X={color:d.mauve},I={color:d.green},$t={color:d.yellow},At={color:d.peach},k={color:d.overlay0,fontStyle:"italic"},Rt=t=>({padding:"4px 8px",cursor:"pointer",backgroundColor:t?d.surface1:"transparent",borderLeft:t?`3px solid ${d.blue}`:"3px solid transparent",display:"flex",justifyContent:"space-between",alignItems:"center"}),nt={color:d.blue,fontWeight:"bold"},Ft={color:d.overlay0,fontSize:"10px"},Ut={width:"100%",accentColor:d.mauve,margin:"4px 0 8px"},Ht={width:"100%",backgroundColor:d.surface0,border:`1px solid ${d.surface1}`,color:d.text,padding:"4px 8px",fontFamily:"inherit",fontSize:"12px",borderRadius:"4px",outline:"none",marginBottom:"8px",boxSizing:"border-box"},jt={padding:"4px 8px",borderBottom:`1px solid ${d.surface0}`},Dt={color:d.teal,fontWeight:"bold"},Bt={color:d.overlay0,fontSize:"10px",marginLeft:"8px"},ot={display:"inline-block",backgroundColor:d.surface1,color:d.subtext0,padding:"1px 6px",borderRadius:"8px",fontSize:"10px",marginLeft:"4px"},zt={color:d.mauve,fontWeight:"bold",fontSize:"12px",marginRight:"8px"};function D(t,e){if(t===null)return u("span",{style:k},"null");if(t===void 0)return u("span",{style:k},"undefined");if(typeof t=="string")return u("span",{style:$t},`"${t}"`);if(typeof t=="boolean")return u("span",{style:At},String(t));if(typeof t=="number")return u("span",{style:I},String(t));if(Array.isArray(t))return t.length===0?u("span",{style:I},"[]"):e>4?u("span",{style:k},"[…]"):u("div",{style:{paddingLeft:"12px"}},...t.map((n,o)=>u("div",{key:o},u("span",{style:X},`${o}: `),D(n,e+1))));if(typeof t=="object"){const n=Object.keys(t);return n.length===0?u("span",{style:I},"{}"):e>4?u("span",{style:k},"{…}"):u("div",{style:{paddingLeft:"12px"}},...n.map(o=>u("div",{key:o},u("span",{style:X},`${o}: `),D(t[o],e+1))))}return u("span",{style:I},String(t))}function Gt({storeNames:t,selectedStore:e,storeStates:n,onSelectStore:o}){const s=e?n[e]:null;return u("div",{style:Ot},u("div",{style:It},...t.map(r=>u("div",{key:r,style:Lt(r===e),onClick:()=>o(r)},r))),u("div",{style:Mt},e?u("div",null,u("div",{style:{marginBottom:"8px",color:d.subtext0}},"State of ",u("span",{style:nt},e)),s!=null?D(s,0):u("span",{style:k},"No state")):u("span",{style:k},"Select a store")))}function Kt(t){const e=new Date(t),n=o=>String(o).padStart(2,"0");return`${n(e.getHours())}:${n(e.getMinutes())}:${n(e.getSeconds())}`}function Wt({actionLog:t,timeTravelIndex:e,filter:n,onFilterChange:o,onTravelTo:s,onSliderChange:r}){const i=n?t.filter(a=>a.actionName.toLowerCase().includes(n.toLowerCase())):t;return u("div",{style:{height:"100%",display:"flex",flexDirection:"column"}},u("input",{style:Ht,placeholder:"Filter actions…",value:n,onInput:a=>o(a.target.value)}),t.length>0?u("div",{style:{flexShrink:"0"}},u("input",{type:"range",style:Ut,min:"0",max:String(t.length-1),value:String(e),onInput:a=>r(Number(a.target.value))}),u("div",{style:{color:d.overlay0,fontSize:"10px",marginBottom:"4px"}},`${e+1} / ${t.length}`)):null,u("div",{style:{flex:"1",overflow:"auto"}},...i.map(a=>{const p=t.indexOf(a);return u("div",{key:p,style:Rt(p===e),onClick:()=>s(p)},u("span",null,u("span",{style:nt},a.actionName),a.payload!==void 0?u("span",{style:{color:d.subtext0,marginLeft:"8px"}},typeof a.payload=="object"?JSON.stringify(a.payload):String(a.payload)):null),u("span",{style:Ft},Kt(a.timestamp)))}),i.length===0?u("div",{style:{color:d.overlay0,padding:"8px"}},t.length===0?"No actions yet":"No matching actions"):null))}function Xt({components:t}){return t.length===0?u("div",{style:{color:d.overlay0,padding:"8px"}},"No connected components mounted"):u("div",null,...t.map(e=>u("div",{key:e.id,style:jt},u("span",{style:Dt},e.displayName),e.storeNames.length>0?u("span",{style:Bt},e.storeNames.map(n=>u("span",{key:n,style:ot},n))):null)))}function qt(){return J({state:{open:!1,activeTab:"stores",selectedStore:null,timeTravelIndex:-1,storeNames:[],storeStates:{},actionLog:[],filter:"",components:[]},actions:{toggle:t=>({...t,open:!t.open}),open:t=>({...t,open:!0}),close:t=>({...t,open:!1}),setTab:(t,e)=>({...t,activeTab:e}),selectStore:(t,e)=>({...t,selectedStore:e}),setFilter:(t,e)=>({...t,filter:e}),setTimeTravelIndex:(t,e)=>({...t,timeTravelIndex:e}),sync:(t,e)=>({...t,...e})}})}function Jt({open:t,activeTab:e,selectedStore:n,timeTravelIndex:o,storeNames:s,storeStates:r,actionLog:i,filter:a,components:p,panelActions:l}){if(!t)return null;const h=[{id:"stores",label:"Stores"},{id:"actions",label:"Actions"},{id:"components",label:"Components"}];let g;return e==="stores"?g=Gt({storeNames:s,selectedStore:n,storeStates:r,onSelectStore:l.selectStore}):e==="actions"?g=Wt({actionLog:i,timeTravelIndex:o,filter:a,onFilterChange:l.setFilter,onTravelTo:l.travelTo,onSliderChange:l.sliderChange}):g=Xt({components:p}),u("div",{style:Ct},u("div",{style:Nt},u("span",{key:"title",style:zt},"Pulse"),...h.map(c=>u("button",{key:c.id,style:vt(c.id===e),onClick:()=>l.setTab(c.id)},c.label)),u("span",{key:"badge",style:ot},`${s.length} stores`),u("button",{key:"close",style:Pt,onClick:l.close},"×")),u("div",{style:Vt},g))}function Qt(t,e){const n=qt();e&&e(n);let o=!1;function s(){if(!o){o=!0;try{const c=t.getStoreNames(),f={};for(const w of c)f[w]=t.getStoreState(w);const b=n.getState().selectedStore||c[0]||null,_=b?t.getHistory(b):[];n.dispatch("sync",{storeNames:c,storeStates:f,actionLog:_,timeTravelIndex:_.length>0?_.length-1:-1,selectedStore:b,components:t.getComponents()})}finally{o=!1}}}t.on(c=>{c.type==="component-mounted"||c.type==="component-unmounted"||s()});const r={selectStore:c=>{n.dispatch("selectStore",c);const f=t.getHistory(c);n.dispatch("sync",{actionLog:f,timeTravelIndex:f.length>0?f.length-1:-1})},setTab:c=>n.dispatch("setTab",c),setFilter:c=>n.dispatch("setFilter",c),close:()=>n.dispatch("close"),travelTo:c=>{const f=n.getState().selectedStore;f&&(j(t,f,c),n.dispatch("setTimeTravelIndex",c))},sliderChange:c=>{const f=n.getState().selectedStore;f&&(j(t,f,c),n.dispatch("setTimeTravelIndex",c))}},i=dt({open:n.select(c=>c.open),activeTab:n.select(c=>c.activeTab),selectedStore:n.select(c=>c.selectedStore),timeTravelIndex:n.select(c=>c.timeTravelIndex),storeNames:n.select(c=>c.storeNames),storeStates:n.select(c=>c.storeStates),actionLog:n.select(c=>c.actionLog),filter:n.select(c=>c.filter),components:n.select(c=>c.components)})(c=>Jt({...c,panelActions:r}));let a=null;function p(){a||(a=document.createElement("div"),a.id="pulse-devtools-root",document.body.appendChild(a),kt(u(i,null),a),s())}function l(){p(),n.dispatch("open")}function h(){n.dispatch("close")}function g(){p(),n.dispatch("toggle")}return{openPanel:l,closePanel:h,togglePanel:g,panelStore:n}}const V=new Q,st=new WeakSet;function rt(t){st.add(t)}ft(t=>{const e=t.connectedFn._bindings||{},n=Object.values(e).map(i=>i.store);if(n.length>0&&n.every(i=>st.has(i)))return;const o=n.map(i=>i.name||"unnamed"),s=t.connectedFn.displayName||"Unknown",r=V.trackComponent(s,o);t._devtoolsId=r},t=>{const e=t._devtoolsId;e!=null&&V.untrackComponent(e)});let N=null;function it(){return N||(N=Qt(V,rt)),N}function Yt(){it().openPanel()}function Zt(){N&&N.closePanel()}function ct(){it().togglePanel()}typeof window<"u"&&(window.addEventListener("keydown",t=>{t.ctrlKey&&t.shiftKey&&t.key==="P"&&(t.preventDefault(),ct())}),window.__PULSE_DEVTOOLS__=V);exports.PulseDevtools=Q;exports._markInternalStore=rt;exports.closePanel=Zt;exports.devtools=V;exports.instrumentStore=St;exports.openPanel=Yt;exports.replayFrom=wt;exports.togglePanel=ct;exports.travelTo=j;
|
|
2
|
+
//# sourceMappingURL=devtools.cjs.map
|