@opentrace/components 0.1.1-rc.30 → 0.1.1-rc.44

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.
@@ -0,0 +1,989 @@
1
+ "use strict";
2
+ const jsxRuntime = require("react/jsx-runtime");
3
+ const t = require("react");
4
+ function detectProvider(url) {
5
+ const lower = url.toLowerCase();
6
+ if (lower.includes("github")) return "github";
7
+ if (lower.includes("gitlab")) return "gitlab";
8
+ if (lower.includes("bitbucket")) return "bitbucket";
9
+ if (lower.includes("dev.azure.com") || lower.includes("visualstudio.com"))
10
+ return "azuredevops";
11
+ return null;
12
+ }
13
+ const HISTORY_KEY = "ot_repo_history";
14
+ const MAX_HISTORY = 5;
15
+ function loadHistory() {
16
+ try {
17
+ const raw = localStorage.getItem(HISTORY_KEY);
18
+ if (!raw) return [];
19
+ const parsed = JSON.parse(raw);
20
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
21
+ } catch {
22
+ return [];
23
+ }
24
+ }
25
+ function saveToHistory(url) {
26
+ const history = loadHistory().filter((u) => u !== url);
27
+ history.unshift(url);
28
+ localStorage.setItem(
29
+ HISTORY_KEY,
30
+ JSON.stringify(history.slice(0, MAX_HISTORY))
31
+ );
32
+ }
33
+ function removeFromHistory(url) {
34
+ const updated = loadHistory().filter((u) => u !== url);
35
+ localStorage.setItem(HISTORY_KEY, JSON.stringify(updated));
36
+ return updated;
37
+ }
38
+ const PROVIDER_ORDER = [
39
+ "github",
40
+ "gitlab",
41
+ "bitbucket",
42
+ "azuredevops"
43
+ ];
44
+ const QUADRANT_CLIPS = {
45
+ github: {
46
+ // top triangle
47
+ quarter: "polygon(0% 0%, 100% 0%, 50% 50%, 50% 50%)",
48
+ full: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)",
49
+ hidden: "polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%)"
50
+ },
51
+ gitlab: {
52
+ // right triangle
53
+ quarter: "polygon(50% 50%, 100% 0%, 100% 100%, 50% 50%)",
54
+ full: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)",
55
+ hidden: "polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%)"
56
+ },
57
+ bitbucket: {
58
+ // bottom triangle
59
+ quarter: "polygon(50% 50%, 50% 50%, 100% 100%, 0% 100%)",
60
+ full: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)",
61
+ hidden: "polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%)"
62
+ },
63
+ azuredevops: {
64
+ // left triangle
65
+ quarter: "polygon(0% 0%, 50% 50%, 50% 50%, 0% 100%)",
66
+ full: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)",
67
+ hidden: "polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%)"
68
+ }
69
+ };
70
+ const PROVIDER_FULL_SVG = {
71
+ github: () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "100%", height: "100%", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" }) }),
72
+ gitlab: () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "100%", height: "100%", viewBox: "0 0 380 380", fill: "currentColor", children: [
73
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M190 353.9L131.1 172.8h117.8L190 353.9z", opacity: "0.85" }),
74
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M190 353.9L131.1 172.8H15.6L190 353.9z", opacity: "0.7" }),
75
+ /* @__PURE__ */ jsxRuntime.jsx(
76
+ "path",
77
+ {
78
+ d: "M15.6 172.8L0.4 219.5c-1.4 4.3 0.1 9 3.8 11.7L190 353.9 15.6 172.8z",
79
+ opacity: "0.55"
80
+ }
81
+ ),
82
+ /* @__PURE__ */ jsxRuntime.jsx(
83
+ "path",
84
+ {
85
+ d: "M15.6 172.8h115.5L87.6 26.5c-1.6-4.9-8.5-4.9-10.1 0L15.6 172.8z",
86
+ opacity: "0.85"
87
+ }
88
+ ),
89
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M190 353.9l58.9-181.1h115.5L190 353.9z", opacity: "0.7" }),
90
+ /* @__PURE__ */ jsxRuntime.jsx(
91
+ "path",
92
+ {
93
+ d: "M364.4 172.8l15.2 46.7c1.4 4.3-0.1 9-3.8 11.7L190 353.9l174.4-181.1z",
94
+ opacity: "0.55"
95
+ }
96
+ ),
97
+ /* @__PURE__ */ jsxRuntime.jsx(
98
+ "path",
99
+ {
100
+ d: "M364.4 172.8H248.9l43.5-146.3c1.6-4.9 8.5-4.9 10.1 0l61.9 146.3z",
101
+ opacity: "0.85"
102
+ }
103
+ )
104
+ ] }),
105
+ bitbucket: () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "100%", height: "100%", viewBox: "0 0 32 32", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2.278 2.133a1.07 1.07 0 00-1.07 1.236l4.058 24.637a1.45 1.45 0 001.417 1.195h19.1a1.07 1.07 0 001.07-.903l4.058-24.93a1.07 1.07 0 00-1.07-1.236zm16.7 17.757h-6.1l-1.647-8.613h9.2z" }) }),
106
+ azuredevops: () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "100%", height: "100%", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 3.622v8.512L11.5 15l-5.425-1.975v1.958L3.004 10.97l8.951.7V4.005L15 3.622zm-2.984.428L6.994 1v2.001L2.382 4.356 1 6.13v4.029l1.978.873V5.869l9.038-1.819z" }) })
107
+ };
108
+ function ProviderIconStrip({ selected }) {
109
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "hero-icon" + (selected ? " hero-icon--provider" : ""), children: [
110
+ /* @__PURE__ */ jsxRuntime.jsxs(
111
+ "svg",
112
+ {
113
+ className: `quad-dividers${selected ? " quad-dividers--hidden" : ""}`,
114
+ width: "100%",
115
+ height: "100%",
116
+ viewBox: "0 0 64 64",
117
+ children: [
118
+ /* @__PURE__ */ jsxRuntime.jsx(
119
+ "line",
120
+ {
121
+ x1: "0",
122
+ y1: "0",
123
+ x2: "64",
124
+ y2: "64",
125
+ stroke: "currentColor",
126
+ strokeWidth: "1.5",
127
+ opacity: "0.2"
128
+ }
129
+ ),
130
+ /* @__PURE__ */ jsxRuntime.jsx(
131
+ "line",
132
+ {
133
+ x1: "64",
134
+ y1: "0",
135
+ x2: "0",
136
+ y2: "64",
137
+ stroke: "currentColor",
138
+ strokeWidth: "1.5",
139
+ opacity: "0.2"
140
+ }
141
+ )
142
+ ]
143
+ }
144
+ ),
145
+ PROVIDER_ORDER.map((key) => {
146
+ const Icon = PROVIDER_FULL_SVG[key];
147
+ const clips = QUADRANT_CLIPS[key];
148
+ const clip = !selected ? clips.quarter : key === selected ? clips.full : clips.hidden;
149
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "quad-layer", style: { clipPath: clip }, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) }, key);
150
+ })
151
+ ] });
152
+ }
153
+ function FolderIcon() {
154
+ return /* @__PURE__ */ jsxRuntime.jsx(
155
+ "svg",
156
+ {
157
+ width: "38",
158
+ height: "38",
159
+ viewBox: "0 0 24 24",
160
+ fill: "none",
161
+ stroke: "currentColor",
162
+ strokeWidth: "1.5",
163
+ strokeLinecap: "round",
164
+ strokeLinejoin: "round",
165
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" })
166
+ }
167
+ );
168
+ }
169
+ const PROVIDER_DISPLAY_NAME = {
170
+ github: "GitHub",
171
+ gitlab: "GitLab",
172
+ bitbucket: "Bitbucket",
173
+ azuredevops: "Azure DevOps"
174
+ };
175
+ const EXAMPLE_REPOS = [
176
+ {
177
+ name: "OpenTrace",
178
+ url: "https://github.com/opentrace/opentrace",
179
+ description: "Knowledge graph for system architecture and code structure",
180
+ size: "M"
181
+ },
182
+ {
183
+ name: "OpenTelemetry Demo",
184
+ url: "https://github.com/open-telemetry/opentelemetry-demo",
185
+ description: "Microservices demo with OTel instrumentation",
186
+ size: "M"
187
+ },
188
+ {
189
+ name: "Express.js",
190
+ url: "https://github.com/expressjs/express",
191
+ description: "Fast, minimalist web framework for Node.js",
192
+ size: "S"
193
+ },
194
+ {
195
+ name: "Podinfo",
196
+ url: "https://github.com/stefanprodan/podinfo",
197
+ description: "Go microservice template for Kubernetes",
198
+ size: "S"
199
+ },
200
+ {
201
+ name: "Grafana",
202
+ url: "https://github.com/grafana/grafana",
203
+ description: "Open-source observability and monitoring platform",
204
+ size: "L"
205
+ },
206
+ {
207
+ name: "Linux",
208
+ url: "https://github.com/torvalds/linux",
209
+ description: "Linux kernel source tree",
210
+ size: "L"
211
+ }
212
+ ];
213
+ function AddRepoModal({
214
+ onClose,
215
+ onSubmit,
216
+ dismissable = true,
217
+ onValidate
218
+ }) {
219
+ const [source, setSource] = t.useState("url");
220
+ const [repoUrl, setRepoUrl] = t.useState("");
221
+ const [history, setHistory] = t.useState(loadHistory);
222
+ const [showHistory, setShowHistory] = t.useState(false);
223
+ const [ref, setRef] = t.useState("");
224
+ const [pat, setPat] = t.useState("");
225
+ const [showPat, setShowPat] = t.useState(false);
226
+ const [loading, setLoading] = t.useState(false);
227
+ const [error, setError] = t.useState(null);
228
+ const [selectedFiles, setSelectedFiles] = t.useState(null);
229
+ const fileInputRef = t.useRef(null);
230
+ const urlInputRef = t.useRef(null);
231
+ const dropdownRef = t.useRef(null);
232
+ t.useEffect(() => {
233
+ if (source === "url") {
234
+ urlInputRef.current?.focus();
235
+ }
236
+ }, [source]);
237
+ t.useEffect(() => {
238
+ function handleClick(e) {
239
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target) && e.target !== urlInputRef.current) {
240
+ setShowHistory(false);
241
+ }
242
+ }
243
+ document.addEventListener("mousedown", handleClick);
244
+ return () => document.removeEventListener("mousedown", handleClick);
245
+ }, []);
246
+ const filteredHistory = history.filter(
247
+ (url) => !repoUrl || url.toLowerCase().includes(repoUrl.toLowerCase())
248
+ );
249
+ const provider = source === "url" ? detectProvider(repoUrl) : null;
250
+ const patStorageKey = provider ? `ot_${provider}_pat` : null;
251
+ const validationMessage = t.useMemo(() => {
252
+ if (source !== "url" || !repoUrl.trim() || !onValidate) return null;
253
+ return onValidate(repoUrl);
254
+ }, [source, repoUrl, onValidate]);
255
+ const directoryName = selectedFiles?.[0]?.webkitRelativePath?.split("/")[0] ?? "";
256
+ t.useEffect(() => {
257
+ if (!provider || !patStorageKey) return;
258
+ const saved = localStorage.getItem(patStorageKey);
259
+ if (saved) setPat(saved);
260
+ else setPat("");
261
+ }, [provider, patStorageKey]);
262
+ t.useEffect(() => {
263
+ setError(null);
264
+ }, [source]);
265
+ function handleSubmit(e) {
266
+ e.preventDefault();
267
+ if (source === "directory") {
268
+ if (!selectedFiles || selectedFiles.length === 0) {
269
+ setError("Select a directory first.");
270
+ return;
271
+ }
272
+ setError(null);
273
+ setLoading(true);
274
+ onSubmit({
275
+ type: "index-directory",
276
+ files: selectedFiles,
277
+ name: directoryName || "local"
278
+ });
279
+ return;
280
+ }
281
+ if (!provider) {
282
+ setError(
283
+ "Enter a GitHub, GitLab, Bitbucket, or Azure DevOps repository URL."
284
+ );
285
+ return;
286
+ }
287
+ if (validationMessage) {
288
+ return;
289
+ }
290
+ setError(null);
291
+ setLoading(true);
292
+ if (patStorageKey) {
293
+ if (pat) localStorage.setItem(patStorageKey, pat);
294
+ else localStorage.removeItem(patStorageKey);
295
+ }
296
+ saveToHistory(repoUrl);
297
+ onSubmit({
298
+ type: "index-repo",
299
+ repoUrl,
300
+ token: pat || void 0,
301
+ ref: ref || void 0
302
+ });
303
+ }
304
+ const heroIcon = source === "directory" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hero-icon hero-icon--provider", children: /* @__PURE__ */ jsxRuntime.jsx(FolderIcon, {}) }) : /* @__PURE__ */ jsxRuntime.jsx(ProviderIconStrip, { selected: provider });
305
+ const title = source === "directory" ? "Add Local Directory" : provider ? `Add from ${PROVIDER_DISPLAY_NAME[provider]}` : "Add Repository";
306
+ const subtitle = source === "directory" ? "Select a directory to index its structure into the graph" : provider ? `Enter a ${PROVIDER_DISPLAY_NAME[provider]} repository URL to index` : "Enter a repository URL to index its structure into the graph";
307
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "modal-backdrop", onClick: dismissable ? onClose : void 0, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "modal-card", onClick: (e) => e.stopPropagation(), children: [
308
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "source-toggle", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "chip-toggle", children: [
309
+ /* @__PURE__ */ jsxRuntime.jsx(
310
+ "button",
311
+ {
312
+ type: "button",
313
+ className: `chip-toggle-btn${source === "url" ? " active" : ""}`,
314
+ onClick: () => setSource("url"),
315
+ children: "URL"
316
+ }
317
+ ),
318
+ /* @__PURE__ */ jsxRuntime.jsx(
319
+ "button",
320
+ {
321
+ type: "button",
322
+ className: `chip-toggle-btn${source === "directory" ? " active" : ""}`,
323
+ onClick: () => setSource("directory"),
324
+ children: "Directory"
325
+ }
326
+ )
327
+ ] }) }),
328
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "form-hero", children: [
329
+ heroIcon,
330
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: title }),
331
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "hero-subtitle", children: subtitle })
332
+ ] }),
333
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [
334
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "form-fields", children: source === "url" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
335
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "form-info", children: [
336
+ /* @__PURE__ */ jsxRuntime.jsxs(
337
+ "svg",
338
+ {
339
+ width: "14",
340
+ height: "14",
341
+ viewBox: "0 0 24 24",
342
+ fill: "none",
343
+ stroke: "currentColor",
344
+ strokeWidth: "2",
345
+ strokeLinecap: "round",
346
+ strokeLinejoin: "round",
347
+ children: [
348
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
349
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
350
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
351
+ ]
352
+ }
353
+ ),
354
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Repository archives are fetched through the OpenTrace API server to avoid browser CORS restrictions. Your access token (if provided) is forwarded but never stored on the server." })
355
+ ] }),
356
+ !provider && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "example-repos", children: [
357
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "example-repos-label", children: "Examples:" }),
358
+ EXAMPLE_REPOS.map((repo) => /* @__PURE__ */ jsxRuntime.jsxs(
359
+ "button",
360
+ {
361
+ type: "button",
362
+ className: "example-repo-chip",
363
+ onClick: () => setRepoUrl(repo.url),
364
+ title: repo.description,
365
+ children: [
366
+ repo.name,
367
+ /* @__PURE__ */ jsxRuntime.jsx(
368
+ "span",
369
+ {
370
+ className: `example-repo-size example-repo-size--${repo.size.toLowerCase()}`,
371
+ children: repo.size
372
+ }
373
+ )
374
+ ]
375
+ },
376
+ repo.url
377
+ ))
378
+ ] }),
379
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autocomplete-wrapper", children: [
380
+ /* @__PURE__ */ jsxRuntime.jsx(
381
+ "input",
382
+ {
383
+ ref: urlInputRef,
384
+ type: "text",
385
+ required: true,
386
+ className: "input-pill",
387
+ placeholder: "https://github.com/owner/repo or https://bitbucket.org/ws/repo",
388
+ value: repoUrl,
389
+ onChange: (e) => {
390
+ setRepoUrl(e.target.value);
391
+ if (!showHistory) setShowHistory(true);
392
+ },
393
+ onFocus: () => setShowHistory(true),
394
+ onKeyDown: (e) => {
395
+ if (e.key === "Escape") setShowHistory(false);
396
+ },
397
+ autoFocus: true,
398
+ autoComplete: "off",
399
+ "data-1p-ignore": true,
400
+ "data-lpignore": "true",
401
+ "data-testid": "repo-url-input"
402
+ }
403
+ ),
404
+ showHistory && filteredHistory.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: dropdownRef, className: "autocomplete-dropdown", children: [
405
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autocomplete-label", children: "Recent" }),
406
+ filteredHistory.map((url) => /* @__PURE__ */ jsxRuntime.jsxs(
407
+ "div",
408
+ {
409
+ className: "autocomplete-item",
410
+ role: "option",
411
+ onMouseDown: (e) => {
412
+ e.preventDefault();
413
+ setRepoUrl(url);
414
+ setShowHistory(false);
415
+ urlInputRef.current?.focus();
416
+ },
417
+ children: [
418
+ /* @__PURE__ */ jsxRuntime.jsxs(
419
+ "svg",
420
+ {
421
+ width: "14",
422
+ height: "14",
423
+ viewBox: "0 0 24 24",
424
+ fill: "none",
425
+ stroke: "currentColor",
426
+ strokeWidth: "2",
427
+ strokeLinecap: "round",
428
+ strokeLinejoin: "round",
429
+ children: [
430
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "1 4 1 10 7 10" }),
431
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })
432
+ ]
433
+ }
434
+ ),
435
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "autocomplete-item-text", children: url.replace(/^https?:\/\/(www\.)?/, "").replace(/\.git$/, "") }),
436
+ /* @__PURE__ */ jsxRuntime.jsx(
437
+ "button",
438
+ {
439
+ type: "button",
440
+ className: "autocomplete-item-remove",
441
+ onMouseDown: (e) => {
442
+ e.preventDefault();
443
+ e.stopPropagation();
444
+ setHistory(removeFromHistory(url));
445
+ },
446
+ "aria-label": "Remove from history",
447
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
448
+ "svg",
449
+ {
450
+ width: "12",
451
+ height: "12",
452
+ viewBox: "0 0 24 24",
453
+ fill: "none",
454
+ stroke: "currentColor",
455
+ strokeWidth: "2",
456
+ strokeLinecap: "round",
457
+ strokeLinejoin: "round",
458
+ children: [
459
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "3 6 5 6 21 6" }),
460
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" }),
461
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 11v6" }),
462
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 11v6" })
463
+ ]
464
+ }
465
+ )
466
+ }
467
+ )
468
+ ]
469
+ },
470
+ url
471
+ ))
472
+ ] })
473
+ ] }),
474
+ provider && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "input-pill-row", children: [
475
+ /* @__PURE__ */ jsxRuntime.jsxs(
476
+ "svg",
477
+ {
478
+ className: "input-icon",
479
+ width: "16",
480
+ height: "16",
481
+ viewBox: "0 0 24 24",
482
+ fill: "none",
483
+ stroke: "currentColor",
484
+ strokeWidth: "2",
485
+ strokeLinecap: "round",
486
+ strokeLinejoin: "round",
487
+ children: [
488
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
489
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
490
+ ]
491
+ }
492
+ ),
493
+ /* @__PURE__ */ jsxRuntime.jsx(
494
+ "input",
495
+ {
496
+ type: showPat ? "text" : "password",
497
+ className: "input-pill input-pill--icon",
498
+ placeholder: `${provider ? PROVIDER_DISPLAY_NAME[provider] : ""} access token (optional, for private repos)`,
499
+ value: pat,
500
+ onChange: (e) => setPat(e.target.value)
501
+ }
502
+ ),
503
+ /* @__PURE__ */ jsxRuntime.jsx(
504
+ "button",
505
+ {
506
+ type: "button",
507
+ className: "input-toggle",
508
+ onClick: () => setShowPat(!showPat),
509
+ tabIndex: -1,
510
+ "aria-label": showPat ? "Hide token" : "Show token",
511
+ children: showPat ? /* @__PURE__ */ jsxRuntime.jsxs(
512
+ "svg",
513
+ {
514
+ width: "16",
515
+ height: "16",
516
+ viewBox: "0 0 24 24",
517
+ fill: "none",
518
+ stroke: "currentColor",
519
+ strokeWidth: "2",
520
+ strokeLinecap: "round",
521
+ strokeLinejoin: "round",
522
+ children: [
523
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94" }),
524
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19" }),
525
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" }),
526
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14.12 14.12a3 3 0 1 1-4.24-4.24" })
527
+ ]
528
+ }
529
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
530
+ "svg",
531
+ {
532
+ width: "16",
533
+ height: "16",
534
+ viewBox: "0 0 24 24",
535
+ fill: "none",
536
+ stroke: "currentColor",
537
+ strokeWidth: "2",
538
+ strokeLinecap: "round",
539
+ strokeLinejoin: "round",
540
+ children: [
541
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
542
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "3" })
543
+ ]
544
+ }
545
+ )
546
+ }
547
+ )
548
+ ] })
549
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "directory-picker", children: [
550
+ /* @__PURE__ */ jsxRuntime.jsx(
551
+ "input",
552
+ {
553
+ ref: fileInputRef,
554
+ type: "file",
555
+ webkitdirectory: "",
556
+ directory: "",
557
+ multiple: true,
558
+ className: "directory-input",
559
+ onChange: (e) => setSelectedFiles(e.target.files)
560
+ }
561
+ ),
562
+ /* @__PURE__ */ jsxRuntime.jsxs(
563
+ "button",
564
+ {
565
+ type: "button",
566
+ className: "input-pill directory-browse-btn",
567
+ onClick: () => fileInputRef.current?.click(),
568
+ children: [
569
+ /* @__PURE__ */ jsxRuntime.jsx(
570
+ "svg",
571
+ {
572
+ width: "16",
573
+ height: "16",
574
+ viewBox: "0 0 24 24",
575
+ fill: "none",
576
+ stroke: "currentColor",
577
+ strokeWidth: "2",
578
+ strokeLinecap: "round",
579
+ strokeLinejoin: "round",
580
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" })
581
+ }
582
+ ),
583
+ directoryName ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "directory-name", children: [
584
+ directoryName,
585
+ " ",
586
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "directory-count", children: [
587
+ "(",
588
+ selectedFiles?.length ?? 0,
589
+ " files)"
590
+ ] })
591
+ ] }) : "Choose Directory..."
592
+ ]
593
+ }
594
+ )
595
+ ] }) }),
596
+ error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "form-error", children: [
597
+ /* @__PURE__ */ jsxRuntime.jsxs(
598
+ "svg",
599
+ {
600
+ width: "14",
601
+ height: "14",
602
+ viewBox: "0 0 24 24",
603
+ fill: "none",
604
+ stroke: "currentColor",
605
+ strokeWidth: "2",
606
+ strokeLinecap: "round",
607
+ strokeLinejoin: "round",
608
+ children: [
609
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
610
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "15", y1: "9", x2: "9", y2: "15" }),
611
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
612
+ ]
613
+ }
614
+ ),
615
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: error })
616
+ ] }),
617
+ validationMessage && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "form-indexed", children: [
618
+ /* @__PURE__ */ jsxRuntime.jsxs(
619
+ "svg",
620
+ {
621
+ width: "14",
622
+ height: "14",
623
+ viewBox: "0 0 24 24",
624
+ fill: "none",
625
+ stroke: "currentColor",
626
+ strokeWidth: "2",
627
+ strokeLinecap: "round",
628
+ strokeLinejoin: "round",
629
+ children: [
630
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
631
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "22 4 12 14.01 9 11.01" })
632
+ ]
633
+ }
634
+ ),
635
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: validationMessage })
636
+ ] }),
637
+ /* @__PURE__ */ jsxRuntime.jsx(
638
+ "button",
639
+ {
640
+ type: "submit",
641
+ className: "btn-cta",
642
+ disabled: loading || !!validationMessage,
643
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
644
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "btn-spinner" }),
645
+ "Indexing..."
646
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
647
+ "Add & Index",
648
+ /* @__PURE__ */ jsxRuntime.jsxs(
649
+ "svg",
650
+ {
651
+ width: "16",
652
+ height: "16",
653
+ viewBox: "0 0 24 24",
654
+ fill: "none",
655
+ stroke: "currentColor",
656
+ strokeWidth: "2.5",
657
+ strokeLinecap: "round",
658
+ strokeLinejoin: "round",
659
+ children: [
660
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" }),
661
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "12 5 19 12 12 19" })
662
+ ]
663
+ }
664
+ )
665
+ ] })
666
+ }
667
+ ),
668
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "form-chips", children: [
669
+ source === "url" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "chip", children: [
670
+ /* @__PURE__ */ jsxRuntime.jsxs(
671
+ "svg",
672
+ {
673
+ width: "12",
674
+ height: "12",
675
+ viewBox: "0 0 24 24",
676
+ fill: "none",
677
+ stroke: "currentColor",
678
+ strokeWidth: "2",
679
+ strokeLinecap: "round",
680
+ strokeLinejoin: "round",
681
+ children: [
682
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "3", x2: "6", y2: "15" }),
683
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "18", cy: "6", r: "3" }),
684
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "6", cy: "18", r: "3" }),
685
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 9a9 9 0 0 1-9 9" })
686
+ ]
687
+ }
688
+ ),
689
+ /* @__PURE__ */ jsxRuntime.jsx(
690
+ "input",
691
+ {
692
+ type: "text",
693
+ className: "chip-input",
694
+ placeholder: "Branch: main",
695
+ value: ref,
696
+ onChange: (e) => setRef(e.target.value),
697
+ autoComplete: "off",
698
+ "data-1p-ignore": true,
699
+ "data-lpignore": "true"
700
+ }
701
+ )
702
+ ] }),
703
+ dismissable && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "chip", onClick: onClose, children: "Cancel" })
704
+ ] })
705
+ ] })
706
+ ] }) });
707
+ }
708
+ function formatMB(bytes) {
709
+ return (bytes / (1024 * 1024)).toFixed(1) + " MB";
710
+ }
711
+ function StageProgressRow({
712
+ label,
713
+ stage,
714
+ removing
715
+ }) {
716
+ const isCompleted = stage.status === "completed";
717
+ const isActive = stage.status === "active";
718
+ const indeterminate = isActive && stage.total === 0;
719
+ const pct = stage.total > 0 ? Math.min(100, stage.current / stage.total * 100) : 0;
720
+ const isBytes = stage.format === "bytes";
721
+ let cls = "stage-row";
722
+ if (removing) cls += " stage-row--removing";
723
+ else if (isCompleted) cls += " stage-row--completed";
724
+ else if (isActive) cls += " stage-row--active";
725
+ const detail = isCompleted ? stage.message : isActive && stage.message ? stage.message : "";
726
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cls, children: [
727
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "stage-header", children: [
728
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "stage-label", children: label }),
729
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "stage-count", children: stage.total > 0 ? isBytes ? `${formatMB(stage.current)} / ${formatMB(stage.total)}` : `${stage.current}/${stage.total}` : stage.current > 0 ? isBytes ? formatMB(stage.current) : `${stage.current}` : "" })
730
+ ] }),
731
+ detail && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "stage-detail", children: detail }),
732
+ /* @__PURE__ */ jsxRuntime.jsx(
733
+ "span",
734
+ {
735
+ className: `stage-bar${indeterminate ? " stage-bar--indeterminate" : ""}`,
736
+ children: /* @__PURE__ */ jsxRuntime.jsx(
737
+ "span",
738
+ {
739
+ className: "stage-bar-fill",
740
+ style: indeterminate ? void 0 : { width: `${isCompleted ? 100 : pct}%` }
741
+ }
742
+ )
743
+ }
744
+ )
745
+ ] });
746
+ }
747
+ function MultiStageProgress({
748
+ stages,
749
+ stageConfig
750
+ }) {
751
+ const entries = stageConfig.map(({ key, label }) => ({
752
+ key,
753
+ label,
754
+ stage: stages[key]
755
+ })).filter((e) => !!e.stage);
756
+ let lastCompletedIdx = -1;
757
+ for (let i = entries.length - 1; i >= 0; i--) {
758
+ if (entries[i].stage.status === "completed") {
759
+ lastCompletedIdx = i;
760
+ break;
761
+ }
762
+ }
763
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "multi-stage-progress", children: entries.map(({ key, label, stage }, i) => /* @__PURE__ */ jsxRuntime.jsx(
764
+ StageProgressRow,
765
+ {
766
+ label,
767
+ stage,
768
+ removing: stage.status === "completed" && i < lastCompletedIdx
769
+ },
770
+ key
771
+ )) });
772
+ }
773
+ function StatsGrid({
774
+ nodes,
775
+ relationships
776
+ }) {
777
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "indexing-stats-grid", children: [
778
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "stat-card", children: [
779
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "stat-value", children: nodes }),
780
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "stat-label", children: "Nodes" })
781
+ ] }),
782
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "stat-card", children: [
783
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "stat-value", children: relationships }),
784
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "stat-label", children: "Edges" })
785
+ ] })
786
+ ] });
787
+ }
788
+ function IndexingProgress({
789
+ state,
790
+ stages: stageConfig,
791
+ icon,
792
+ onClose,
793
+ onCancel,
794
+ onMinimize,
795
+ title,
796
+ message
797
+ }) {
798
+ if (state.status === "error") {
799
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "modal-backdrop", children: /* @__PURE__ */ jsxRuntime.jsx(
800
+ "div",
801
+ {
802
+ className: "modal-card modal-card-wide",
803
+ onClick: (e) => e.stopPropagation(),
804
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "indexing-progress", children: [
805
+ /* @__PURE__ */ jsxRuntime.jsx(
806
+ MultiStageProgress,
807
+ {
808
+ stages: state.stages,
809
+ stageConfig
810
+ }
811
+ ),
812
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "failed-content", children: [
813
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "failed-icon", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "32", height: "32", viewBox: "0 0 32 32", fill: "none", children: [
814
+ /* @__PURE__ */ jsxRuntime.jsx(
815
+ "circle",
816
+ {
817
+ cx: "16",
818
+ cy: "16",
819
+ r: "14",
820
+ stroke: "currentColor",
821
+ strokeWidth: "2",
822
+ fill: "color-mix(in oklch, currentColor 10%, transparent)"
823
+ }
824
+ ),
825
+ /* @__PURE__ */ jsxRuntime.jsx(
826
+ "line",
827
+ {
828
+ x1: "11",
829
+ y1: "11",
830
+ x2: "21",
831
+ y2: "21",
832
+ stroke: "currentColor",
833
+ strokeWidth: "2.5",
834
+ strokeLinecap: "round"
835
+ }
836
+ ),
837
+ /* @__PURE__ */ jsxRuntime.jsx(
838
+ "line",
839
+ {
840
+ x1: "21",
841
+ y1: "11",
842
+ x2: "11",
843
+ y2: "21",
844
+ stroke: "currentColor",
845
+ strokeWidth: "2.5",
846
+ strokeLinecap: "round"
847
+ }
848
+ )
849
+ ] }) }),
850
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: title ?? "Indexing Failed" }),
851
+ state.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "failed-message", children: state.error })
852
+ ] }),
853
+ (state.nodesCreated > 0 || state.relationshipsCreated > 0) && /* @__PURE__ */ jsxRuntime.jsx(
854
+ StatsGrid,
855
+ {
856
+ nodes: state.nodesCreated,
857
+ relationships: state.relationshipsCreated
858
+ }
859
+ ),
860
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "btn-cta btn-cta--secondary", onClick: onClose, children: "Close" })
861
+ ] })
862
+ }
863
+ ) });
864
+ }
865
+ if (state.status === "done") {
866
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "modal-backdrop", children: /* @__PURE__ */ jsxRuntime.jsx(
867
+ "div",
868
+ {
869
+ className: "modal-card modal-card-wide",
870
+ onClick: (e) => e.stopPropagation(),
871
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "indexing-progress", children: [
872
+ /* @__PURE__ */ jsxRuntime.jsx(
873
+ MultiStageProgress,
874
+ {
875
+ stages: state.stages,
876
+ stageConfig
877
+ }
878
+ ),
879
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "done-content", children: [
880
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "done-checkmark", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "32", height: "32", viewBox: "0 0 32 32", fill: "none", children: [
881
+ /* @__PURE__ */ jsxRuntime.jsx(
882
+ "circle",
883
+ {
884
+ cx: "16",
885
+ cy: "16",
886
+ r: "14",
887
+ stroke: "currentColor",
888
+ strokeWidth: "2",
889
+ fill: "color-mix(in oklch, currentColor 15%, transparent)"
890
+ }
891
+ ),
892
+ /* @__PURE__ */ jsxRuntime.jsx(
893
+ "polyline",
894
+ {
895
+ className: "done-check-path",
896
+ points: "10,16.5 14,20.5 22,12",
897
+ stroke: "currentColor",
898
+ strokeWidth: "2.5",
899
+ strokeLinecap: "round",
900
+ strokeLinejoin: "round",
901
+ fill: "none"
902
+ }
903
+ )
904
+ ] }) }),
905
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: title ?? "Complete" })
906
+ ] }),
907
+ /* @__PURE__ */ jsxRuntime.jsx(
908
+ StatsGrid,
909
+ {
910
+ nodes: state.nodesCreated,
911
+ relationships: state.relationshipsCreated
912
+ }
913
+ ),
914
+ message && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "indexing-message", children: message }),
915
+ /* @__PURE__ */ jsxRuntime.jsx(
916
+ "button",
917
+ {
918
+ className: "btn-cta btn-cta--secondary",
919
+ onClick: onMinimize ?? onClose,
920
+ children: onMinimize ? "Minimize" : "Close"
921
+ }
922
+ )
923
+ ] })
924
+ }
925
+ ) });
926
+ }
927
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "modal-backdrop", children: /* @__PURE__ */ jsxRuntime.jsxs(
928
+ "div",
929
+ {
930
+ className: "modal-card modal-card-wide",
931
+ onClick: (e) => e.stopPropagation(),
932
+ children: [
933
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "indexing-header", children: [
934
+ icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "indexing-header-icon", children: icon }),
935
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: title ?? "Indexing Repository" })
936
+ ] }),
937
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "indexing-progress", children: [
938
+ /* @__PURE__ */ jsxRuntime.jsx(
939
+ MultiStageProgress,
940
+ {
941
+ stages: state.stages,
942
+ stageConfig
943
+ }
944
+ ),
945
+ /* @__PURE__ */ jsxRuntime.jsx(
946
+ StatsGrid,
947
+ {
948
+ nodes: state.nodesCreated,
949
+ relationships: state.relationshipsCreated
950
+ }
951
+ ),
952
+ message && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "indexing-message", children: message }),
953
+ /* @__PURE__ */ jsxRuntime.jsx(
954
+ "button",
955
+ {
956
+ className: "btn-cta btn-cta--secondary",
957
+ onClick: onMinimize ?? onCancel,
958
+ children: onMinimize ? "Minimize" : "Cancel"
959
+ }
960
+ )
961
+ ] })
962
+ ]
963
+ }
964
+ ) });
965
+ }
966
+ function normalizeRepoUrl(raw) {
967
+ const trimmed = raw.trim();
968
+ const scpMatch = trimmed.match(/^[\w.-]+@([^:]+):(.+)$/);
969
+ if (scpMatch) {
970
+ const host = scpMatch[1];
971
+ const path = stripDotGit(scpMatch[2]);
972
+ return `https://${host}/${path}`;
973
+ }
974
+ const sshMatch = trimmed.match(/^ssh:\/\/[\w.-]+@([^/]+)\/(.+)$/);
975
+ if (sshMatch) {
976
+ const host = sshMatch[1];
977
+ const path = stripDotGit(sshMatch[2]);
978
+ return `https://${host}/${path}`;
979
+ }
980
+ return stripDotGit(trimmed);
981
+ }
982
+ function stripDotGit(s) {
983
+ return s.endsWith(".git") ? s.slice(0, -4) : s;
984
+ }
985
+ exports.AddRepoModal = AddRepoModal;
986
+ exports.IndexingProgress = IndexingProgress;
987
+ exports.detectProvider = detectProvider;
988
+ exports.normalizeRepoUrl = normalizeRepoUrl;
989
+ //# sourceMappingURL=urlNormalize-DL0SAEQS.cjs.map