@assistant-ui/react-devtools 0.1.1
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 +65 -0
- package/dist/DevToolsFrame.d.ts +8 -0
- package/dist/DevToolsFrame.d.ts.map +1 -0
- package/dist/DevToolsFrame.js +49 -0
- package/dist/DevToolsFrame.js.map +1 -0
- package/dist/DevToolsHost.d.ts +35 -0
- package/dist/DevToolsHost.d.ts.map +1 -0
- package/dist/DevToolsHost.js +104 -0
- package/dist/DevToolsHost.js.map +1 -0
- package/dist/DevToolsModal.d.ts +2 -0
- package/dist/DevToolsModal.d.ts.map +1 -0
- package/dist/DevToolsModal.js +170 -0
- package/dist/DevToolsModal.js.map +1 -0
- package/dist/ExtensionHost.d.ts +7 -0
- package/dist/ExtensionHost.d.ts.map +1 -0
- package/dist/ExtensionHost.js +50 -0
- package/dist/ExtensionHost.js.map +1 -0
- package/dist/FrameClient.d.ts +35 -0
- package/dist/FrameClient.d.ts.map +1 -0
- package/dist/FrameClient.js +63 -0
- package/dist/FrameClient.js.map +1 -0
- package/dist/FrameHost.d.ts +8 -0
- package/dist/FrameHost.d.ts.map +1 -0
- package/dist/FrameHost.js +28 -0
- package/dist/FrameHost.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/styles/DevToolsModal.styles.d.ts +14 -0
- package/dist/styles/DevToolsModal.styles.d.ts.map +1 -0
- package/dist/styles/DevToolsModal.styles.js +121 -0
- package/dist/styles/DevToolsModal.styles.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/serialization.d.ts +5 -0
- package/dist/utils/serialization.d.ts.map +1 -0
- package/dist/utils/serialization.js +77 -0
- package/dist/utils/serialization.js.map +1 -0
- package/dist/utils/toolNormalization.d.ts +9 -0
- package/dist/utils/toolNormalization.d.ts.map +1 -0
- package/dist/utils/toolNormalization.js +58 -0
- package/dist/utils/toolNormalization.js.map +1 -0
- package/package.json +76 -0
- package/src/DevToolsFrame.tsx +55 -0
- package/src/DevToolsHost.ts +150 -0
- package/src/DevToolsModal.tsx +178 -0
- package/src/ExtensionHost.ts +57 -0
- package/src/FrameClient.ts +98 -0
- package/src/FrameHost.ts +31 -0
- package/src/constants.ts +2 -0
- package/src/index.ts +17 -0
- package/src/styles/DevToolsModal.styles.ts +137 -0
- package/src/types.ts +13 -0
- package/src/utils/serialization.ts +97 -0
- package/src/utils/toolNormalization.ts +83 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
export interface DevToolsModalStyles {
|
|
4
|
+
floatingContainer: CSSProperties;
|
|
5
|
+
floatingButton: CSSProperties;
|
|
6
|
+
floatingButtonHover: CSSProperties;
|
|
7
|
+
backdrop: CSSProperties;
|
|
8
|
+
modal: CSSProperties;
|
|
9
|
+
dismissButton: CSSProperties;
|
|
10
|
+
dismissButtonHover: CSSProperties;
|
|
11
|
+
modalContent: CSSProperties;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const COLORS = {
|
|
15
|
+
light: {
|
|
16
|
+
primary: "#2563EB",
|
|
17
|
+
background: "#f8fafc",
|
|
18
|
+
surface: "#ffffff",
|
|
19
|
+
text: "#111827",
|
|
20
|
+
textLight: "#6b7280",
|
|
21
|
+
border: "rgba(148, 163, 184, 0.35)",
|
|
22
|
+
shadow: "rgba(37, 99, 235, 0.35)",
|
|
23
|
+
buttonText: "#f9fafb",
|
|
24
|
+
hoverBg: "rgba(148, 163, 184, 0.18)",
|
|
25
|
+
},
|
|
26
|
+
dark: {
|
|
27
|
+
primary: "#111827",
|
|
28
|
+
background: "#09090b",
|
|
29
|
+
surface: "#09090b",
|
|
30
|
+
text: "#e5e7eb",
|
|
31
|
+
textLight: "#9ca3af",
|
|
32
|
+
border: "rgba(63, 63, 70, 0.6)",
|
|
33
|
+
shadow: "rgba(0, 0, 0, 0.55)",
|
|
34
|
+
buttonText: "#e5e7eb",
|
|
35
|
+
hoverBg: "rgba(148, 163, 184, 0.12)",
|
|
36
|
+
},
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
export const getStyles = (darkMode: boolean): DevToolsModalStyles => {
|
|
40
|
+
const theme = darkMode ? COLORS.dark : COLORS.light;
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
floatingContainer: {
|
|
44
|
+
position: "fixed",
|
|
45
|
+
bottom: "24px",
|
|
46
|
+
right: "24px",
|
|
47
|
+
zIndex: 2147483647,
|
|
48
|
+
},
|
|
49
|
+
floatingButton: {
|
|
50
|
+
width: "42px",
|
|
51
|
+
height: "42px",
|
|
52
|
+
borderRadius: "9999px",
|
|
53
|
+
border: "none",
|
|
54
|
+
background: theme.primary,
|
|
55
|
+
color: theme.buttonText,
|
|
56
|
+
cursor: "pointer",
|
|
57
|
+
display: "flex",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
justifyContent: "center",
|
|
60
|
+
boxShadow: darkMode
|
|
61
|
+
? `0 10px 40px ${COLORS.dark.shadow}`
|
|
62
|
+
: `0 10px 40px ${COLORS.light.shadow}`,
|
|
63
|
+
transition: "transform 0.2s ease, box-shadow 0.2s ease",
|
|
64
|
+
},
|
|
65
|
+
floatingButtonHover: {
|
|
66
|
+
transform: "translateY(-2px)",
|
|
67
|
+
boxShadow: darkMode
|
|
68
|
+
? "0 16px 50px rgba(17, 24, 39, 0.55)"
|
|
69
|
+
: "0 16px 50px rgba(37, 99, 235, 0.45)",
|
|
70
|
+
},
|
|
71
|
+
backdrop: {
|
|
72
|
+
position: "fixed",
|
|
73
|
+
inset: 0,
|
|
74
|
+
background: "rgba(15, 23, 42, 0.45)",
|
|
75
|
+
backdropFilter: "blur(6px)",
|
|
76
|
+
animation: "fadeIn 0.12s ease",
|
|
77
|
+
zIndex: 2147483646,
|
|
78
|
+
},
|
|
79
|
+
modal: {
|
|
80
|
+
position: "fixed",
|
|
81
|
+
top: "50%",
|
|
82
|
+
left: "50%",
|
|
83
|
+
transform: "translate(-50%, -50%)",
|
|
84
|
+
width: "min(960px, 90vw)",
|
|
85
|
+
height: "min(720px, 85vh)",
|
|
86
|
+
background: theme.background,
|
|
87
|
+
borderRadius: "16px",
|
|
88
|
+
border: `1px solid ${theme.border}`,
|
|
89
|
+
boxShadow: darkMode
|
|
90
|
+
? "0 32px 120px rgba(0, 0, 0, 0.55)"
|
|
91
|
+
: "0 32px 120px rgba(15, 23, 42, 0.35)",
|
|
92
|
+
display: "flex",
|
|
93
|
+
flexDirection: "column",
|
|
94
|
+
overflow: "hidden",
|
|
95
|
+
animation: "slideIn 0.16s ease",
|
|
96
|
+
zIndex: 2147483647,
|
|
97
|
+
},
|
|
98
|
+
dismissButton: {
|
|
99
|
+
alignSelf: "flex-end",
|
|
100
|
+
margin: "10px 12px 0 0",
|
|
101
|
+
background: "transparent",
|
|
102
|
+
border: "none",
|
|
103
|
+
color: theme.textLight,
|
|
104
|
+
cursor: "pointer",
|
|
105
|
+
padding: "6px",
|
|
106
|
+
borderRadius: "6px",
|
|
107
|
+
transition: "background 0.2s ease, color 0.2s ease",
|
|
108
|
+
},
|
|
109
|
+
dismissButtonHover: {
|
|
110
|
+
background: theme.hoverBg,
|
|
111
|
+
color: theme.text,
|
|
112
|
+
},
|
|
113
|
+
modalContent: {
|
|
114
|
+
flex: 1,
|
|
115
|
+
overflow: "hidden",
|
|
116
|
+
position: "relative",
|
|
117
|
+
background: theme.background,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const ANIMATION_STYLES = `
|
|
123
|
+
@keyframes fadeIn {
|
|
124
|
+
from { opacity: 0; }
|
|
125
|
+
to { opacity: 1; }
|
|
126
|
+
}
|
|
127
|
+
@keyframes slideIn {
|
|
128
|
+
from {
|
|
129
|
+
opacity: 0;
|
|
130
|
+
transform: translate(-50%, -48%) scale(0.95);
|
|
131
|
+
}
|
|
132
|
+
to {
|
|
133
|
+
opacity: 1;
|
|
134
|
+
transform: translate(-50%, -50%) scale(1);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
`;
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NormalizedTool } from "./utils/toolNormalization";
|
|
2
|
+
|
|
3
|
+
// Core types for DevTools UI
|
|
4
|
+
export type TabType = "state" | "events" | "context";
|
|
5
|
+
export type ViewMode = "raw" | "preview";
|
|
6
|
+
|
|
7
|
+
// Types used by serialization utility
|
|
8
|
+
export interface SerializedModelContext {
|
|
9
|
+
system?: string;
|
|
10
|
+
tools?: NormalizedTool[];
|
|
11
|
+
callSettings?: Record<string, unknown>;
|
|
12
|
+
config?: Record<string, unknown>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { ModelContext } from "@assistant-ui/react";
|
|
2
|
+
import type { SerializedModelContext } from "../types";
|
|
3
|
+
import { normalizeToolList } from "./toolNormalization";
|
|
4
|
+
|
|
5
|
+
export const sanitizeForMessage = (
|
|
6
|
+
value: unknown,
|
|
7
|
+
seen = new WeakSet<object>(),
|
|
8
|
+
): unknown => {
|
|
9
|
+
// Early return for primitives
|
|
10
|
+
if (value === null || value === undefined) return value;
|
|
11
|
+
if (
|
|
12
|
+
typeof value === "string" ||
|
|
13
|
+
typeof value === "number" ||
|
|
14
|
+
typeof value === "boolean"
|
|
15
|
+
) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === "function") {
|
|
19
|
+
return "[Function]";
|
|
20
|
+
}
|
|
21
|
+
if (value instanceof Date) {
|
|
22
|
+
return value.toISOString();
|
|
23
|
+
}
|
|
24
|
+
if (value instanceof Map) {
|
|
25
|
+
const result: Record<string, unknown> = {};
|
|
26
|
+
for (const [key, entry] of value.entries()) {
|
|
27
|
+
result[String(key)] = sanitizeForMessage(entry, seen);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
if (value instanceof Set) {
|
|
32
|
+
return Array.from(value).map((entry) => sanitizeForMessage(entry, seen));
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
if (seen.has(value as unknown as object)) return "[Circular]";
|
|
36
|
+
seen.add(value as unknown as object);
|
|
37
|
+
return value
|
|
38
|
+
.map((entry) => sanitizeForMessage(entry, seen))
|
|
39
|
+
.filter((item) => item !== undefined);
|
|
40
|
+
}
|
|
41
|
+
if (typeof value === "object") {
|
|
42
|
+
if (seen.has(value as object)) return "[Circular]";
|
|
43
|
+
seen.add(value as object);
|
|
44
|
+
const result: Record<string, unknown> = {};
|
|
45
|
+
for (const [key, entry] of Object.entries(
|
|
46
|
+
value as Record<string, unknown>,
|
|
47
|
+
)) {
|
|
48
|
+
result[key] = sanitizeForMessage(entry, seen);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const serializeModelContext = (
|
|
56
|
+
context: ModelContext | undefined,
|
|
57
|
+
): SerializedModelContext | undefined => {
|
|
58
|
+
if (!context || typeof context !== "object") {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const modelContext = context as Record<string, unknown>;
|
|
63
|
+
const result: SerializedModelContext = {};
|
|
64
|
+
|
|
65
|
+
const systemValue = modelContext["system"];
|
|
66
|
+
if (typeof systemValue === "string" && systemValue.length > 0) {
|
|
67
|
+
result.system = systemValue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const tools = normalizeToolList(modelContext["tools"]);
|
|
71
|
+
if (tools.length > 0) {
|
|
72
|
+
result.tools = tools.map((tool) => ({
|
|
73
|
+
...tool,
|
|
74
|
+
parameters: sanitizeForMessage(tool.parameters),
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (modelContext["callSettings"] !== undefined) {
|
|
79
|
+
const callSettings = sanitizeForMessage(modelContext["callSettings"]);
|
|
80
|
+
if (
|
|
81
|
+
callSettings &&
|
|
82
|
+
typeof callSettings === "object" &&
|
|
83
|
+
!Array.isArray(callSettings)
|
|
84
|
+
) {
|
|
85
|
+
result.callSettings = callSettings as Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (modelContext["config"] !== undefined) {
|
|
90
|
+
const config = sanitizeForMessage(modelContext["config"]);
|
|
91
|
+
if (config && typeof config === "object" && !Array.isArray(config)) {
|
|
92
|
+
result.config = config as Record<string, unknown>;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
97
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export type NormalizedTool = {
|
|
4
|
+
name: string;
|
|
5
|
+
type?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
parameters?: unknown;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
12
|
+
value !== null && typeof value === "object";
|
|
13
|
+
|
|
14
|
+
const toJsonSchema = (value: unknown): unknown => {
|
|
15
|
+
if (value instanceof z.ZodType) {
|
|
16
|
+
try {
|
|
17
|
+
return z.toJSONSchema(value);
|
|
18
|
+
} catch {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return value;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const mapToNormalizedTool = (
|
|
27
|
+
name: string,
|
|
28
|
+
raw: Record<string, unknown>,
|
|
29
|
+
): NormalizedTool => {
|
|
30
|
+
const tool: NormalizedTool = { name };
|
|
31
|
+
|
|
32
|
+
if (typeof raw["type"] === "string") {
|
|
33
|
+
tool.type = raw["type"] as string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof raw["description"] === "string") {
|
|
37
|
+
tool.description = raw["description"] as string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (typeof raw["disabled"] === "boolean") {
|
|
41
|
+
tool.disabled = raw["disabled"] as boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (Object.prototype.hasOwnProperty.call(raw, "parameters")) {
|
|
45
|
+
tool.parameters = toJsonSchema(raw["parameters"]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return tool;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const normalizeToolList = (value: unknown): NormalizedTool[] => {
|
|
52
|
+
if (!value || typeof value !== "object") {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
const tools: NormalizedTool[] = [];
|
|
58
|
+
|
|
59
|
+
for (const entry of value) {
|
|
60
|
+
if (!isRecord(entry) || typeof entry["name"] !== "string") continue;
|
|
61
|
+
tools.push(mapToNormalizedTool(entry["name"] as string, entry));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return tools;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (isRecord(value)) {
|
|
68
|
+
const tools: NormalizedTool[] = [];
|
|
69
|
+
|
|
70
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
71
|
+
if (!isRecord(entry)) {
|
|
72
|
+
tools.push({ name });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
tools.push(mapToNormalizedTool(name, entry));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return tools;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return [];
|
|
83
|
+
};
|