@_davideast/stitch-mcp 0.5.3 → 0.5.5
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/dist/chunk-18nfnnkg.js +947 -0
- package/dist/chunk-18nfnnkg.js.map +28 -0
- package/dist/chunk-1sgyj1qf.js +256 -0
- package/dist/chunk-1sgyj1qf.js.map +11 -0
- package/dist/chunk-1tzaa3zn.js +370 -0
- package/dist/chunk-1tzaa3zn.js.map +16 -0
- package/dist/chunk-2efzz3tw.js +10 -0
- package/dist/chunk-2efzz3tw.js.map +9 -0
- package/dist/chunk-2eq8thmz.js +31529 -0
- package/dist/chunk-2eq8thmz.js.map +245 -0
- package/dist/chunk-4xa2a5hb.js +19 -0
- package/dist/chunk-4xa2a5hb.js.map +9 -0
- package/dist/chunk-7vdj1qwb.js +2121 -0
- package/dist/chunk-7vdj1qwb.js.map +44 -0
- package/dist/chunk-7zyv8g2t.js +5216 -0
- package/dist/chunk-7zyv8g2t.js.map +67 -0
- package/dist/chunk-94xqpnv4.js +7 -0
- package/dist/chunk-94xqpnv4.js.map +9 -0
- package/dist/chunk-b43pzs3z.js +839 -0
- package/dist/chunk-b43pzs3z.js.map +11 -0
- package/dist/chunk-byzfppa1.js +759 -0
- package/dist/chunk-byzfppa1.js.map +19 -0
- package/dist/chunk-cjkw69md.js +94 -0
- package/dist/chunk-cjkw69md.js.map +10 -0
- package/dist/chunk-d92ngrr6.js +680 -0
- package/dist/chunk-d92ngrr6.js.map +17 -0
- package/dist/chunk-de74byjc.js +19 -0
- package/dist/chunk-de74byjc.js.map +9 -0
- package/dist/chunk-ewab4gg0.js +17 -0
- package/dist/chunk-ewab4gg0.js.map +9 -0
- package/dist/chunk-f0phn3y1.js +1495 -0
- package/dist/chunk-f0phn3y1.js.map +23 -0
- package/dist/chunk-f398cwqb.js +24 -0
- package/dist/chunk-f398cwqb.js.map +9 -0
- package/dist/chunk-fkzq5m59.js +111 -0
- package/dist/chunk-fkzq5m59.js.map +10 -0
- package/dist/chunk-gzk8pt16.js +44184 -0
- package/dist/chunk-gzk8pt16.js.map +237 -0
- package/dist/chunk-kme6y874.js +125 -0
- package/dist/chunk-kme6y874.js.map +12 -0
- package/dist/chunk-mw5wn97e.js +109 -0
- package/dist/chunk-mw5wn97e.js.map +10 -0
- package/dist/chunk-mxcybqhd.js +412 -0
- package/dist/chunk-mxcybqhd.js.map +20 -0
- package/dist/chunk-nep9nerg.js +137 -0
- package/dist/chunk-nep9nerg.js.map +10 -0
- package/dist/chunk-nrcb494d.js +50 -0
- package/dist/chunk-nrcb494d.js.map +9 -0
- package/dist/chunk-p9vvygz4.js +736 -0
- package/dist/chunk-p9vvygz4.js.map +16 -0
- package/dist/chunk-rd2ye9s7.js +17 -0
- package/dist/chunk-rd2ye9s7.js.map +9 -0
- package/dist/chunk-svk5y62j.js +164 -0
- package/dist/chunk-svk5y62j.js.map +10 -0
- package/dist/chunk-w3wh3zkf.js +269 -0
- package/dist/chunk-w3wh3zkf.js.map +10 -0
- package/dist/chunk-x1tt02n9.js +264 -0
- package/dist/chunk-x1tt02n9.js.map +12 -0
- package/dist/chunk-x7g5p1gv.js +66 -0
- package/dist/chunk-x7g5p1gv.js.map +10 -0
- package/dist/chunk-z7b1n864.js +246 -0
- package/dist/chunk-z7b1n864.js.map +14 -0
- package/dist/chunk-zcc6seqb.js +19132 -0
- package/dist/chunk-zcc6seqb.js.map +115 -0
- package/dist/commands/doctor/command.js +1 -1
- package/dist/commands/init/command.js +1 -1
- package/dist/commands/logout/command.js +1 -1
- package/dist/commands/proxy/command.js +1 -1
- package/dist/commands/screens/command.js +4 -4
- package/dist/commands/serve/command.js +5 -5
- package/dist/commands/site/command.js +1 -1
- package/dist/commands/snapshot/command.js +1 -1
- package/dist/commands/tool/command.js +1 -1
- package/dist/commands/view/command.js +1 -1
- package/dist/index.js +5 -7
- package/dist/index.js.map +1 -1
- package/dist/ui/copy-behaviors/clipboard.d.ts +0 -7
- package/package.json +1 -3
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
import {
|
|
2
|
+
openUrl,
|
|
3
|
+
require_jsx_dev_runtime
|
|
4
|
+
} from "./chunk-x1tt02n9.js";
|
|
5
|
+
import {
|
|
6
|
+
Box_default,
|
|
7
|
+
Text,
|
|
8
|
+
use_app_default,
|
|
9
|
+
use_input_default
|
|
10
|
+
} from "./chunk-zcc6seqb.js";
|
|
11
|
+
import {
|
|
12
|
+
require_react
|
|
13
|
+
} from "./chunk-b43pzs3z.js";
|
|
14
|
+
import {
|
|
15
|
+
copyJson,
|
|
16
|
+
copyText,
|
|
17
|
+
downloadAndCopyImage,
|
|
18
|
+
downloadAndCopyText
|
|
19
|
+
} from "./chunk-fkzq5m59.js";
|
|
20
|
+
import"./chunk-q6sv0243.js";
|
|
21
|
+
import"./chunk-3sfn889r.js";
|
|
22
|
+
import {
|
|
23
|
+
__toESM
|
|
24
|
+
} from "./chunk-9wyra8hs.js";
|
|
25
|
+
|
|
26
|
+
// src/ui/InteractiveViewer.tsx
|
|
27
|
+
var import_react2 = __toESM(require_react(), 1);
|
|
28
|
+
|
|
29
|
+
// src/ui/JsonTree.tsx
|
|
30
|
+
var import_react = __toESM(require_react(), 1);
|
|
31
|
+
|
|
32
|
+
// src/ui/copy-behaviors/handlers.ts
|
|
33
|
+
var defaultCopyHandler = {
|
|
34
|
+
async copy(ctx) {
|
|
35
|
+
try {
|
|
36
|
+
await copyJson(ctx.value);
|
|
37
|
+
const preview = typeof ctx.value === "string" ? `"${ctx.value.slice(0, 50)}${ctx.value.length > 50 ? "..." : ""}"` : JSON.stringify(ctx.value).slice(0, 50);
|
|
38
|
+
return { success: true, message: `Copied: ${preview}` };
|
|
39
|
+
} catch (error) {
|
|
40
|
+
return { success: false, message: `Copy failed: ${error}` };
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
async copyExtended(ctx) {
|
|
44
|
+
try {
|
|
45
|
+
const obj = { [ctx.key]: ctx.value };
|
|
46
|
+
await copyJson(obj);
|
|
47
|
+
return { success: true, message: `Copied: { ${ctx.key}: ... }` };
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return { success: false, message: `Copy failed: ${error}` };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var imageUrlCopyHandler = {
|
|
54
|
+
async copy(ctx) {
|
|
55
|
+
try {
|
|
56
|
+
if (typeof ctx.value !== "string") {
|
|
57
|
+
return { success: false, message: "Value is not a URL string" };
|
|
58
|
+
}
|
|
59
|
+
await copyText(ctx.value);
|
|
60
|
+
return { success: true, message: `Copied URL: ${ctx.value.slice(0, 60)}...` };
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return { success: false, message: `Copy failed: ${error}` };
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
async copyExtended(ctx) {
|
|
66
|
+
try {
|
|
67
|
+
if (typeof ctx.value !== "string") {
|
|
68
|
+
return { success: false, message: "Value is not a URL string" };
|
|
69
|
+
}
|
|
70
|
+
ctx.onProgress?.("\uD83D\uDCF7 Downloading image...");
|
|
71
|
+
await downloadAndCopyImage(ctx.value);
|
|
72
|
+
return { success: true, message: "\uD83D\uDCF7 Image copied to clipboard!" };
|
|
73
|
+
} catch (error) {
|
|
74
|
+
return { success: false, message: `Image copy failed: ${error}` };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var htmlCodeCopyHandler = {
|
|
79
|
+
async copy(ctx) {
|
|
80
|
+
try {
|
|
81
|
+
if (typeof ctx.value !== "string") {
|
|
82
|
+
return { success: false, message: "Value is not a URL string" };
|
|
83
|
+
}
|
|
84
|
+
await copyText(ctx.value);
|
|
85
|
+
return { success: true, message: `Copied URL: ${ctx.value.slice(0, 60)}...` };
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return { success: false, message: `Copy failed: ${error}` };
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
async copyExtended(ctx) {
|
|
91
|
+
try {
|
|
92
|
+
if (typeof ctx.value !== "string") {
|
|
93
|
+
return { success: false, message: "Value is not a URL string" };
|
|
94
|
+
}
|
|
95
|
+
ctx.onProgress?.("\uD83D\uDCDD Downloading HTML code...");
|
|
96
|
+
await downloadAndCopyText(ctx.value);
|
|
97
|
+
return { success: true, message: "\uD83D\uDCDD HTML code copied to clipboard!" };
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return { success: false, message: `HTML copy failed: ${error}` };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/ui/copy-behaviors/registry.ts
|
|
105
|
+
var registrations = [];
|
|
106
|
+
function registerHandler(matcher, handler) {
|
|
107
|
+
registrations.push({ matcher, handler });
|
|
108
|
+
}
|
|
109
|
+
function getHandler(path) {
|
|
110
|
+
for (let i = registrations.length - 1;i >= 0; i--) {
|
|
111
|
+
const registration = registrations[i];
|
|
112
|
+
if (registration && registration.matcher(path)) {
|
|
113
|
+
return registration.handler;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return defaultCopyHandler;
|
|
117
|
+
}
|
|
118
|
+
function endsWith(suffix) {
|
|
119
|
+
return (path) => path.endsWith(suffix);
|
|
120
|
+
}
|
|
121
|
+
registerHandler(endsWith(".thumbnailScreenshot.downloadUrl"), imageUrlCopyHandler);
|
|
122
|
+
registerHandler(endsWith(".screenshot.downloadUrl"), imageUrlCopyHandler);
|
|
123
|
+
registerHandler(endsWith(".htmlCode.downloadUrl"), htmlCodeCopyHandler);
|
|
124
|
+
|
|
125
|
+
// src/ui/navigation-behaviors/handler.ts
|
|
126
|
+
function screenInstanceNavigationHandler(ctx) {
|
|
127
|
+
if (!ctx.path.includes("screenInstances")) {
|
|
128
|
+
return { shouldNavigate: false };
|
|
129
|
+
}
|
|
130
|
+
if (ctx.value && typeof ctx.value === "object" && ctx.value.sourceScreen) {
|
|
131
|
+
return {
|
|
132
|
+
shouldNavigate: true,
|
|
133
|
+
target: ctx.value.sourceScreen,
|
|
134
|
+
type: "screen"
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (ctx.key === "sourceScreen" && typeof ctx.value === "string") {
|
|
138
|
+
return {
|
|
139
|
+
shouldNavigate: true,
|
|
140
|
+
target: ctx.value,
|
|
141
|
+
type: "screen"
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return { shouldNavigate: false };
|
|
145
|
+
}
|
|
146
|
+
var navigationHandlers = [
|
|
147
|
+
screenInstanceNavigationHandler
|
|
148
|
+
];
|
|
149
|
+
function getNavigationTarget(ctx) {
|
|
150
|
+
for (const handler of navigationHandlers) {
|
|
151
|
+
const result = handler(ctx);
|
|
152
|
+
if (result.shouldNavigate) {
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { shouldNavigate: false };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/ui/serve-behaviors/server.ts
|
|
160
|
+
import { createServer } from "node:http";
|
|
161
|
+
import { randomBytes } from "node:crypto";
|
|
162
|
+
async function serveHtmlInMemory(html, options) {
|
|
163
|
+
const timeout = options?.timeout ?? 5 * 60 * 1000;
|
|
164
|
+
const openBrowser = options?.openBrowser ?? true;
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
const server = createServer((req, res) => {
|
|
167
|
+
const nonce = randomBytes(16).toString("base64");
|
|
168
|
+
const csp = [
|
|
169
|
+
"default-src 'self' data: https:;",
|
|
170
|
+
`script-src 'self' 'nonce-${nonce}';`,
|
|
171
|
+
"style-src 'self' 'unsafe-inline';",
|
|
172
|
+
"object-src 'none';",
|
|
173
|
+
"base-uri 'self';"
|
|
174
|
+
].join(" ");
|
|
175
|
+
res.writeHead(200, {
|
|
176
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
177
|
+
"Content-Security-Policy": csp,
|
|
178
|
+
"X-Content-Type-Options": "nosniff",
|
|
179
|
+
"Referrer-Policy": "no-referrer"
|
|
180
|
+
});
|
|
181
|
+
const htmlWithNonces = html.replace(/<script(\b[^>]*)>/gi, `<script$1 nonce="${nonce}">`);
|
|
182
|
+
res.end(htmlWithNonces);
|
|
183
|
+
});
|
|
184
|
+
server.listen(0, "127.0.0.1", () => {
|
|
185
|
+
const address = server.address();
|
|
186
|
+
if (!address || typeof address === "string") {
|
|
187
|
+
reject(new Error("Failed to get server address"));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const url = `http://127.0.0.1:${address.port}`;
|
|
191
|
+
const timer = setTimeout(() => server.close(), timeout);
|
|
192
|
+
const stop = () => {
|
|
193
|
+
clearTimeout(timer);
|
|
194
|
+
server.close();
|
|
195
|
+
};
|
|
196
|
+
if (openBrowser) {
|
|
197
|
+
openUrl(url);
|
|
198
|
+
}
|
|
199
|
+
resolve({ url, stop });
|
|
200
|
+
});
|
|
201
|
+
server.on("error", reject);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/ui/serve-behaviors/handlers.ts
|
|
206
|
+
var deps = {
|
|
207
|
+
serveHtmlInMemory
|
|
208
|
+
};
|
|
209
|
+
var htmlCodeServeHandler = {
|
|
210
|
+
async serve(ctx) {
|
|
211
|
+
try {
|
|
212
|
+
let url;
|
|
213
|
+
if (typeof ctx.value === "string") {
|
|
214
|
+
url = ctx.value;
|
|
215
|
+
} else if (typeof ctx.value === "object" && ctx.value?.downloadUrl) {
|
|
216
|
+
url = ctx.value.downloadUrl;
|
|
217
|
+
} else {
|
|
218
|
+
return { success: false, message: "No download URL found" };
|
|
219
|
+
}
|
|
220
|
+
ctx.onProgress?.("\uD83D\uDCE5 Downloading HTML...");
|
|
221
|
+
const response = await fetch(url);
|
|
222
|
+
if (!response.ok) {
|
|
223
|
+
return { success: false, message: `Download failed: ${response.status} ${response.statusText}` };
|
|
224
|
+
}
|
|
225
|
+
const html = await response.text();
|
|
226
|
+
ctx.onProgress?.("\uD83D\uDE80 Starting local server...");
|
|
227
|
+
const { url: serveUrl } = await deps.serveHtmlInMemory(html);
|
|
228
|
+
ctx.onProgress?.("\uD83C\uDF10 Opening browser...");
|
|
229
|
+
return { success: true, message: `\uD83C\uDF10 Preview at ${serveUrl} (auto-closes in 5 min)`, url: serveUrl };
|
|
230
|
+
} catch (error) {
|
|
231
|
+
return { success: false, message: `Serve failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// src/ui/serve-behaviors/registry.ts
|
|
237
|
+
var registrations2 = [];
|
|
238
|
+
function registerServeHandler(matcher, handler) {
|
|
239
|
+
registrations2.push({ matcher, handler });
|
|
240
|
+
}
|
|
241
|
+
function getServeHandler(path) {
|
|
242
|
+
for (let i = registrations2.length - 1;i >= 0; i--) {
|
|
243
|
+
const reg = registrations2[i];
|
|
244
|
+
if (reg && reg.matcher(path))
|
|
245
|
+
return reg.handler;
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
function endsWith2(suffix) {
|
|
250
|
+
return (path) => path.endsWith(suffix);
|
|
251
|
+
}
|
|
252
|
+
registerServeHandler(endsWith2(".htmlCode"), htmlCodeServeHandler);
|
|
253
|
+
registerServeHandler(endsWith2(".htmlCode.downloadUrl"), htmlCodeServeHandler);
|
|
254
|
+
|
|
255
|
+
// src/ui/JsonTree.tsx
|
|
256
|
+
var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
|
|
257
|
+
function getType(value) {
|
|
258
|
+
if (value === null)
|
|
259
|
+
return "null";
|
|
260
|
+
if (Array.isArray(value))
|
|
261
|
+
return "array";
|
|
262
|
+
return typeof value;
|
|
263
|
+
}
|
|
264
|
+
function buildVisibleTree(data, expandedIds, prefix = "", depth = 0, rootLabel) {
|
|
265
|
+
const nodes = [];
|
|
266
|
+
const type = getType(data);
|
|
267
|
+
if (rootLabel && prefix === "" && depth === 0) {
|
|
268
|
+
const isExpanded = expandedIds.has(rootLabel);
|
|
269
|
+
nodes.push({
|
|
270
|
+
id: rootLabel,
|
|
271
|
+
key: rootLabel,
|
|
272
|
+
value: data,
|
|
273
|
+
depth: 0,
|
|
274
|
+
isLeaf: false,
|
|
275
|
+
isExpanded,
|
|
276
|
+
hasChildren: true
|
|
277
|
+
});
|
|
278
|
+
if (isExpanded) {
|
|
279
|
+
nodes.push(...buildVisibleTree(data, expandedIds, rootLabel, 1));
|
|
280
|
+
}
|
|
281
|
+
return nodes;
|
|
282
|
+
}
|
|
283
|
+
if (type === "object" || type === "array") {
|
|
284
|
+
const keys = Object.keys(data);
|
|
285
|
+
for (const key of keys) {
|
|
286
|
+
const value = data[key];
|
|
287
|
+
const id = prefix ? `${prefix}.${key}` : key;
|
|
288
|
+
const valueType = getType(value);
|
|
289
|
+
const isLeaf = valueType !== "object" && valueType !== "array";
|
|
290
|
+
const hasChildren = !isLeaf && Object.keys(value).length > 0;
|
|
291
|
+
const isExpanded = expandedIds.has(id);
|
|
292
|
+
nodes.push({
|
|
293
|
+
id,
|
|
294
|
+
key,
|
|
295
|
+
value,
|
|
296
|
+
depth,
|
|
297
|
+
isLeaf,
|
|
298
|
+
isExpanded,
|
|
299
|
+
hasChildren
|
|
300
|
+
});
|
|
301
|
+
if (hasChildren && isExpanded) {
|
|
302
|
+
nodes.push(...buildVisibleTree(value, expandedIds, id, depth + 1));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return nodes;
|
|
307
|
+
}
|
|
308
|
+
var JsonTree = ({ data, rootLabel, onNavigate, onBack }) => {
|
|
309
|
+
const [expandedIds, setExpandedIds] = import_react.useState(() => {
|
|
310
|
+
const ids = new Set;
|
|
311
|
+
if (rootLabel) {
|
|
312
|
+
ids.add(rootLabel);
|
|
313
|
+
} else if (data && typeof data === "object") {
|
|
314
|
+
Object.keys(data).forEach((key) => ids.add(key));
|
|
315
|
+
}
|
|
316
|
+
return ids;
|
|
317
|
+
});
|
|
318
|
+
const [selectedIndex, setSelectedIndex] = import_react.useState(0);
|
|
319
|
+
const [feedbackMessage, setFeedbackMessage] = import_react.useState(null);
|
|
320
|
+
const lastCPressTime = import_react.useRef(0);
|
|
321
|
+
const feedbackTimeout = import_react.useRef(null);
|
|
322
|
+
const visibleNodes = import_react.useMemo(() => {
|
|
323
|
+
return buildVisibleTree(data, expandedIds, "", 0, rootLabel);
|
|
324
|
+
}, [data, expandedIds, rootLabel]);
|
|
325
|
+
const { exit } = use_app_default();
|
|
326
|
+
use_input_default((input, key) => {
|
|
327
|
+
if (input === "q") {
|
|
328
|
+
exit();
|
|
329
|
+
}
|
|
330
|
+
if (input === "c") {
|
|
331
|
+
const node = visibleNodes[selectedIndex];
|
|
332
|
+
if (!node)
|
|
333
|
+
return;
|
|
334
|
+
const now = Date.now();
|
|
335
|
+
const timeSinceLastC = now - lastCPressTime.current;
|
|
336
|
+
lastCPressTime.current = now;
|
|
337
|
+
const isDoubleTap = timeSinceLastC < 300;
|
|
338
|
+
const handler = getHandler(node.id);
|
|
339
|
+
const onProgress = (message) => {
|
|
340
|
+
if (feedbackTimeout.current)
|
|
341
|
+
clearTimeout(feedbackTimeout.current);
|
|
342
|
+
setFeedbackMessage(message);
|
|
343
|
+
};
|
|
344
|
+
const ctx = { key: node.key, value: node.value, path: node.id, onProgress };
|
|
345
|
+
const showFeedback = (result) => {
|
|
346
|
+
if (feedbackTimeout.current)
|
|
347
|
+
clearTimeout(feedbackTimeout.current);
|
|
348
|
+
setFeedbackMessage(result.message);
|
|
349
|
+
feedbackTimeout.current = setTimeout(() => setFeedbackMessage(null), 3000);
|
|
350
|
+
};
|
|
351
|
+
if (isDoubleTap) {
|
|
352
|
+
handler.copyExtended(ctx).then(showFeedback);
|
|
353
|
+
} else {
|
|
354
|
+
setTimeout(() => {
|
|
355
|
+
if (Date.now() - lastCPressTime.current >= 280) {
|
|
356
|
+
handler.copy(ctx).then(showFeedback);
|
|
357
|
+
}
|
|
358
|
+
}, 300);
|
|
359
|
+
}
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (input === "s") {
|
|
363
|
+
const node = visibleNodes[selectedIndex];
|
|
364
|
+
if (!node)
|
|
365
|
+
return;
|
|
366
|
+
const handler = getServeHandler(node.id);
|
|
367
|
+
if (!handler) {
|
|
368
|
+
if (feedbackTimeout.current)
|
|
369
|
+
clearTimeout(feedbackTimeout.current);
|
|
370
|
+
setFeedbackMessage("⚠️ No preview available for this path");
|
|
371
|
+
feedbackTimeout.current = setTimeout(() => setFeedbackMessage(null), 3000);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const onProgress = (message) => {
|
|
375
|
+
if (feedbackTimeout.current)
|
|
376
|
+
clearTimeout(feedbackTimeout.current);
|
|
377
|
+
setFeedbackMessage(message);
|
|
378
|
+
};
|
|
379
|
+
const ctx = { key: node.key, value: node.value, path: node.id, onProgress };
|
|
380
|
+
handler.serve(ctx).then((result) => {
|
|
381
|
+
if (feedbackTimeout.current)
|
|
382
|
+
clearTimeout(feedbackTimeout.current);
|
|
383
|
+
setFeedbackMessage(result.message);
|
|
384
|
+
feedbackTimeout.current = setTimeout(() => setFeedbackMessage(null), 1e4);
|
|
385
|
+
});
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (input === "o") {
|
|
389
|
+
const node = visibleNodes[selectedIndex];
|
|
390
|
+
if (!node)
|
|
391
|
+
return;
|
|
392
|
+
let projectId;
|
|
393
|
+
const projectsMatch = node.id.match(/projects\.(\d+)/);
|
|
394
|
+
if (projectsMatch && projectsMatch[1]) {
|
|
395
|
+
const projectIndex = parseInt(projectsMatch[1], 10);
|
|
396
|
+
const project = data.projects?.[projectIndex];
|
|
397
|
+
if (project?.name) {
|
|
398
|
+
projectId = project.name.replace("projects/", "");
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (!projectId && typeof node.value === "object" && node.value?.name) {
|
|
402
|
+
const nameMatch = node.value.name.match(/projects\/(\d+)/);
|
|
403
|
+
if (nameMatch) {
|
|
404
|
+
projectId = nameMatch[1];
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (!projectId && node.key === "name" && typeof node.value === "string") {
|
|
408
|
+
const nameMatch = node.value.match(/projects\/(\d+)/);
|
|
409
|
+
if (nameMatch) {
|
|
410
|
+
projectId = nameMatch[1];
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (!projectId && (rootLabel === "screen" || rootLabel === "resource")) {
|
|
414
|
+
const nameMatch = data.name?.match(/projects\/(\d+)/);
|
|
415
|
+
if (nameMatch) {
|
|
416
|
+
projectId = nameMatch[1];
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (projectId) {
|
|
420
|
+
const url = `https://stitch.withgoogle.com/projects/${projectId}`;
|
|
421
|
+
openUrl(url);
|
|
422
|
+
if (feedbackTimeout.current)
|
|
423
|
+
clearTimeout(feedbackTimeout.current);
|
|
424
|
+
setFeedbackMessage(`\uD83D\uDD17 Opened project in browser`);
|
|
425
|
+
feedbackTimeout.current = setTimeout(() => setFeedbackMessage(null), 3000);
|
|
426
|
+
} else {
|
|
427
|
+
if (feedbackTimeout.current)
|
|
428
|
+
clearTimeout(feedbackTimeout.current);
|
|
429
|
+
setFeedbackMessage(`⚠️ No project found at this path`);
|
|
430
|
+
feedbackTimeout.current = setTimeout(() => setFeedbackMessage(null), 3000);
|
|
431
|
+
}
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (key.upArrow) {
|
|
435
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
436
|
+
}
|
|
437
|
+
if (key.downArrow) {
|
|
438
|
+
setSelectedIndex(Math.min(visibleNodes.length - 1, selectedIndex + 1));
|
|
439
|
+
}
|
|
440
|
+
if (key.rightArrow || key.return) {
|
|
441
|
+
const node = visibleNodes[selectedIndex];
|
|
442
|
+
if (node && node.hasChildren) {
|
|
443
|
+
if (!node.isExpanded) {
|
|
444
|
+
const newExpanded = new Set(expandedIds);
|
|
445
|
+
newExpanded.add(node.id);
|
|
446
|
+
setExpandedIds(newExpanded);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (key.return && node && onNavigate) {
|
|
450
|
+
const navResult = getNavigationTarget({ path: node.id, value: node.value, key: node.key });
|
|
451
|
+
if (navResult.shouldNavigate) {
|
|
452
|
+
onNavigate(navResult);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if ((key.delete || key.backspace) && onBack) {
|
|
458
|
+
onBack();
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (key.leftArrow) {
|
|
462
|
+
const node = visibleNodes[selectedIndex];
|
|
463
|
+
if (node) {
|
|
464
|
+
if (node.isExpanded) {
|
|
465
|
+
const newExpanded = new Set(expandedIds);
|
|
466
|
+
newExpanded.delete(node.id);
|
|
467
|
+
setExpandedIds(newExpanded);
|
|
468
|
+
} else {
|
|
469
|
+
const lastDot = node.id.lastIndexOf(".");
|
|
470
|
+
if (lastDot !== -1) {
|
|
471
|
+
const parentId = node.id.substring(0, lastDot);
|
|
472
|
+
const parentIndex = visibleNodes.findIndex((n) => n.id === parentId);
|
|
473
|
+
if (parentIndex !== -1) {
|
|
474
|
+
setSelectedIndex(parentIndex);
|
|
475
|
+
}
|
|
476
|
+
} else {}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
const viewportHeight = 20;
|
|
482
|
+
const startRow = Math.max(0, Math.min(selectedIndex - 2, visibleNodes.length - viewportHeight));
|
|
483
|
+
const endRow = Math.min(startRow + viewportHeight, visibleNodes.length);
|
|
484
|
+
const viewportNodes = visibleNodes.slice(startRow, endRow);
|
|
485
|
+
if (!data || typeof data !== "object") {
|
|
486
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
487
|
+
children: [
|
|
488
|
+
"Invalid data: ",
|
|
489
|
+
String(data)
|
|
490
|
+
]
|
|
491
|
+
}, undefined, true, undefined, this);
|
|
492
|
+
}
|
|
493
|
+
if (Object.keys(data).length === 0) {
|
|
494
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
495
|
+
children: "Empty object"
|
|
496
|
+
}, undefined, false, undefined, this);
|
|
497
|
+
}
|
|
498
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
499
|
+
flexDirection: "column",
|
|
500
|
+
children: [
|
|
501
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
502
|
+
color: "blue",
|
|
503
|
+
bold: true,
|
|
504
|
+
children: "JSON Viewer (Use Arrows to Navigate, 'q' to Quit)"
|
|
505
|
+
}, undefined, false, undefined, this),
|
|
506
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
507
|
+
flexDirection: "column",
|
|
508
|
+
borderStyle: "single",
|
|
509
|
+
children: [
|
|
510
|
+
viewportNodes.map((node, index) => {
|
|
511
|
+
const absoluteIndex = startRow + index;
|
|
512
|
+
const isSelected = absoluteIndex === selectedIndex;
|
|
513
|
+
const indentation = " ".repeat(node.depth);
|
|
514
|
+
let prefixChar = " ";
|
|
515
|
+
if (node.hasChildren) {
|
|
516
|
+
prefixChar = node.isExpanded ? "▼" : "▶";
|
|
517
|
+
}
|
|
518
|
+
let valueDisplay = "";
|
|
519
|
+
if (node.isLeaf) {
|
|
520
|
+
const valType = getType(node.value);
|
|
521
|
+
if (valType === "string")
|
|
522
|
+
valueDisplay = `"${node.value}"`;
|
|
523
|
+
else
|
|
524
|
+
valueDisplay = String(node.value);
|
|
525
|
+
} else {
|
|
526
|
+
const type = Array.isArray(node.value) ? "[]" : "{}";
|
|
527
|
+
const itemCount = Object.keys(node.value).length;
|
|
528
|
+
const label = node.value.title || node.value.name || node.value.displayName || node.value.id || null;
|
|
529
|
+
if (label && typeof label === "string") {
|
|
530
|
+
valueDisplay = `${type} "${label}" (${itemCount})`;
|
|
531
|
+
} else {
|
|
532
|
+
valueDisplay = `${type} ${itemCount} items`;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
536
|
+
children: /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
537
|
+
backgroundColor: isSelected ? "blue" : undefined,
|
|
538
|
+
color: isSelected ? "white" : undefined,
|
|
539
|
+
wrap: "truncate",
|
|
540
|
+
children: [
|
|
541
|
+
indentation,
|
|
542
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
543
|
+
color: "green",
|
|
544
|
+
children: [
|
|
545
|
+
prefixChar,
|
|
546
|
+
" ",
|
|
547
|
+
node.key
|
|
548
|
+
]
|
|
549
|
+
}, undefined, true, undefined, this),
|
|
550
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
551
|
+
children: ": "
|
|
552
|
+
}, undefined, false, undefined, this),
|
|
553
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
554
|
+
color: "yellow",
|
|
555
|
+
children: valueDisplay
|
|
556
|
+
}, undefined, false, undefined, this)
|
|
557
|
+
]
|
|
558
|
+
}, undefined, true, undefined, this)
|
|
559
|
+
}, node.id, false, undefined, this);
|
|
560
|
+
}),
|
|
561
|
+
visibleNodes.length > viewportHeight && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
562
|
+
color: "gray",
|
|
563
|
+
children: [
|
|
564
|
+
"... ",
|
|
565
|
+
visibleNodes.length - endRow,
|
|
566
|
+
" more items ..."
|
|
567
|
+
]
|
|
568
|
+
}, undefined, true, undefined, this)
|
|
569
|
+
]
|
|
570
|
+
}, undefined, true, undefined, this),
|
|
571
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
572
|
+
color: "gray",
|
|
573
|
+
children: [
|
|
574
|
+
"Selected Path: ",
|
|
575
|
+
visibleNodes[selectedIndex]?.id || "none",
|
|
576
|
+
" | 'c' copy, 'cc' extended, 's' preview"
|
|
577
|
+
]
|
|
578
|
+
}, undefined, true, undefined, this),
|
|
579
|
+
feedbackMessage && /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
580
|
+
color: "cyan",
|
|
581
|
+
bold: true,
|
|
582
|
+
children: feedbackMessage
|
|
583
|
+
}, undefined, false, undefined, this)
|
|
584
|
+
]
|
|
585
|
+
}, undefined, true, undefined, this);
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
// src/ui/InteractiveViewer.tsx
|
|
589
|
+
var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
|
|
590
|
+
var InteractiveViewer = ({
|
|
591
|
+
initialData,
|
|
592
|
+
initialRootLabel,
|
|
593
|
+
initialHistory,
|
|
594
|
+
onFetch,
|
|
595
|
+
onExit
|
|
596
|
+
}) => {
|
|
597
|
+
const [history, setHistory] = import_react2.useState(() => {
|
|
598
|
+
if (initialHistory && initialHistory.length > 0) {
|
|
599
|
+
return [...initialHistory, { data: initialData, rootLabel: initialRootLabel }];
|
|
600
|
+
}
|
|
601
|
+
return [{ data: initialData, rootLabel: initialRootLabel }];
|
|
602
|
+
});
|
|
603
|
+
const [isLoading, setIsLoading] = import_react2.useState(false);
|
|
604
|
+
const [error, setError] = import_react2.useState(null);
|
|
605
|
+
const currentState = history[history.length - 1];
|
|
606
|
+
const handleNavigate = import_react2.useCallback(async (result) => {
|
|
607
|
+
if (!result.target)
|
|
608
|
+
return;
|
|
609
|
+
setIsLoading(true);
|
|
610
|
+
setError(null);
|
|
611
|
+
try {
|
|
612
|
+
const data = await onFetch(result.target);
|
|
613
|
+
let rootLabel;
|
|
614
|
+
if (result.type === "screen") {
|
|
615
|
+
rootLabel = "screen";
|
|
616
|
+
} else if (result.type === "project") {
|
|
617
|
+
rootLabel = "project";
|
|
618
|
+
} else {
|
|
619
|
+
rootLabel = "resource";
|
|
620
|
+
}
|
|
621
|
+
setHistory((prev) => [...prev, { data, rootLabel, resourcePath: result.target }]);
|
|
622
|
+
} catch (err) {
|
|
623
|
+
setError(`Navigation failed: ${err}`);
|
|
624
|
+
} finally {
|
|
625
|
+
setIsLoading(false);
|
|
626
|
+
}
|
|
627
|
+
}, [onFetch]);
|
|
628
|
+
const handleBack = import_react2.useCallback(() => {
|
|
629
|
+
if (history.length > 1) {
|
|
630
|
+
setHistory((prev) => prev.slice(0, -1));
|
|
631
|
+
setError(null);
|
|
632
|
+
}
|
|
633
|
+
}, [history.length]);
|
|
634
|
+
if (isLoading) {
|
|
635
|
+
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
|
|
636
|
+
flexDirection: "column",
|
|
637
|
+
children: /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
638
|
+
color: "blue",
|
|
639
|
+
children: "Loading..."
|
|
640
|
+
}, undefined, false, undefined, this)
|
|
641
|
+
}, undefined, false, undefined, this);
|
|
642
|
+
}
|
|
643
|
+
if (!currentState) {
|
|
644
|
+
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
645
|
+
color: "red",
|
|
646
|
+
children: "No data to display"
|
|
647
|
+
}, undefined, false, undefined, this);
|
|
648
|
+
}
|
|
649
|
+
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
|
|
650
|
+
flexDirection: "column",
|
|
651
|
+
children: [
|
|
652
|
+
history.length > 1 && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
653
|
+
color: "gray",
|
|
654
|
+
dimColor: true,
|
|
655
|
+
children: [
|
|
656
|
+
"← Press Backspace to go back (",
|
|
657
|
+
history.length - 1,
|
|
658
|
+
" level",
|
|
659
|
+
history.length > 2 ? "s" : "",
|
|
660
|
+
" deep)"
|
|
661
|
+
]
|
|
662
|
+
}, undefined, true, undefined, this),
|
|
663
|
+
error && /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
664
|
+
color: "red",
|
|
665
|
+
children: error
|
|
666
|
+
}, undefined, false, undefined, this),
|
|
667
|
+
/* @__PURE__ */ jsx_dev_runtime2.jsxDEV(JsonTree, {
|
|
668
|
+
data: currentState.data,
|
|
669
|
+
rootLabel: currentState.rootLabel,
|
|
670
|
+
onNavigate: handleNavigate,
|
|
671
|
+
onBack: history.length > 1 ? handleBack : undefined
|
|
672
|
+
}, history.length, false, undefined, this)
|
|
673
|
+
]
|
|
674
|
+
}, undefined, true, undefined, this);
|
|
675
|
+
};
|
|
676
|
+
export {
|
|
677
|
+
InteractiveViewer
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
//# debugId=5616FED4529C741864756E2164756E21
|