@preact/signals-devtools-ui 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/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/devtools-ui.d.ts +5 -0
- package/dist/devtools-ui.js +1 -0
- package/dist/devtools-ui.js.map +1 -0
- package/dist/devtools-ui.min.js +1 -0
- package/dist/devtools-ui.min.js.map +1 -0
- package/dist/devtools-ui.mjs +1 -0
- package/dist/devtools-ui.mjs.map +1 -0
- package/dist/devtools-ui.module.js +1 -0
- package/dist/devtools-ui.module.js.map +1 -0
- package/dist/styles.css +835 -0
- package/package.json +66 -0
- package/src/DevToolsPanel.tsx +107 -0
- package/src/components/Button.tsx +31 -0
- package/src/components/EmptyState.tsx +29 -0
- package/src/components/Graph.tsx +496 -0
- package/src/components/Header.tsx +39 -0
- package/src/components/SettingsPanel.tsx +129 -0
- package/src/components/StatusIndicator.tsx +22 -0
- package/src/components/UpdateItem.tsx +75 -0
- package/src/components/UpdateTreeNode.tsx +69 -0
- package/src/components/UpdatesContainer.tsx +41 -0
- package/src/components/index.ts +9 -0
- package/src/context.ts +359 -0
- package/src/index.ts +47 -0
- package/src/styles.css +835 -0
- package/src/types.ts +35 -0
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@preact/signals-devtools-ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "DevTools UI components for @preact/signals",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"preact",
|
|
8
|
+
"signals",
|
|
9
|
+
"devtools",
|
|
10
|
+
"ui"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/preactjs/signals",
|
|
15
|
+
"directory": "packages/devtools-ui"
|
|
16
|
+
},
|
|
17
|
+
"bugs": "https://github.com/preactjs/signals/issues",
|
|
18
|
+
"homepage": "https://preactjs.com",
|
|
19
|
+
"funding": {
|
|
20
|
+
"type": "opencollective",
|
|
21
|
+
"url": "https://opencollective.com/preact"
|
|
22
|
+
},
|
|
23
|
+
"amdName": "preactSignalsDevtoolsUI",
|
|
24
|
+
"main": "dist/devtools-ui.js",
|
|
25
|
+
"module": "dist/devtools-ui.module.js",
|
|
26
|
+
"unpkg": "dist/devtools-ui.min.js",
|
|
27
|
+
"types": "dist/devtools-ui.d.ts",
|
|
28
|
+
"source": "src/index.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/devtools-ui.d.ts",
|
|
32
|
+
"browser": "./dist/devtools-ui.module.js",
|
|
33
|
+
"import": "./dist/devtools-ui.mjs",
|
|
34
|
+
"require": "./dist/devtools-ui.js"
|
|
35
|
+
},
|
|
36
|
+
"./styles": "./dist/styles.css"
|
|
37
|
+
},
|
|
38
|
+
"mangle": "../../mangle.json",
|
|
39
|
+
"files": [
|
|
40
|
+
"src",
|
|
41
|
+
"dist",
|
|
42
|
+
"CHANGELOG.md",
|
|
43
|
+
"LICENSE",
|
|
44
|
+
"README.md"
|
|
45
|
+
],
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@preact/signals-devtools-adapter": "0.1.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^18.19.103",
|
|
51
|
+
"@vitest/browser": "^3.2.4",
|
|
52
|
+
"playwright": "^1.53.1",
|
|
53
|
+
"preact": "^10.26.9",
|
|
54
|
+
"typescript": "~5.8.3",
|
|
55
|
+
"vite": "^6.3.5",
|
|
56
|
+
"vitest": "^3.2.4",
|
|
57
|
+
"@preact/signals": "2.6.0"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public",
|
|
61
|
+
"provenance": false
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"test": "vitest run"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { render } from "preact";
|
|
2
|
+
import { useSignal } from "@preact/signals";
|
|
3
|
+
import type { DevToolsAdapter } from "@preact/signals-devtools-adapter";
|
|
4
|
+
import { EmptyState } from "./components/EmptyState";
|
|
5
|
+
import { Header } from "./components/Header";
|
|
6
|
+
import { SettingsPanel } from "./components/SettingsPanel";
|
|
7
|
+
import { GraphVisualization } from "./components/Graph";
|
|
8
|
+
import { UpdatesContainer } from "./components/UpdatesContainer";
|
|
9
|
+
import { initDevTools, destroyDevTools, getContext } from "./context";
|
|
10
|
+
|
|
11
|
+
export interface DevToolsPanelProps {
|
|
12
|
+
/** Hide the header (useful for embedded contexts) */
|
|
13
|
+
hideHeader?: boolean;
|
|
14
|
+
/** Initial tab to show */
|
|
15
|
+
initialTab?: "updates" | "graph";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function DevToolsPanel({
|
|
19
|
+
hideHeader = false,
|
|
20
|
+
initialTab = "updates",
|
|
21
|
+
}: DevToolsPanelProps = {}) {
|
|
22
|
+
const { connectionStore } = getContext();
|
|
23
|
+
const activeTab = useSignal<"updates" | "graph">(initialTab);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div id="app" className="signals-devtools">
|
|
27
|
+
{!hideHeader && <Header />}
|
|
28
|
+
|
|
29
|
+
<SettingsPanel />
|
|
30
|
+
|
|
31
|
+
<main className="main-content">
|
|
32
|
+
<div className="tabs">
|
|
33
|
+
<button
|
|
34
|
+
className={`tab ${activeTab.value === "updates" ? "active" : ""}`}
|
|
35
|
+
onClick={() => (activeTab.value = "updates")}
|
|
36
|
+
>
|
|
37
|
+
Updates
|
|
38
|
+
</button>
|
|
39
|
+
<button
|
|
40
|
+
className={`tab ${activeTab.value === "graph" ? "active" : ""}`}
|
|
41
|
+
onClick={() => (activeTab.value = "graph")}
|
|
42
|
+
>
|
|
43
|
+
Dependency Graph
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
<div className="tab-content">
|
|
47
|
+
{!connectionStore.isConnected ? (
|
|
48
|
+
<EmptyState onRefresh={connectionStore.refreshConnection} />
|
|
49
|
+
) : (
|
|
50
|
+
<>
|
|
51
|
+
{activeTab.value === "updates" && <UpdatesContainer />}
|
|
52
|
+
{activeTab.value === "graph" && <GraphVisualization />}
|
|
53
|
+
</>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
</main>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface MountOptions extends DevToolsPanelProps {
|
|
62
|
+
/** The adapter to use for communication */
|
|
63
|
+
adapter: DevToolsAdapter;
|
|
64
|
+
/** The container element to render into */
|
|
65
|
+
container: HTMLElement;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Mount the DevTools UI into a container element.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```tsx
|
|
73
|
+
* import { mount } from "@preact/signals-devtools-ui";
|
|
74
|
+
* import { createDirectAdapter } from "@preact/signals-devtools-adapter";
|
|
75
|
+
*
|
|
76
|
+
* const adapter = createDirectAdapter();
|
|
77
|
+
*
|
|
78
|
+
* const unmount = await mount({
|
|
79
|
+
* adapter,
|
|
80
|
+
* container: document.getElementById("devtools")!,
|
|
81
|
+
* });
|
|
82
|
+
*
|
|
83
|
+
* // Later, to cleanup:
|
|
84
|
+
* unmount();
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export async function mount(options: MountOptions): Promise<() => void> {
|
|
88
|
+
const { adapter, container, ...panelProps } = options;
|
|
89
|
+
|
|
90
|
+
// Initialize context with adapter
|
|
91
|
+
initDevTools(adapter);
|
|
92
|
+
|
|
93
|
+
// Connect the adapter
|
|
94
|
+
await adapter.connect();
|
|
95
|
+
|
|
96
|
+
// Clear existing content
|
|
97
|
+
container.innerHTML = "";
|
|
98
|
+
|
|
99
|
+
// Render the panel
|
|
100
|
+
render(<DevToolsPanel {...panelProps} />, container);
|
|
101
|
+
|
|
102
|
+
// Return cleanup function
|
|
103
|
+
return () => {
|
|
104
|
+
render(null, container);
|
|
105
|
+
destroyDevTools();
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ComponentChildren } from "preact";
|
|
2
|
+
|
|
3
|
+
interface ButtonProps {
|
|
4
|
+
onClick: () => void;
|
|
5
|
+
className?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
children: ComponentChildren;
|
|
8
|
+
variant?: "primary" | "secondary";
|
|
9
|
+
active?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function Button({
|
|
13
|
+
onClick,
|
|
14
|
+
className = "",
|
|
15
|
+
disabled = false,
|
|
16
|
+
children,
|
|
17
|
+
variant = "secondary",
|
|
18
|
+
active = false,
|
|
19
|
+
}: ButtonProps) {
|
|
20
|
+
const baseClass = "btn";
|
|
21
|
+
const variantClass = variant === "primary" ? "btn-primary" : "btn-secondary";
|
|
22
|
+
const activeClass = active ? "active" : "";
|
|
23
|
+
const combinedClassName =
|
|
24
|
+
`${baseClass} ${variantClass} ${activeClass} ${className}`.trim();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<button onClick={onClick} className={combinedClassName} disabled={disabled}>
|
|
28
|
+
{children}
|
|
29
|
+
</button>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Button } from "./Button";
|
|
2
|
+
|
|
3
|
+
interface EmptyStateProps {
|
|
4
|
+
onRefresh: () => void;
|
|
5
|
+
title?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
buttonText?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function EmptyState({
|
|
11
|
+
onRefresh,
|
|
12
|
+
title = "No Signals Detected",
|
|
13
|
+
description = "Make sure your application is using @preact/signals-debug package.",
|
|
14
|
+
buttonText = "Refresh Detection",
|
|
15
|
+
}: EmptyStateProps) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="empty-state">
|
|
18
|
+
<div className="empty-state-content">
|
|
19
|
+
<h2>{title}</h2>
|
|
20
|
+
<p>{description}</p>
|
|
21
|
+
<div className="empty-state-actions">
|
|
22
|
+
<Button onClick={onRefresh} variant="primary">
|
|
23
|
+
{buttonText}
|
|
24
|
+
</Button>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|