@epoch32/visage 0.0.0-stage → 1.0.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 (135) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +110 -2
  3. package/dist/src/api/index.js +4002 -0
  4. package/dist/src/charts/index.js +2717 -0
  5. package/dist/src/devtools/index.js +4677 -0
  6. package/dist/src/dom/index.js +1550 -0
  7. package/dist/src/form/index.js +1415 -0
  8. package/dist/src/index.js +11826 -0
  9. package/dist/src/jsx-runtime/index.js +1630 -0
  10. package/dist/src/query/index.js +2422 -0
  11. package/dist/src/router/index.js +1856 -0
  12. package/dist/src/rpc/index.js +3484 -0
  13. package/dist/src/server/index.js +1173 -0
  14. package/dist/src/signals/index.js +1077 -0
  15. package/dist/src/style/index.js +2340 -0
  16. package/dist/src/template/index.js +1149 -0
  17. package/dist/src/ui/index.js +4155 -0
  18. package/dist/src/virtual/index.js +1955 -0
  19. package/dist/src/vite/index.js +984 -0
  20. package/package.json +140 -4
  21. package/src/api/client.ts +371 -0
  22. package/src/api/file-router.ts +183 -0
  23. package/src/api/handler.ts +326 -0
  24. package/src/api/index.ts +71 -0
  25. package/src/api/middleware.ts +54 -0
  26. package/src/api/pragma.ts +155 -0
  27. package/src/api/procedure.ts +172 -0
  28. package/src/api/types.ts +249 -0
  29. package/src/charts/Area.tsx +111 -0
  30. package/src/charts/Axis.tsx +126 -0
  31. package/src/charts/Bar.tsx +86 -0
  32. package/src/charts/CartesianGrid.tsx +72 -0
  33. package/src/charts/Chart.tsx +269 -0
  34. package/src/charts/Cursor.tsx +42 -0
  35. package/src/charts/Legend.tsx +56 -0
  36. package/src/charts/Line.tsx +106 -0
  37. package/src/charts/Pie.tsx +88 -0
  38. package/src/charts/Tooltip.tsx +127 -0
  39. package/src/charts/context.ts +30 -0
  40. package/src/charts/index.ts +14 -0
  41. package/src/charts/path.ts +155 -0
  42. package/src/charts/scale.ts +133 -0
  43. package/src/charts/types.ts +50 -0
  44. package/src/devtools/DomTab.tsx +134 -0
  45. package/src/devtools/GraphInspector.tsx +196 -0
  46. package/src/devtools/GraphTab.tsx +315 -0
  47. package/src/devtools/QueryTab.tsx +200 -0
  48. package/src/devtools/RpcTab.tsx +190 -0
  49. package/src/devtools/SignalsTab.tsx +129 -0
  50. package/src/devtools/component.tsx +243 -0
  51. package/src/devtools/highlighter.ts +134 -0
  52. package/src/devtools/hub.ts +175 -0
  53. package/src/devtools/index.ts +20 -0
  54. package/src/devtools/styles.ts +313 -0
  55. package/src/devtools/types.ts +45 -0
  56. package/src/dom/async.ts +232 -0
  57. package/src/dom/control-flow.ts +353 -0
  58. package/src/dom/index.ts +32 -0
  59. package/src/dom/lazy.ts +82 -0
  60. package/src/dom/render.ts +286 -0
  61. package/src/dom/resource.ts +120 -0
  62. package/src/form/form.ts +218 -0
  63. package/src/form/index.ts +17 -0
  64. package/src/form/schema.ts +299 -0
  65. package/src/index.ts +413 -0
  66. package/src/jsx-dev-runtime/index.ts +1 -0
  67. package/src/jsx-runtime/index.ts +112 -0
  68. package/src/jsx-runtime/jsx.d.ts +278 -0
  69. package/src/query/create-mutation.ts +64 -0
  70. package/src/query/create-query.ts +426 -0
  71. package/src/query/focus-manager.ts +68 -0
  72. package/src/query/index.ts +63 -0
  73. package/src/query/mutation-cache.ts +138 -0
  74. package/src/query/online-manager.ts +67 -0
  75. package/src/query/query-cache.ts +369 -0
  76. package/src/query/query-client.ts +248 -0
  77. package/src/query/types.ts +221 -0
  78. package/src/query/utils.ts +132 -0
  79. package/src/router/index.ts +19 -0
  80. package/src/router/router.tsx +352 -0
  81. package/src/rpc/client.ts +317 -0
  82. package/src/rpc/error.ts +70 -0
  83. package/src/rpc/index.ts +52 -0
  84. package/src/rpc/procedure.ts +88 -0
  85. package/src/rpc/router.ts +105 -0
  86. package/src/rpc/server.ts +511 -0
  87. package/src/rpc/types.ts +328 -0
  88. package/src/server/hydrate.ts +21 -0
  89. package/src/server/index.ts +10 -0
  90. package/src/server/render-to-stream.ts +31 -0
  91. package/src/server/render-to-string.ts +125 -0
  92. package/src/signals/computed.ts +217 -0
  93. package/src/signals/context.ts +89 -0
  94. package/src/signals/effect.ts +161 -0
  95. package/src/signals/index.ts +57 -0
  96. package/src/signals/scope.ts +132 -0
  97. package/src/signals/signal.ts +203 -0
  98. package/src/signals/store.ts +163 -0
  99. package/src/signals/system.ts +124 -0
  100. package/src/signals/types.ts +64 -0
  101. package/src/style/component.tsx +122 -0
  102. package/src/style/create-style.ts +229 -0
  103. package/src/style/cx.ts +75 -0
  104. package/src/style/hash.ts +19 -0
  105. package/src/style/index.ts +8 -0
  106. package/src/style/layout.tsx +406 -0
  107. package/src/style/manager.ts +87 -0
  108. package/src/style/parser.ts +257 -0
  109. package/src/style/scale.ts +237 -0
  110. package/src/template/index.ts +4 -0
  111. package/src/template/template.ts +126 -0
  112. package/src/ui/accordion.tsx +286 -0
  113. package/src/ui/command-dialog.tsx +49 -0
  114. package/src/ui/command.tsx +400 -0
  115. package/src/ui/data-table.tsx +322 -0
  116. package/src/ui/dialog.tsx +362 -0
  117. package/src/ui/dropdown-menu.tsx +381 -0
  118. package/src/ui/icon.tsx +69 -0
  119. package/src/ui/index.ts +138 -0
  120. package/src/ui/layout.tsx +13 -0
  121. package/src/ui/popover.tsx +249 -0
  122. package/src/ui/switch.tsx +114 -0
  123. package/src/ui/tabs.tsx +297 -0
  124. package/src/ui/toast.tsx +280 -0
  125. package/src/ui/tooltip.tsx +262 -0
  126. package/src/ui/utils.ts +110 -0
  127. package/src/virtual/index.ts +12 -0
  128. package/src/virtual/virtual-list.tsx +185 -0
  129. package/src/virtual/virtualizer.ts +274 -0
  130. package/src/vite/hmr.ts +38 -0
  131. package/src/vite/index.ts +3 -0
  132. package/src/vite/lucide.ts +124 -0
  133. package/src/vite/plugin.ts +102 -0
  134. package/src/vite/rpc-middleware.ts +82 -0
  135. package/src/vite/types.ts +65 -0
@@ -0,0 +1,200 @@
1
+ import { For, Show } from "../dom";
2
+ import type { WritableSignal } from "../signals";
3
+ import type { DevToolsHub } from "./hub";
4
+ import type { QueryRecord } from "./types";
5
+
6
+ interface QueryTabProps {
7
+ hub: DevToolsHub;
8
+ selectedId: WritableSignal<string | null>;
9
+ searchQuery: WritableSignal<string>;
10
+ formatJSON: (val: any) => string;
11
+ }
12
+
13
+ export function QueryTab(props: QueryTabProps) {
14
+ const filteredQueries = () => {
15
+ const q = props.searchQuery().toLowerCase().trim();
16
+ const list = props.hub.queriesList();
17
+ if (!q) return list;
18
+ return list.filter(
19
+ (item) =>
20
+ item.hash.toLowerCase().includes(q) || JSON.stringify(item.key).toLowerCase().includes(q)
21
+ );
22
+ };
23
+
24
+ const selectedQuery = () => {
25
+ const id = props.selectedId();
26
+ if (!id) return null;
27
+ return props.hub.queriesList().find((qr) => qr.hash === id) ?? null;
28
+ };
29
+
30
+ return (
31
+ <div style={{ display: "flex", flex: 1, width: "100%", height: "100%", overflow: "hidden" }}>
32
+ {/* Query List */}
33
+ <div class="visage-devtools-sidebar">
34
+ <div class="visage-devtools-search">
35
+ <input
36
+ class="visage-devtools-input"
37
+ placeholder="Search query cache..."
38
+ value={props.searchQuery}
39
+ onInput={(e: any) => props.searchQuery.set(e.target.value)}
40
+ />
41
+ </div>
42
+ <div class="visage-devtools-list">
43
+ <For
44
+ each={filteredQueries}
45
+ fallback={
46
+ <div style={{ padding: "12px", color: "#64748b", fontSize: "12px" }}>
47
+ No active queries in cache.
48
+ </div>
49
+ }
50
+ >
51
+ {(qr: QueryRecord) => {
52
+ const isSelected = () => props.selectedId() === qr.hash;
53
+ return (
54
+ <div
55
+ class={() => `visage-devtools-item ${isSelected() ? "selected" : ""}`}
56
+ onClick={() => props.selectedId.set(qr.hash)}
57
+ >
58
+ <div
59
+ style={{
60
+ display: "flex",
61
+ alignItems: "center",
62
+ gap: "6px",
63
+ overflow: "hidden"
64
+ }}
65
+ >
66
+ <span
67
+ class="visage-devtools-badge"
68
+ style={{
69
+ background:
70
+ qr.status() === "success"
71
+ ? "#065f46"
72
+ : qr.status() === "error"
73
+ ? "#991b1b"
74
+ : "#854d0e",
75
+ color: "#fff"
76
+ }}
77
+ >
78
+ {() => qr.status()}
79
+ </span>
80
+ <span
81
+ style={{
82
+ fontWeight: 500,
83
+ overflow: "hidden",
84
+ textOverflow: "ellipsis",
85
+ whiteSpace: "nowrap"
86
+ }}
87
+ >
88
+ {JSON.stringify(qr.key)}
89
+ </span>
90
+ </div>
91
+ <span style={{ fontSize: "10px", color: "#64748b" }}>
92
+ {() => qr.fetchStatus()}
93
+ </span>
94
+ </div>
95
+ );
96
+ }}
97
+ </For>
98
+ </div>
99
+ </div>
100
+
101
+ {/* Query Detail */}
102
+ <div class="visage-devtools-main">
103
+ <Show
104
+ when={selectedQuery}
105
+ fallback={
106
+ <div
107
+ style={{ color: "#64748b", textAlign: "center", marginTop: "40px", fontSize: "13px" }}
108
+ >
109
+ Select a query from the cache list to view live cached response and trigger manual
110
+ refetches.
111
+ </div>
112
+ }
113
+ >
114
+ {(qr: () => QueryRecord) => (
115
+ <div>
116
+ <div
117
+ style={{
118
+ display: "flex",
119
+ alignItems: "center",
120
+ justifyContent: "space-between",
121
+ marginBottom: "16px"
122
+ }}
123
+ >
124
+ <div>
125
+ <h3 style={{ margin: 0, fontSize: "15px", color: "#f8fafc" }}>
126
+ {() => JSON.stringify(qr().key)}
127
+ </h3>
128
+ <span style={{ fontSize: "11px", color: "#64748b" }}>
129
+ Hash: {() => qr().hash}
130
+ </span>
131
+ </div>
132
+ <div style={{ display: "flex", gap: "8px" }}>
133
+ <button
134
+ type="button"
135
+ class="visage-devtools-btn"
136
+ onClick={() => qr().refetch?.()}
137
+ >
138
+ Refetch
139
+ </button>
140
+ <button
141
+ type="button"
142
+ class="visage-devtools-btn"
143
+ onClick={() => qr().invalidate?.()}
144
+ >
145
+ Invalidate Cache
146
+ </button>
147
+ </div>
148
+ </div>
149
+
150
+ <div
151
+ style={{
152
+ display: "grid",
153
+ gridTemplateColumns: "1fr 1fr",
154
+ gap: "10px",
155
+ marginBottom: "16px"
156
+ }}
157
+ >
158
+ <div
159
+ style={{
160
+ background: "#0c111d",
161
+ padding: "10px",
162
+ borderRadius: "6px",
163
+ border: "1px solid #1e293b"
164
+ }}
165
+ >
166
+ <div style={{ fontSize: "11px", color: "#64748b" }}>STATUS</div>
167
+ <div style={{ fontWeight: 600, color: "#38bdf8" }}>
168
+ {() => `${qr().status()} (${qr().fetchStatus()})`}
169
+ </div>
170
+ </div>
171
+ <div
172
+ style={{
173
+ background: "#0c111d",
174
+ padding: "10px",
175
+ borderRadius: "6px",
176
+ border: "1px solid #1e293b"
177
+ }}
178
+ >
179
+ <div style={{ fontSize: "11px", color: "#64748b" }}>LAST UPDATED</div>
180
+ <div style={{ fontWeight: 600, color: "#f1f5f9" }}>
181
+ {() =>
182
+ qr().dataUpdatedAt()
183
+ ? new Date(qr().dataUpdatedAt()).toLocaleTimeString()
184
+ : "Never"
185
+ }
186
+ </div>
187
+ </div>
188
+ </div>
189
+
190
+ <div style={{ fontSize: "12px", color: "#94a3b8", marginBottom: "6px" }}>
191
+ Cached Response Payload:
192
+ </div>
193
+ <pre class="visage-devtools-json">{() => props.formatJSON(qr().data())}</pre>
194
+ </div>
195
+ )}
196
+ </Show>
197
+ </div>
198
+ </div>
199
+ );
200
+ }
@@ -0,0 +1,190 @@
1
+ import { For, Show } from "../dom";
2
+ import type { WritableSignal } from "../signals";
3
+ import type { DevToolsHub } from "./hub";
4
+ import type { RpcRecord } from "./types";
5
+
6
+ interface RpcTabProps {
7
+ hub: DevToolsHub;
8
+ selectedId: WritableSignal<string | null>;
9
+ searchQuery: WritableSignal<string>;
10
+ formatJSON: (val: any) => string;
11
+ }
12
+
13
+ export function RpcTab(props: RpcTabProps) {
14
+ const filteredRpc = () => {
15
+ const q = props.searchQuery().toLowerCase().trim();
16
+ const list = props.hub.rpcLogs();
17
+ if (!q) return list;
18
+ return list.filter((r) => r.path.toLowerCase().includes(q) || r.type.toLowerCase().includes(q));
19
+ };
20
+
21
+ const selectedRpc = () => {
22
+ const id = props.selectedId();
23
+ if (!id) return null;
24
+ return props.hub.rpcLogs().find((r) => r.id === id) ?? null;
25
+ };
26
+
27
+ return (
28
+ <div style={{ display: "flex", flex: 1, width: "100%", height: "100%", overflow: "hidden" }}>
29
+ {/* RPC Calls List */}
30
+ <div class="visage-devtools-sidebar">
31
+ <div class="visage-devtools-search" style={{ display: "flex", gap: "6px" }}>
32
+ <input
33
+ class="visage-devtools-input"
34
+ placeholder="Filter RPC routes..."
35
+ value={props.searchQuery}
36
+ onInput={(e: any) => props.searchQuery.set(e.target.value)}
37
+ />
38
+ <button
39
+ type="button"
40
+ class="visage-devtools-btn"
41
+ onClick={() => props.hub.clearLogs()}
42
+ title="Clear RPC Logs"
43
+ >
44
+ Clear
45
+ </button>
46
+ </div>
47
+ <div class="visage-devtools-list">
48
+ <For
49
+ each={filteredRpc}
50
+ fallback={
51
+ <div style={{ padding: "12px", color: "#64748b", fontSize: "12px" }}>
52
+ No RPC activity recorded yet.
53
+ </div>
54
+ }
55
+ >
56
+ {(r: RpcRecord) => {
57
+ const isSelected = () => props.selectedId() === r.id;
58
+ return (
59
+ <div
60
+ class={() => `visage-devtools-item ${isSelected() ? "selected" : ""}`}
61
+ onClick={() => props.selectedId.set(r.id)}
62
+ >
63
+ <div
64
+ style={{
65
+ display: "flex",
66
+ alignItems: "center",
67
+ gap: "6px",
68
+ overflow: "hidden"
69
+ }}
70
+ >
71
+ <span
72
+ class="visage-devtools-badge"
73
+ style={{
74
+ background:
75
+ r.type === "mutation"
76
+ ? "#c026d3"
77
+ : r.type === "subscription"
78
+ ? "#7c3aed"
79
+ : "#0284c7",
80
+ color: "#fff"
81
+ }}
82
+ >
83
+ {r.type.slice(0, 3).toUpperCase()}
84
+ </span>
85
+ <span
86
+ style={{
87
+ fontWeight: 500,
88
+ overflow: "hidden",
89
+ textOverflow: "ellipsis",
90
+ whiteSpace: "nowrap"
91
+ }}
92
+ >
93
+ {r.path}
94
+ </span>
95
+ </div>
96
+ <div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
97
+ <span
98
+ style={{
99
+ fontSize: "11px",
100
+ color:
101
+ r.status === "success"
102
+ ? "#10b981"
103
+ : r.status === "error"
104
+ ? "#ef4444"
105
+ : "#f59e0b"
106
+ }}
107
+ >
108
+ {r.status}
109
+ </span>
110
+ {r.duration !== undefined ? (
111
+ <span style={{ fontSize: "10px", color: "#64748b" }}>{r.duration}ms</span>
112
+ ) : null}
113
+ </div>
114
+ </div>
115
+ );
116
+ }}
117
+ </For>
118
+ </div>
119
+ </div>
120
+
121
+ {/* RPC Detail View */}
122
+ <div class="visage-devtools-main">
123
+ <Show
124
+ when={selectedRpc}
125
+ fallback={
126
+ <div
127
+ style={{ color: "#64748b", textAlign: "center", marginTop: "40px", fontSize: "13px" }}
128
+ >
129
+ Select an RPC procedure call from the left to inspect full payload, latency, and
130
+ streaming SSE frames.
131
+ </div>
132
+ }
133
+ >
134
+ {(r: () => RpcRecord) => (
135
+ <div>
136
+ <div
137
+ style={{
138
+ display: "flex",
139
+ alignItems: "center",
140
+ justifyContent: "space-between",
141
+ marginBottom: "16px"
142
+ }}
143
+ >
144
+ <div>
145
+ <h3 style={{ margin: 0, fontSize: "16px", color: "#f8fafc" }}>
146
+ {() => `${r().type.toUpperCase()}: ${r().path}`}
147
+ </h3>
148
+ <span style={{ fontSize: "11px", color: "#64748b" }}>
149
+ Call ID: {() => r().id}
150
+ </span>
151
+ </div>
152
+ <span
153
+ class="visage-devtools-badge"
154
+ style={{ fontSize: "12px", padding: "3px 8px" }}
155
+ >
156
+ {() => (r().duration !== undefined ? `${r().duration}ms` : "Active")}
157
+ </span>
158
+ </div>
159
+
160
+ <div style={{ marginBottom: "12px" }}>
161
+ <div style={{ fontSize: "12px", color: "#94a3b8", marginBottom: "4px" }}>
162
+ Input Parameters:
163
+ </div>
164
+ <pre class="visage-devtools-json">{() => props.formatJSON(r().input)}</pre>
165
+ </div>
166
+
167
+ <Show when={() => r().response !== undefined}>
168
+ <div style={{ marginBottom: "12px" }}>
169
+ <div style={{ fontSize: "12px", color: "#94a3b8", marginBottom: "4px" }}>
170
+ Server Response:
171
+ </div>
172
+ <pre class="visage-devtools-json">{() => props.formatJSON(r().response)}</pre>
173
+ </div>
174
+ </Show>
175
+
176
+ <Show when={() => (r().events?.length ?? 0) > 0}>
177
+ <div>
178
+ <div style={{ fontSize: "12px", color: "#94a3b8", marginBottom: "4px" }}>
179
+ Live SSE Event Stream ({() => r().events?.length ?? 0} frames):
180
+ </div>
181
+ <pre class="visage-devtools-json">{() => props.formatJSON(r().events)}</pre>
182
+ </div>
183
+ </Show>
184
+ </div>
185
+ )}
186
+ </Show>
187
+ </div>
188
+ </div>
189
+ );
190
+ }
@@ -0,0 +1,129 @@
1
+ import { For, Show } from "../dom";
2
+ import type { WritableSignal } from "../signals";
3
+ import type { DevToolsHub } from "./hub";
4
+ import type { SignalRecord } from "./types";
5
+
6
+ interface SignalsTabProps {
7
+ hub: DevToolsHub;
8
+ selectedId: WritableSignal<string | null>;
9
+ searchQuery: WritableSignal<string>;
10
+ formatJSON: (val: any) => string;
11
+ }
12
+
13
+ export function SignalsTab(props: SignalsTabProps) {
14
+ const filteredSignals = () => {
15
+ const q = props.searchQuery().toLowerCase().trim();
16
+ const list = props.hub.signalsList();
17
+ if (!q) return list;
18
+ return list.filter((s) => s.name.toLowerCase().includes(q) || s.id.toLowerCase().includes(q));
19
+ };
20
+
21
+ const selectedSignal = () => {
22
+ const id = props.selectedId();
23
+ if (!id) return null;
24
+ return props.hub.signalsList().find((s) => s.id === id) ?? null;
25
+ };
26
+
27
+ return (
28
+ <div style={{ display: "flex", flex: 1, width: "100%", height: "100%", overflow: "hidden" }}>
29
+ {/* Left List */}
30
+ <div class="visage-devtools-sidebar">
31
+ <div class="visage-devtools-search">
32
+ <input
33
+ class="visage-devtools-input"
34
+ placeholder="Search signals..."
35
+ value={props.searchQuery}
36
+ onInput={(e: any) => props.searchQuery.set(e.target.value)}
37
+ />
38
+ </div>
39
+ <div class="visage-devtools-list">
40
+ <For each={filteredSignals}>
41
+ {(sig: SignalRecord) => {
42
+ const isSelected = () => props.selectedId() === sig.id;
43
+ return (
44
+ <div
45
+ class={() => `visage-devtools-item ${sig.type} ${isSelected() ? "selected" : ""}`}
46
+ onClick={() => props.selectedId.set(sig.id)}
47
+ >
48
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
49
+ <span
50
+ style={{
51
+ fontSize: "9px",
52
+ fontWeight: 700,
53
+ padding: "1px 4px",
54
+ borderRadius: "3px",
55
+ background: sig.type === "computed" ? "#3b0764" : "#082f49",
56
+ color: sig.type === "computed" ? "#c084fc" : "#38bdf8",
57
+ border: `1px solid ${sig.type === "computed" ? "#581c87" : "#0369a1"}`
58
+ }}
59
+ >
60
+ {sig.type === "computed" ? "CMP" : "SIG"}
61
+ </span>
62
+ <span style={{ fontWeight: 500, color: "#f1f5f9" }}>{sig.name}</span>
63
+ </div>
64
+ <span class="visage-devtools-badge">v{() => sig.version()}</span>
65
+ </div>
66
+ );
67
+ }}
68
+ </For>
69
+ </div>
70
+ </div>
71
+
72
+ {/* Right Details */}
73
+ <div class="visage-devtools-main">
74
+ <Show
75
+ when={selectedSignal}
76
+ fallback={
77
+ <div
78
+ style={{
79
+ color: "#64748b",
80
+ textAlign: "center",
81
+ marginTop: "40px",
82
+ fontSize: "13px"
83
+ }}
84
+ >
85
+ Select a signal or computed from the left to inspect its live reactive value.
86
+ </div>
87
+ }
88
+ >
89
+ {(sig: () => SignalRecord) => (
90
+ <div>
91
+ <div
92
+ style={{
93
+ display: "flex",
94
+ alignItems: "center",
95
+ justifyContent: "space-between",
96
+ marginBottom: "16px"
97
+ }}
98
+ >
99
+ <div>
100
+ <h3 style={{ margin: 0, fontSize: "16px", color: "#f8fafc" }}>
101
+ {() => sig().name}
102
+ </h3>
103
+ <span style={{ fontSize: "11px", color: "#64748b" }}>ID: {() => sig().id}</span>
104
+ </div>
105
+ <div style={{ display: "flex", gap: "8px" }}>
106
+ <span
107
+ class="visage-devtools-badge"
108
+ style={{
109
+ background: () => (sig().type === "computed" ? "#581c87" : "#0369a1"),
110
+ color: "#fff"
111
+ }}
112
+ >
113
+ {() => sig().type.toUpperCase()}
114
+ </span>
115
+ <span class="visage-devtools-badge">VERSION: {() => sig().version()}</span>
116
+ </div>
117
+ </div>
118
+
119
+ <div style={{ marginBottom: "12px", fontSize: "12px", color: "#94a3b8" }}>
120
+ Current Reactive Value:
121
+ </div>
122
+ <pre class="visage-devtools-json">{() => props.formatJSON(sig().getValue())}</pre>
123
+ </div>
124
+ )}
125
+ </Show>
126
+ </div>
127
+ </div>
128
+ );
129
+ }