@stigmer/react 0.0.52 → 0.0.54
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/deployment-mode.d.ts +35 -0
- package/deployment-mode.d.ts.map +1 -0
- package/deployment-mode.js +41 -0
- package/deployment-mode.js.map +1 -0
- package/execution/ApprovalCard.d.ts +8 -6
- package/execution/ApprovalCard.d.ts.map +1 -1
- package/execution/ApprovalCard.js +34 -96
- package/execution/ApprovalCard.js.map +1 -1
- package/execution/ArtifactCard.d.ts +11 -1
- package/execution/ArtifactCard.d.ts.map +1 -1
- package/execution/ArtifactCard.js +22 -3
- package/execution/ArtifactCard.js.map +1 -1
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +1 -1
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/ArtifactsWidget.d.ts +26 -19
- package/execution/ArtifactsWidget.d.ts.map +1 -1
- package/execution/ArtifactsWidget.js +24 -26
- package/execution/ArtifactsWidget.js.map +1 -1
- package/execution/McpToolDetail.d.ts +48 -0
- package/execution/McpToolDetail.d.ts.map +1 -0
- package/execution/McpToolDetail.js +159 -0
- package/execution/McpToolDetail.js.map +1 -0
- package/execution/MessageThread.d.ts +10 -1
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +19 -17
- package/execution/MessageThread.js.map +1 -1
- package/execution/SandboxContext.d.ts +32 -0
- package/execution/SandboxContext.d.ts.map +1 -0
- package/execution/SandboxContext.js +26 -0
- package/execution/SandboxContext.js.map +1 -0
- package/execution/ToolArgsView.d.ts +41 -0
- package/execution/ToolArgsView.d.ts.map +1 -0
- package/execution/ToolArgsView.js +134 -0
- package/execution/ToolArgsView.js.map +1 -0
- package/execution/ToolCallDetail.d.ts +11 -4
- package/execution/ToolCallDetail.d.ts.map +1 -1
- package/execution/ToolCallDetail.js +32 -101
- package/execution/ToolCallDetail.js.map +1 -1
- package/execution/ToolCallGroup.d.ts.map +1 -1
- package/execution/ToolCallGroup.js +3 -2
- package/execution/ToolCallGroup.js.map +1 -1
- package/execution/ToolCallItem.d.ts +2 -0
- package/execution/ToolCallItem.d.ts.map +1 -1
- package/execution/ToolCallItem.js +13 -3
- package/execution/ToolCallItem.js.map +1 -1
- package/execution/WriteBackCard.d.ts +34 -0
- package/execution/WriteBackCard.d.ts.map +1 -0
- package/execution/WriteBackCard.js +75 -0
- package/execution/WriteBackCard.js.map +1 -0
- package/execution/WriteBacksWidget.d.ts +49 -0
- package/execution/WriteBacksWidget.d.ts.map +1 -0
- package/execution/WriteBacksWidget.js +44 -0
- package/execution/WriteBacksWidget.js.map +1 -0
- package/execution/__tests__/file-path-resolver.test.d.ts +2 -0
- package/execution/__tests__/file-path-resolver.test.d.ts.map +1 -0
- package/execution/__tests__/file-path-resolver.test.js +180 -0
- package/execution/__tests__/file-path-resolver.test.js.map +1 -0
- package/execution/file-path-resolver.d.ts +3 -3
- package/execution/file-path-resolver.d.ts.map +1 -1
- package/execution/file-path-resolver.js +23 -12
- package/execution/file-path-resolver.js.map +1 -1
- package/execution/index.d.ts +16 -1
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +9 -1
- package/execution/index.js.map +1 -1
- package/execution/sandbox-path-normalizer.d.ts +46 -0
- package/execution/sandbox-path-normalizer.d.ts.map +1 -0
- package/execution/sandbox-path-normalizer.js +73 -0
- package/execution/sandbox-path-normalizer.js.map +1 -0
- package/execution/tool-categories.d.ts +35 -8
- package/execution/tool-categories.d.ts.map +1 -1
- package/execution/tool-categories.js +76 -10
- package/execution/tool-categories.js.map +1 -1
- package/execution/tool-rendering-primitives.d.ts +61 -0
- package/execution/tool-rendering-primitives.d.ts.map +1 -0
- package/execution/tool-rendering-primitives.js +106 -0
- package/execution/tool-rendering-primitives.js.map +1 -0
- package/execution/useArtifactContent.d.ts +5 -1
- package/execution/useArtifactContent.d.ts.map +1 -1
- package/execution/useArtifactContent.js +6 -2
- package/execution/useArtifactContent.js.map +1 -1
- package/execution/useWorkspaceWriteBacks.d.ts +40 -0
- package/execution/useWorkspaceWriteBacks.d.ts.map +1 -0
- package/execution/useWorkspaceWriteBacks.js +41 -0
- package/execution/useWorkspaceWriteBacks.js.map +1 -0
- package/github/GitHubRepoPicker.d.ts +5 -2
- package/github/GitHubRepoPicker.d.ts.map +1 -1
- package/github/GitHubRepoPicker.js +133 -36
- package/github/GitHubRepoPicker.js.map +1 -1
- package/github/index.d.ts +1 -0
- package/github/index.d.ts.map +1 -1
- package/github/index.js +1 -0
- package/github/index.js.map +1 -1
- package/github/useGitHubSearch.d.ts +20 -0
- package/github/useGitHubSearch.d.ts.map +1 -0
- package/github/useGitHubSearch.js +127 -0
- package/github/useGitHubSearch.js.map +1 -0
- package/index.d.ts +9 -6
- package/index.d.ts.map +1 -1
- package/index.js +7 -3
- package/index.js.map +1 -1
- package/internal/CloudFeatureNotice.d.ts +19 -0
- package/internal/CloudFeatureNotice.d.ts.map +1 -0
- package/internal/CloudFeatureNotice.js +21 -0
- package/internal/CloudFeatureNotice.js.map +1 -0
- package/mcp-server/McpServerDetailView.d.ts +15 -1
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +11 -3
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/package.json +4 -4
- package/provider.d.ts +14 -2
- package/provider.d.ts.map +1 -1
- package/provider.js +3 -2
- package/provider.js.map +1 -1
- package/session/index.d.ts +4 -0
- package/session/index.d.ts.map +1 -1
- package/session/index.js +2 -0
- package/session/index.js.map +1 -1
- package/session/useSessionArtifacts.d.ts +73 -0
- package/session/useSessionArtifacts.d.ts.map +1 -0
- package/session/useSessionArtifacts.js +95 -0
- package/session/useSessionArtifacts.js.map +1 -0
- package/session/useSessionWriteBacks.d.ts +56 -0
- package/session/useSessionWriteBacks.d.ts.map +1 -0
- package/session/useSessionWriteBacks.js +56 -0
- package/session/useSessionWriteBacks.js.map +1 -0
- package/src/deployment-mode.ts +46 -0
- package/src/execution/ApprovalCard.tsx +130 -283
- package/src/execution/ArtifactCard.tsx +40 -0
- package/src/execution/ArtifactPreviewModal.tsx +2 -0
- package/src/execution/ArtifactsWidget.tsx +51 -43
- package/src/execution/McpToolDetail.tsx +283 -0
- package/src/execution/MessageThread.tsx +18 -0
- package/src/execution/SandboxContext.ts +47 -0
- package/src/execution/ToolArgsView.tsx +279 -0
- package/src/execution/ToolCallDetail.tsx +54 -220
- package/src/execution/ToolCallGroup.tsx +3 -2
- package/src/execution/ToolCallItem.tsx +21 -3
- package/src/execution/WriteBackCard.tsx +210 -0
- package/src/execution/WriteBacksWidget.tsx +82 -0
- package/src/execution/__tests__/file-path-resolver.test.ts +295 -0
- package/src/execution/file-path-resolver.ts +24 -12
- package/src/execution/index.ts +38 -0
- package/src/execution/sandbox-path-normalizer.ts +80 -0
- package/src/execution/tool-categories.ts +89 -9
- package/src/execution/tool-rendering-primitives.tsx +253 -0
- package/src/execution/useArtifactContent.ts +6 -1
- package/src/execution/useWorkspaceWriteBacks.ts +56 -0
- package/src/github/GitHubRepoPicker.tsx +413 -108
- package/src/github/index.ts +5 -0
- package/src/github/useGitHubSearch.ts +162 -0
- package/src/index.ts +27 -0
- package/src/internal/CloudFeatureNotice.tsx +60 -0
- package/src/mcp-server/McpServerDetailView.tsx +24 -2
- package/src/provider.tsx +18 -2
- package/src/session/index.ts +12 -0
- package/src/session/useSessionArtifacts.ts +143 -0
- package/src/session/useSessionWriteBacks.ts +94 -0
- package/styles.css +1 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
/**
|
|
6
|
+
* Shared truncation threshold for all collapsible tool rendering
|
|
7
|
+
* primitives. Applied consistently across detail views and approval
|
|
8
|
+
* card previews.
|
|
9
|
+
*/
|
|
10
|
+
export const TRUNCATION_LINE_LIMIT = 10;
|
|
11
|
+
/**
|
|
12
|
+
* A labeled `<pre>` block with automatic line-based truncation and
|
|
13
|
+
* an expand/collapse toggle.
|
|
14
|
+
*
|
|
15
|
+
* Used for tool arguments, file content previews, and result blocks
|
|
16
|
+
* across both the detail view and the approval card.
|
|
17
|
+
*/
|
|
18
|
+
export function CollapsibleCode({ label, content, className }) {
|
|
19
|
+
const lines = content.split("\n");
|
|
20
|
+
const needsTruncation = lines.length > TRUNCATION_LINE_LIMIT;
|
|
21
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
22
|
+
const displayContent = needsTruncation && !isExpanded
|
|
23
|
+
? lines.slice(0, TRUNCATION_LINE_LIMIT).join("\n") + "\n\u2026"
|
|
24
|
+
: content;
|
|
25
|
+
return (_jsxs("div", { className: cn("space-y-1", className), children: [_jsx("span", { className: "font-medium text-muted-foreground", children: label }), _jsx("pre", { className: "max-h-80 overflow-auto whitespace-pre-wrap break-words rounded-md border border-border bg-muted/40 p-2 font-mono text-foreground", children: displayContent }), needsTruncation && (_jsx("button", { type: "button", onClick: () => setIsExpanded((v) => !v), className: "text-xs font-medium text-primary transition-colors hover:text-primary/80", children: isExpanded ? "Show less" : `Show all ${lines.length} lines` }))] }));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A bare `<pre>` element with line-based truncation. Unlike
|
|
29
|
+
* {@link CollapsibleCode}, this has no label, border, or background
|
|
30
|
+
* — the caller controls container styling via `className`.
|
|
31
|
+
*
|
|
32
|
+
* Pass container styles (border, background, max-height) through
|
|
33
|
+
* `className` when rendering standalone; omit when the parent
|
|
34
|
+
* already provides a styled container (e.g. terminal blocks).
|
|
35
|
+
*/
|
|
36
|
+
export function CollapsiblePre({ content, className }) {
|
|
37
|
+
const lines = content.split("\n");
|
|
38
|
+
const needsTruncation = lines.length > TRUNCATION_LINE_LIMIT;
|
|
39
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
40
|
+
const displayContent = needsTruncation && !isExpanded
|
|
41
|
+
? lines.slice(0, TRUNCATION_LINE_LIMIT).join("\n") + "\n\u2026"
|
|
42
|
+
: content;
|
|
43
|
+
return (_jsxs(_Fragment, { children: [_jsx("pre", { className: cn("whitespace-pre-wrap break-words font-mono", className), children: displayContent }), needsTruncation && (_jsx("button", { type: "button", onClick: () => setIsExpanded((v) => !v), className: "mt-1 text-xs font-medium text-primary transition-colors hover:text-primary/80", children: isExpanded ? "Show less" : `Show all ${lines.length} lines` }))] }));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A collapsible JSON section with a chevron toggle. Initially
|
|
47
|
+
* collapsed, showing the label and line count. Useful for complex
|
|
48
|
+
* (non-scalar) tool arguments.
|
|
49
|
+
*/
|
|
50
|
+
export function CollapsibleJsonBlock({ label, content }) {
|
|
51
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
52
|
+
const lines = content.split("\n");
|
|
53
|
+
const isLong = lines.length > 3;
|
|
54
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsxs("button", { type: "button", onClick: () => setIsExpanded((v) => !v), className: "flex items-center gap-1 font-medium text-muted-foreground transition-colors hover:text-foreground", children: [_jsx("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", className: cn("shrink-0 transition-transform duration-150", isExpanded && "rotate-90"), "aria-hidden": "true", children: _jsx("path", { d: "M2 1L6 4L2 7" }) }), label, !isExpanded && isLong && (_jsxs("span", { className: "font-normal text-muted-foreground/60", children: ["(", lines.length, " lines)"] }))] }), isExpanded && (_jsx("pre", { className: "max-h-80 overflow-auto whitespace-pre-wrap break-words rounded-md border border-border bg-muted/40 p-2 font-mono text-foreground", children: content }))] }));
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Inline SVG icons
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
/** Small document icon for file path displays (10x10). */
|
|
60
|
+
export function FilePathIcon() {
|
|
61
|
+
return (_jsxs("svg", { width: "10", height: "10", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round", className: "shrink-0 text-muted-foreground", "aria-hidden": "true", children: [_jsx("path", { d: "M7 1H3C2.45 1 2 1.45 2 2V10C2 10.55 2.45 11 3 11H9C9.55 11 10 10.55 10 10V4L7 1Z" }), _jsx("path", { d: "M7 1V4H10" })] }));
|
|
62
|
+
}
|
|
63
|
+
/** MCP server node/link icon (10x10). */
|
|
64
|
+
export function McpServerIcon() {
|
|
65
|
+
return (_jsxs("svg", { width: "10", height: "10", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round", className: "shrink-0", "aria-hidden": "true", children: [_jsx("circle", { cx: "6", cy: "3", r: "1.5" }), _jsx("circle", { cx: "6", cy: "9", r: "1.5" }), _jsx("path", { d: "M6 4.5V7.5" }), _jsx("path", { d: "M3 6H4.5" }), _jsx("path", { d: "M7.5 6H9" })] }));
|
|
66
|
+
}
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Shared utilities
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
/** Safely serialise an object to pretty JSON. */
|
|
71
|
+
export function formatJson(obj) {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.stringify(obj, null, 2);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return String(obj);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Pretty-print a result string if it's valid JSON, otherwise return as-is. */
|
|
80
|
+
export function formatResult(result) {
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(result);
|
|
83
|
+
return JSON.stringify(parsed, null, 2);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Detect scalar values (string, number, boolean). */
|
|
90
|
+
export function isScalar(value) {
|
|
91
|
+
const t = typeof value;
|
|
92
|
+
return t === "string" || t === "number" || t === "boolean";
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Title-case a snake_case or camelCase argument key for display.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* humanizeArgKey("mcp_server_slug") // "Mcp Server Slug"
|
|
99
|
+
*/
|
|
100
|
+
export function humanizeArgKey(key) {
|
|
101
|
+
return key
|
|
102
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
103
|
+
.replace(/[_-]+/g, " ")
|
|
104
|
+
.replace(/\b[a-z]/g, (c) => c.toUpperCase());
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=tool-rendering-primitives.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-rendering-primitives.js","sourceRoot":"","sources":["../../src/execution/tool-rendering-primitives.tsx"],"names":[],"mappings":"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"}
|
|
@@ -67,9 +67,13 @@ export interface UseArtifactContentReturn {
|
|
|
67
67
|
* @param storageKey - Storage key from `ExecutionArtifact.storageKey`, or `null` to skip.
|
|
68
68
|
* @param entryPath - For directory artifacts: relative path of a file within
|
|
69
69
|
* the archive to extract. `null` returns the full artifact (existing behavior).
|
|
70
|
+
* @param contentHash - SHA-256 hex digest from `ExecutionArtifact.contentHash`.
|
|
71
|
+
* When the same file is overwritten during execution, the `storageKey` stays
|
|
72
|
+
* stable but `contentHash` changes, triggering a re-fetch so the UI never
|
|
73
|
+
* shows stale content. Pass `undefined` or omit for backwards compatibility.
|
|
70
74
|
*
|
|
71
75
|
* @see useExecutionArtifacts — extracts artifact metadata from an execution
|
|
72
76
|
* @see isTextArtifact — heuristic for whether content is fetchable as text
|
|
73
77
|
*/
|
|
74
|
-
export declare function useArtifactContent(executionId: string | null, storageKey: string | null, entryPath?: string | null): UseArtifactContentReturn;
|
|
78
|
+
export declare function useArtifactContent(executionId: string | null, storageKey: string | null, entryPath?: string | null, contentHash?: string): UseArtifactContentReturn;
|
|
75
79
|
//# sourceMappingURL=useArtifactContent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useArtifactContent.d.ts","sourceRoot":"","sources":["../../src/execution/useArtifactContent.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAE9B,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAE5B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED
|
|
1
|
+
{"version":3,"file":"useArtifactContent.d.ts","sourceRoot":"","sources":["../../src/execution/useArtifactContent.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAE9B,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAE5B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EACzB,WAAW,CAAC,EAAE,MAAM,GACnB,wBAAwB,CAiE1B"}
|
|
@@ -43,11 +43,15 @@ import { useStigmer } from "../hooks";
|
|
|
43
43
|
* @param storageKey - Storage key from `ExecutionArtifact.storageKey`, or `null` to skip.
|
|
44
44
|
* @param entryPath - For directory artifacts: relative path of a file within
|
|
45
45
|
* the archive to extract. `null` returns the full artifact (existing behavior).
|
|
46
|
+
* @param contentHash - SHA-256 hex digest from `ExecutionArtifact.contentHash`.
|
|
47
|
+
* When the same file is overwritten during execution, the `storageKey` stays
|
|
48
|
+
* stable but `contentHash` changes, triggering a re-fetch so the UI never
|
|
49
|
+
* shows stale content. Pass `undefined` or omit for backwards compatibility.
|
|
46
50
|
*
|
|
47
51
|
* @see useExecutionArtifacts — extracts artifact metadata from an execution
|
|
48
52
|
* @see isTextArtifact — heuristic for whether content is fetchable as text
|
|
49
53
|
*/
|
|
50
|
-
export function useArtifactContent(executionId, storageKey, entryPath) {
|
|
54
|
+
export function useArtifactContent(executionId, storageKey, entryPath, contentHash) {
|
|
51
55
|
const stigmer = useStigmer();
|
|
52
56
|
const [content, setContent] = useState(null);
|
|
53
57
|
const [contentType, setContentType] = useState(null);
|
|
@@ -96,7 +100,7 @@ export function useArtifactContent(executionId, storageKey, entryPath) {
|
|
|
96
100
|
return () => {
|
|
97
101
|
cancelled.current = true;
|
|
98
102
|
};
|
|
99
|
-
}, [executionId, storageKey, entryPath, stigmer, fetchKey]);
|
|
103
|
+
}, [executionId, storageKey, entryPath, contentHash, stigmer, fetchKey]);
|
|
100
104
|
return { content, contentType, isTruncated, isLoading, error, refetch };
|
|
101
105
|
}
|
|
102
106
|
//# sourceMappingURL=useArtifactContent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useArtifactContent.js","sourceRoot":"","sources":["../../src/execution/useArtifactContent.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,SAAS,EAAU,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,+BAA+B,EAAE,MAAM,4DAA4D,CAAC;AAC7G,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAqCtC
|
|
1
|
+
{"version":3,"file":"useArtifactContent.js","sourceRoot":"","sources":["../../src/execution/useArtifactContent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
2
|
+
import type { WorkspaceWriteBack } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/writeback_pb";
|
|
3
|
+
export interface UseWorkspaceWriteBacksReturn {
|
|
4
|
+
/** Write-back outcomes for git-backed workspace entries, ordered by workspace entry name. */
|
|
5
|
+
readonly writeBacks: readonly WorkspaceWriteBack[];
|
|
6
|
+
/** `true` when the execution has at least one write-back entry. */
|
|
7
|
+
readonly hasWriteBacks: boolean;
|
|
8
|
+
/** Total number of write-back entries. */
|
|
9
|
+
readonly writeBackCount: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Pure derivation hook that extracts workspace write-back data from an
|
|
13
|
+
* {@link AgentExecution} snapshot.
|
|
14
|
+
*
|
|
15
|
+
* Follows the same `useMemo`-based derivation pattern as
|
|
16
|
+
* {@link useExecutionArtifacts}: no side effects, no data fetching.
|
|
17
|
+
* The execution object (typically from {@link useExecutionStream}) is
|
|
18
|
+
* the single input.
|
|
19
|
+
*
|
|
20
|
+
* Returns an empty array when the execution is `null` or has no
|
|
21
|
+
* write-backs, eliminating null-checking at every consumer call site.
|
|
22
|
+
*
|
|
23
|
+
* Each `WorkspaceWriteBack` entry corresponds to a git-backed workspace
|
|
24
|
+
* entry where the platform detected file changes and ran the automatic
|
|
25
|
+
* branch/commit/push/PR workflow.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* const { execution } = useExecutionStream(executionId);
|
|
30
|
+
* const { writeBacks, hasWriteBacks } = useWorkspaceWriteBacks(execution);
|
|
31
|
+
*
|
|
32
|
+
* if (hasWriteBacks) {
|
|
33
|
+
* writeBacks.forEach((wb) => console.log(wb.workspaceEntryName, wb.pullRequestUrl));
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @see useExecutionArtifacts — similar derivation hook for artifacts
|
|
38
|
+
*/
|
|
39
|
+
export declare function useWorkspaceWriteBacks(execution: AgentExecution | null): UseWorkspaceWriteBacksReturn;
|
|
40
|
+
//# sourceMappingURL=useWorkspaceWriteBacks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWorkspaceWriteBacks.d.ts","sourceRoot":"","sources":["../../src/execution/useWorkspaceWriteBacks.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6DAA6D,CAAC;AAClG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mEAAmE,CAAC;AAE5G,MAAM,WAAW,4BAA4B;IAC3C,6FAA6F;IAC7F,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACnD,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,0CAA0C;IAC1C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,cAAc,GAAG,IAAI,GAC/B,4BAA4B,CAU9B"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Pure derivation hook that extracts workspace write-back data from an
|
|
5
|
+
* {@link AgentExecution} snapshot.
|
|
6
|
+
*
|
|
7
|
+
* Follows the same `useMemo`-based derivation pattern as
|
|
8
|
+
* {@link useExecutionArtifacts}: no side effects, no data fetching.
|
|
9
|
+
* The execution object (typically from {@link useExecutionStream}) is
|
|
10
|
+
* the single input.
|
|
11
|
+
*
|
|
12
|
+
* Returns an empty array when the execution is `null` or has no
|
|
13
|
+
* write-backs, eliminating null-checking at every consumer call site.
|
|
14
|
+
*
|
|
15
|
+
* Each `WorkspaceWriteBack` entry corresponds to a git-backed workspace
|
|
16
|
+
* entry where the platform detected file changes and ran the automatic
|
|
17
|
+
* branch/commit/push/PR workflow.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const { execution } = useExecutionStream(executionId);
|
|
22
|
+
* const { writeBacks, hasWriteBacks } = useWorkspaceWriteBacks(execution);
|
|
23
|
+
*
|
|
24
|
+
* if (hasWriteBacks) {
|
|
25
|
+
* writeBacks.forEach((wb) => console.log(wb.workspaceEntryName, wb.pullRequestUrl));
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @see useExecutionArtifacts — similar derivation hook for artifacts
|
|
30
|
+
*/
|
|
31
|
+
export function useWorkspaceWriteBacks(execution) {
|
|
32
|
+
return useMemo(() => {
|
|
33
|
+
const writeBacks = execution?.status?.workspaceWriteBacks ?? [];
|
|
34
|
+
return {
|
|
35
|
+
writeBacks,
|
|
36
|
+
hasWriteBacks: writeBacks.length > 0,
|
|
37
|
+
writeBackCount: writeBacks.length,
|
|
38
|
+
};
|
|
39
|
+
}, [execution]);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=useWorkspaceWriteBacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWorkspaceWriteBacks.js","sourceRoot":"","sources":["../../src/execution/useWorkspaceWriteBacks.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAahC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAAgC;IAEhC,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,UAAU,GAAG,SAAS,EAAE,MAAM,EAAE,mBAAmB,IAAI,EAAE,CAAC;QAEhE,OAAO;YACL,UAAU;YACV,aAAa,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC;YACpC,cAAc,EAAE,UAAU,CAAC,MAAM;SAClC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -11,12 +11,15 @@ export interface GitHubRepoPickerProps {
|
|
|
11
11
|
* Styled component for browsing and selecting a GitHub repository.
|
|
12
12
|
*
|
|
13
13
|
* Features:
|
|
14
|
-
* -
|
|
14
|
+
* - Two modes: "My Repos" (user's own repos) and "All GitHub" (public search)
|
|
15
|
+
* - Owner-grouped sections in My Repos mode
|
|
15
16
|
* - Recently selected repos pinned at top
|
|
16
|
-
* - Fixed
|
|
17
|
+
* - Fixed max-height with scroll shadow indicators
|
|
17
18
|
* - Keyboard navigation (Arrow keys, Enter, Escape)
|
|
18
19
|
* - Search with match highlighting
|
|
19
20
|
* - Branch selector after repo selection
|
|
21
|
+
* - Manual URL entry for repos not discoverable via search
|
|
22
|
+
* - Link to manage GitHub App repository access
|
|
20
23
|
*
|
|
21
24
|
* All visual properties flow through `--stgm-*` tokens.
|
|
22
25
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GitHubRepoPicker.d.ts","sourceRoot":"","sources":["../../src/github/GitHubRepoPicker.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GitHubRepoPicker.d.ts","sourceRoot":"","sources":["../../src/github/GitHubRepoPicker.tsx"],"names":[],"mappings":"AAaA,MAAM,WAAW,qBAAqB;IACpC,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAwHD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE,qBAAqB,2CA0cvB"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useEffect, useRef, useMemo, } from "react";
|
|
4
4
|
import { useGitHubRepos } from "./useGitHubRepos";
|
|
5
|
+
import { useGitHubSearch } from "./useGitHubSearch";
|
|
5
6
|
const RECENT_REPOS_KEY = "stigmer:github:recent-repos";
|
|
6
7
|
const MAX_RECENT = 3;
|
|
7
8
|
function getRecentRepos() {
|
|
@@ -21,7 +22,6 @@ function addRecentRepo(repo) {
|
|
|
21
22
|
function groupRepos(filteredRepos, recentEntries) {
|
|
22
23
|
const groups = [];
|
|
23
24
|
const repoLookup = new Map(filteredRepos.map((r) => [`${r.owner}/${r.name}`, r]));
|
|
24
|
-
// Recent group — only include entries that exist in the filtered set
|
|
25
25
|
const recentMatched = recentEntries
|
|
26
26
|
.map((r) => repoLookup.get(`${r.owner}/${r.name}`))
|
|
27
27
|
.filter((r) => r !== undefined);
|
|
@@ -33,7 +33,6 @@ function groupRepos(filteredRepos, recentEntries) {
|
|
|
33
33
|
repos: recentMatched,
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
-
// Owner groups — personal (User) repos first, then orgs by repo count
|
|
37
36
|
const ownerMap = new Map();
|
|
38
37
|
for (const repo of filteredRepos) {
|
|
39
38
|
const existing = ownerMap.get(repo.owner);
|
|
@@ -69,26 +68,38 @@ function HighlightMatch({ text, query }) {
|
|
|
69
68
|
// Main component
|
|
70
69
|
// ---------------------------------------------------------------------------
|
|
71
70
|
const LIST_ID = "stgm-repo-list";
|
|
71
|
+
const GITHUB_INSTALLATIONS_URL = "https://github.com/settings/installations";
|
|
72
72
|
/**
|
|
73
73
|
* Styled component for browsing and selecting a GitHub repository.
|
|
74
74
|
*
|
|
75
75
|
* Features:
|
|
76
|
-
* -
|
|
76
|
+
* - Two modes: "My Repos" (user's own repos) and "All GitHub" (public search)
|
|
77
|
+
* - Owner-grouped sections in My Repos mode
|
|
77
78
|
* - Recently selected repos pinned at top
|
|
78
|
-
* - Fixed
|
|
79
|
+
* - Fixed max-height with scroll shadow indicators
|
|
79
80
|
* - Keyboard navigation (Arrow keys, Enter, Escape)
|
|
80
81
|
* - Search with match highlighting
|
|
81
82
|
* - Branch selector after repo selection
|
|
83
|
+
* - Manual URL entry for repos not discoverable via search
|
|
84
|
+
* - Link to manage GitHub App repository access
|
|
82
85
|
*
|
|
83
86
|
* All visual properties flow through `--stgm-*` tokens.
|
|
84
87
|
*/
|
|
85
88
|
export function GitHubRepoPicker({ token, onSelect, onCancel, className, }) {
|
|
86
|
-
const
|
|
87
|
-
|
|
89
|
+
const [mode, setMode] = useState("my-repos");
|
|
90
|
+
const [showManualEntry, setShowManualEntry] = useState(false);
|
|
91
|
+
// My Repos data
|
|
92
|
+
const myRepos = useGitHubRepos(token);
|
|
93
|
+
// All GitHub search data
|
|
94
|
+
const githubSearch = useGitHubSearch(token);
|
|
95
|
+
// Branch selection state (shared across modes)
|
|
88
96
|
const [selectedRepo, setSelectedRepo] = useState(null);
|
|
89
97
|
const [branches, setBranches] = useState([]);
|
|
90
98
|
const [selectedBranch, setSelectedBranch] = useState("");
|
|
91
99
|
const [loadingBranches, setLoadingBranches] = useState(false);
|
|
100
|
+
// Manual URL state
|
|
101
|
+
const [manualUrl, setManualUrl] = useState("");
|
|
102
|
+
const [manualBranch, setManualBranch] = useState("");
|
|
92
103
|
// Keyboard navigation
|
|
93
104
|
const [focusIndex, setFocusIndex] = useState(-1);
|
|
94
105
|
const listRef = useRef(null);
|
|
@@ -98,9 +109,18 @@ export function GitHubRepoPicker({ token, onSelect, onCancel, className, }) {
|
|
|
98
109
|
const [canScrollDown, setCanScrollDown] = useState(false);
|
|
99
110
|
// Recent repos
|
|
100
111
|
const [recentRepos, setRecentRepos] = useState(getRecentRepos);
|
|
101
|
-
//
|
|
102
|
-
const
|
|
112
|
+
// --- Mode-dependent derived state ---
|
|
113
|
+
const activeSearch = mode === "my-repos" ? myRepos.search : githubSearch.query;
|
|
114
|
+
const setActiveSearch = mode === "my-repos" ? myRepos.setSearch : githubSearch.setQuery;
|
|
115
|
+
const activeRepos = mode === "my-repos" ? myRepos.repos : githubSearch.results;
|
|
116
|
+
const activeError = mode === "my-repos" ? myRepos.error : githubSearch.error;
|
|
117
|
+
const activeIsLoading = mode === "my-repos" ? myRepos.isLoading : githubSearch.isSearching;
|
|
118
|
+
// Group repos only in "my-repos" mode
|
|
119
|
+
const groups = useMemo(() => (mode === "my-repos" ? groupRepos(activeRepos, recentRepos) : []), [mode, activeRepos, recentRepos]);
|
|
120
|
+
// Flat list for keyboard nav: grouped in my-repos, flat in all-github
|
|
103
121
|
const flatItems = useMemo(() => {
|
|
122
|
+
if (mode === "all-github")
|
|
123
|
+
return [...activeRepos];
|
|
104
124
|
const items = [];
|
|
105
125
|
for (const group of groups) {
|
|
106
126
|
for (const repo of group.repos) {
|
|
@@ -108,11 +128,11 @@ export function GitHubRepoPicker({ token, onSelect, onCancel, className, }) {
|
|
|
108
128
|
}
|
|
109
129
|
}
|
|
110
130
|
return items;
|
|
111
|
-
}, [groups]);
|
|
112
|
-
// Reset focus index when search changes
|
|
131
|
+
}, [mode, activeRepos, groups]);
|
|
132
|
+
// Reset focus index when search or mode changes
|
|
113
133
|
useEffect(() => {
|
|
114
134
|
setFocusIndex(-1);
|
|
115
|
-
}, [
|
|
135
|
+
}, [activeSearch, mode]);
|
|
116
136
|
// Scroll focused item into view
|
|
117
137
|
useEffect(() => {
|
|
118
138
|
if (focusIndex >= 0) {
|
|
@@ -136,10 +156,22 @@ export function GitHubRepoPicker({ token, onSelect, onCancel, className, }) {
|
|
|
136
156
|
updateScrollShadows();
|
|
137
157
|
return () => el.removeEventListener("scroll", updateScrollShadows);
|
|
138
158
|
}, [updateScrollShadows]);
|
|
139
|
-
// Re-check shadows when repo data changes
|
|
140
159
|
useEffect(() => {
|
|
141
160
|
updateScrollShadows();
|
|
142
|
-
}, [
|
|
161
|
+
}, [activeRepos, updateScrollShadows]);
|
|
162
|
+
const handleModeSwitch = useCallback((newMode) => {
|
|
163
|
+
if (newMode === mode)
|
|
164
|
+
return;
|
|
165
|
+
setMode(newMode);
|
|
166
|
+
setFocusIndex(-1);
|
|
167
|
+
if (newMode === "my-repos") {
|
|
168
|
+
githubSearch.setQuery("");
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
myRepos.setSearch("");
|
|
172
|
+
}
|
|
173
|
+
setTimeout(() => searchRef.current?.focus(), 0);
|
|
174
|
+
}, [mode, githubSearch, myRepos]);
|
|
143
175
|
const handleRepoClick = useCallback(async (repo) => {
|
|
144
176
|
setSelectedRepo({
|
|
145
177
|
owner: repo.owner,
|
|
@@ -149,10 +181,10 @@ export function GitHubRepoPicker({ token, onSelect, onCancel, className, }) {
|
|
|
149
181
|
});
|
|
150
182
|
setSelectedBranch(repo.defaultBranch);
|
|
151
183
|
setLoadingBranches(true);
|
|
152
|
-
const b = await fetchBranches(repo.owner, repo.name);
|
|
184
|
+
const b = await myRepos.fetchBranches(repo.owner, repo.name);
|
|
153
185
|
setBranches(b);
|
|
154
186
|
setLoadingBranches(false);
|
|
155
|
-
}, [
|
|
187
|
+
}, [myRepos]);
|
|
156
188
|
const handleAdd = useCallback(() => {
|
|
157
189
|
if (selectedRepo && selectedBranch) {
|
|
158
190
|
addRecentRepo({
|
|
@@ -168,8 +200,15 @@ export function GitHubRepoPicker({ token, onSelect, onCancel, className, }) {
|
|
|
168
200
|
setSelectedBranch("");
|
|
169
201
|
}
|
|
170
202
|
}, [selectedRepo, selectedBranch, onSelect]);
|
|
171
|
-
|
|
172
|
-
|
|
203
|
+
const handleManualAdd = useCallback(() => {
|
|
204
|
+
const url = manualUrl.trim();
|
|
205
|
+
if (!url)
|
|
206
|
+
return;
|
|
207
|
+
onSelect(url, manualBranch.trim() || "main");
|
|
208
|
+
setManualUrl("");
|
|
209
|
+
setManualBranch("");
|
|
210
|
+
setShowManualEntry(false);
|
|
211
|
+
}, [manualUrl, manualBranch, onSelect]);
|
|
173
212
|
const handleSearchKeyDown = useCallback((e) => {
|
|
174
213
|
if (e.key === "ArrowDown") {
|
|
175
214
|
e.preventDefault();
|
|
@@ -186,30 +225,46 @@ export function GitHubRepoPicker({ token, onSelect, onCancel, className, }) {
|
|
|
186
225
|
handleRepoClick(flatItems[focusIndex]);
|
|
187
226
|
}
|
|
188
227
|
else if (e.key === "Enter") {
|
|
189
|
-
// Prevent form submission when no item is focused
|
|
190
228
|
e.preventDefault();
|
|
191
229
|
}
|
|
192
230
|
else if (e.key === "Escape") {
|
|
193
231
|
e.preventDefault();
|
|
194
|
-
if (
|
|
195
|
-
|
|
232
|
+
if (activeSearch) {
|
|
233
|
+
setActiveSearch("");
|
|
196
234
|
setFocusIndex(-1);
|
|
197
235
|
}
|
|
198
236
|
else {
|
|
199
237
|
onCancel?.();
|
|
200
238
|
}
|
|
201
239
|
}
|
|
202
|
-
}, [flatItems, focusIndex, handleRepoClick, onCancel,
|
|
240
|
+
}, [flatItems, focusIndex, handleRepoClick, onCancel, activeSearch, setActiveSearch]);
|
|
241
|
+
const handleManualKeyDown = useCallback((e) => {
|
|
242
|
+
if (e.key === "Enter") {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
handleManualAdd();
|
|
245
|
+
}
|
|
246
|
+
else if (e.key === "Escape") {
|
|
247
|
+
e.preventDefault();
|
|
248
|
+
setShowManualEntry(false);
|
|
249
|
+
setTimeout(() => searchRef.current?.focus(), 0);
|
|
250
|
+
}
|
|
251
|
+
}, [handleManualAdd]);
|
|
203
252
|
// --- Branch selection view ---
|
|
204
253
|
if (selectedRepo) {
|
|
205
254
|
return (_jsxs("div", { className: ["space-y-2", className].filter(Boolean).join(" "), children: [_jsxs("div", { className: "flex items-center gap-2 text-xs text-foreground", children: [_jsx("button", { type: "button", onClick: () => {
|
|
206
255
|
setSelectedRepo(null);
|
|
207
256
|
setBranches([]);
|
|
208
|
-
// Restore focus to search
|
|
209
257
|
setTimeout(() => searchRef.current?.focus(), 0);
|
|
210
258
|
}, className: "text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Back to repo list", children: _jsx(ChevronLeftIcon, {}) }), _jsxs("span", { className: "font-medium truncate", children: [selectedRepo.owner, "/", selectedRepo.name] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "text-[0.65rem] text-muted-foreground", children: "Branch" }), loadingBranches ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "Loading branches..." })) : (_jsx("select", { value: selectedBranch, onChange: (e) => setSelectedBranch(e.target.value), className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: branches.map((b) => (_jsx("option", { value: b.name, children: b.name }, b.name))) }))] }), _jsx("div", { className: "flex justify-end", children: _jsx("button", { type: "button", onClick: handleAdd, disabled: !selectedBranch, className: "rounded-md bg-primary px-2.5 py-1 text-xs text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-40", children: "Add" }) })] }));
|
|
211
259
|
}
|
|
212
|
-
// ---
|
|
260
|
+
// --- Manual URL entry view ---
|
|
261
|
+
if (showManualEntry) {
|
|
262
|
+
return (_jsxs("div", { className: ["space-y-2", className].filter(Boolean).join(" "), children: [_jsxs("div", { className: "flex items-center gap-2 text-xs text-foreground", children: [_jsx("button", { type: "button", onClick: () => {
|
|
263
|
+
setShowManualEntry(false);
|
|
264
|
+
setTimeout(() => searchRef.current?.focus(), 0);
|
|
265
|
+
}, className: "text-muted-foreground hover:text-foreground transition-colors", "aria-label": "Back to repo list", children: _jsx(ChevronLeftIcon, {}) }), _jsx("span", { className: "font-medium", children: "Paste a repository URL" })] }), _jsx("input", { type: "url", placeholder: "https://github.com/org/repo", value: manualUrl, onChange: (e) => setManualUrl(e.target.value), onKeyDown: handleManualKeyDown, className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", autoFocus: true }), _jsx("input", { type: "text", placeholder: "Branch (optional, defaults to main)", value: manualBranch, onChange: (e) => setManualBranch(e.target.value), onKeyDown: handleManualKeyDown, className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" }), _jsx("div", { className: "flex justify-end", children: _jsx("button", { type: "button", onClick: handleManualAdd, disabled: !manualUrl.trim(), className: "rounded-md bg-primary px-2.5 py-1 text-xs text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-40", children: "Add" }) })] }));
|
|
266
|
+
}
|
|
267
|
+
// --- Compute flat index offsets per group for "my-repos" rendering ---
|
|
213
268
|
let runningIndex = 0;
|
|
214
269
|
const groupOffsets = groups.map((g) => {
|
|
215
270
|
const offset = runningIndex;
|
|
@@ -217,21 +272,63 @@ export function GitHubRepoPicker({ token, onSelect, onCancel, className, }) {
|
|
|
217
272
|
return offset;
|
|
218
273
|
});
|
|
219
274
|
// --- Main repo list view ---
|
|
220
|
-
return (_jsxs("div", { className: ["space-y-1.5", className].filter(Boolean).join(" "), children: [
|
|
275
|
+
return (_jsxs("div", { className: ["space-y-1.5", className].filter(Boolean).join(" "), children: [_jsxs("div", { className: "flex rounded-md border border-border bg-muted/30 p-0.5", children: [_jsx("button", { type: "button", onClick: () => handleModeSwitch("my-repos"), className: [
|
|
276
|
+
"flex-1 rounded px-2 py-1 text-[0.65rem] font-medium transition-colors",
|
|
277
|
+
mode === "my-repos"
|
|
278
|
+
? "bg-background text-foreground shadow-sm"
|
|
279
|
+
: "text-muted-foreground hover:text-foreground",
|
|
280
|
+
].join(" "), children: "My Repos" }), _jsx("button", { type: "button", onClick: () => handleModeSwitch("all-github"), className: [
|
|
281
|
+
"flex-1 rounded px-2 py-1 text-[0.65rem] font-medium transition-colors",
|
|
282
|
+
mode === "all-github"
|
|
283
|
+
? "bg-background text-foreground shadow-sm"
|
|
284
|
+
: "text-muted-foreground hover:text-foreground",
|
|
285
|
+
].join(" "), children: "All GitHub" })] }), _jsx("input", { ref: searchRef, type: "text", role: "combobox", "aria-expanded": true, "aria-controls": LIST_ID, "aria-activedescendant": focusIndex >= 0 ? `stgm-repo-${focusIndex}` : undefined, placeholder: mode === "my-repos"
|
|
286
|
+
? "Search repositories..."
|
|
287
|
+
: "Search all of GitHub...", value: activeSearch, onChange: (e) => setActiveSearch(e.target.value), onKeyDown: handleSearchKeyDown, className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", autoFocus: true }), activeError && (_jsx("p", { className: "text-xs text-destructive", children: activeError })), _jsxs("div", { className: "relative", children: [canScrollUp && (_jsx("div", { className: "absolute inset-x-0 top-0 h-3 z-10 pointer-events-none", style: {
|
|
221
288
|
background: "linear-gradient(to bottom, var(--color-card, hsl(0 0% 9%)), transparent)",
|
|
222
|
-
} })), _jsx("div", { ref: listRef, id: LIST_ID, role: "listbox", "aria-label": "Repositories", className: "max-h-64 overflow-y-auto", children:
|
|
223
|
-
? "No repos match your search"
|
|
224
|
-
: "No repositories found" })) : (_jsxs(_Fragment, { children: [groups.map((group, gi) => (_jsxs("div", { children: [_jsxs("div", { className: "sticky top-0 z-[1] bg-card/95 px-2 py-1 text-[0.65rem] font-medium text-muted-foreground backdrop-blur-sm", children: [group.label, !group.isRecent && (_jsxs("span", { className: "ml-1 opacity-50", children: ["(", group.repos.length, ")"] }))] }), group.repos.map((repo, ri) => {
|
|
225
|
-
const flatIdx = groupOffsets[gi] + ri;
|
|
226
|
-
return (_jsxs("button", { id: `stgm-repo-${flatIdx}`, type: "button", "data-idx": flatIdx, onClick: () => handleRepoClick(repo), className: [
|
|
227
|
-
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs transition-colors",
|
|
228
|
-
flatIdx === focusIndex
|
|
229
|
-
? "bg-accent text-foreground"
|
|
230
|
-
: "text-foreground hover:bg-accent/50",
|
|
231
|
-
].join(" "), role: "option", "aria-selected": flatIdx === focusIndex, children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: group.isRecent ? (_jsx(HighlightMatch, { text: repo.fullName, query: search })) : (_jsx(HighlightMatch, { text: repo.name, query: search })) }), _jsx("span", { className: "shrink-0 rounded px-1 py-0.5 text-[0.6rem] bg-muted text-muted-foreground", children: repo.isPrivate ? "private" : "public" })] }, `${group.key}-${repo.id}`));
|
|
232
|
-
})] }, group.key))), isBackgroundLoading && (_jsx("div", { className: "py-1 text-center text-[0.6rem] text-muted-foreground", children: "Loading more..." }))] })) }), canScrollDown && (_jsx("div", { className: "absolute inset-x-0 bottom-0 h-3 z-10 pointer-events-none", style: {
|
|
289
|
+
} })), _jsx("div", { ref: listRef, id: LIST_ID, role: "listbox", "aria-label": "Repositories", className: "max-h-64 overflow-y-auto", children: mode === "my-repos" ? (_jsx(MyReposList, { groups: groups, groupOffsets: groupOffsets, flatItems: flatItems, focusIndex: focusIndex, isLoading: myRepos.isLoading, isBackgroundLoading: myRepos.isBackgroundLoading, search: myRepos.search, onRepoClick: handleRepoClick })) : (_jsx(SearchResultsList, { results: githubSearch.results, focusIndex: focusIndex, isSearching: githubSearch.isSearching, query: githubSearch.query, totalCount: githubSearch.totalCount, hasMore: githubSearch.hasMore, onRepoClick: handleRepoClick, onLoadMore: githubSearch.loadMore })) }), canScrollDown && (_jsx("div", { className: "absolute inset-x-0 bottom-0 h-3 z-10 pointer-events-none", style: {
|
|
233
290
|
background: "linear-gradient(to top, var(--color-card, hsl(0 0% 9%)), transparent)",
|
|
234
|
-
} }))] })] }));
|
|
291
|
+
} }))] }), _jsxs("div", { className: "flex items-center gap-3 border-t border-border pt-1.5 text-[0.65rem] text-muted-foreground", children: [_jsx("button", { type: "button", onClick: () => setShowManualEntry(true), className: "hover:text-foreground transition-colors", children: "Paste a URL" }), _jsx("span", { className: "opacity-30", children: "\u00B7" }), _jsx("a", { href: GITHUB_INSTALLATIONS_URL, target: "_blank", rel: "noopener noreferrer", className: "hover:text-foreground transition-colors", children: "Manage access" })] })] }));
|
|
292
|
+
}
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
// My Repos list (grouped)
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
function MyReposList({ groups, groupOffsets, flatItems, focusIndex, isLoading, isBackgroundLoading, search, onRepoClick, }) {
|
|
297
|
+
if (isLoading)
|
|
298
|
+
return _jsx(LoadingSkeleton, {});
|
|
299
|
+
if (flatItems.length === 0) {
|
|
300
|
+
return (_jsx("div", { className: "py-4 text-center text-xs text-muted-foreground", children: search ? "No repos match your search" : "No repositories found" }));
|
|
301
|
+
}
|
|
302
|
+
return (_jsxs(_Fragment, { children: [groups.map((group, gi) => (_jsxs("div", { children: [_jsxs("div", { className: "sticky top-0 z-[1] bg-card/95 px-2 py-1 text-[0.65rem] font-medium text-muted-foreground backdrop-blur-sm", children: [group.label, !group.isRecent && (_jsxs("span", { className: "ml-1 opacity-50", children: ["(", group.repos.length, ")"] }))] }), group.repos.map((repo, ri) => {
|
|
303
|
+
const flatIdx = groupOffsets[gi] + ri;
|
|
304
|
+
return (_jsx(RepoRow, { repo: repo, flatIdx: flatIdx, focusIndex: focusIndex, displayName: group.isRecent ? repo.fullName : repo.name, query: search, onClick: onRepoClick }, `${group.key}-${repo.id}`));
|
|
305
|
+
})] }, group.key))), isBackgroundLoading && (_jsx("div", { className: "py-1 text-center text-[0.6rem] text-muted-foreground", children: "Loading more..." }))] }));
|
|
306
|
+
}
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// All GitHub search results list (flat)
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
function SearchResultsList({ results, focusIndex, isSearching, query, totalCount, hasMore, onRepoClick, onLoadMore, }) {
|
|
311
|
+
if (!query) {
|
|
312
|
+
return (_jsx("div", { className: "py-6 text-center text-xs text-muted-foreground", children: "Type to search all of GitHub" }));
|
|
313
|
+
}
|
|
314
|
+
if (isSearching && results.length === 0) {
|
|
315
|
+
return _jsx(LoadingSkeleton, {});
|
|
316
|
+
}
|
|
317
|
+
if (results.length === 0) {
|
|
318
|
+
return (_jsx("div", { className: "py-4 text-center text-xs text-muted-foreground", children: "No repositories found" }));
|
|
319
|
+
}
|
|
320
|
+
return (_jsxs(_Fragment, { children: [totalCount > 0 && (_jsxs("div", { className: "px-2 py-1 text-[0.6rem] text-muted-foreground", children: [totalCount.toLocaleString(), " ", totalCount === 1 ? "result" : "results"] })), results.map((repo, i) => (_jsx(RepoRow, { repo: repo, flatIdx: i, focusIndex: focusIndex, displayName: repo.fullName, query: query, onClick: onRepoClick }, repo.id))), isSearching && (_jsx("div", { className: "py-1 text-center text-[0.6rem] text-muted-foreground", children: "Searching..." })), hasMore && !isSearching && (_jsx("button", { type: "button", onClick: onLoadMore, className: "w-full py-1.5 text-center text-[0.65rem] text-muted-foreground hover:text-foreground transition-colors", children: "Load more results" }))] }));
|
|
321
|
+
}
|
|
322
|
+
// ---------------------------------------------------------------------------
|
|
323
|
+
// Shared repo row
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
function RepoRow({ repo, flatIdx, focusIndex, displayName, query, onClick, }) {
|
|
326
|
+
return (_jsxs("button", { id: `stgm-repo-${flatIdx}`, type: "button", "data-idx": flatIdx, onClick: () => onClick(repo), className: [
|
|
327
|
+
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs transition-colors",
|
|
328
|
+
flatIdx === focusIndex
|
|
329
|
+
? "bg-accent text-foreground"
|
|
330
|
+
: "text-foreground hover:bg-accent/50",
|
|
331
|
+
].join(" "), role: "option", "aria-selected": flatIdx === focusIndex, children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: _jsx(HighlightMatch, { text: displayName, query: query }) }), _jsx("span", { className: "shrink-0 rounded px-1 py-0.5 text-[0.6rem] bg-muted text-muted-foreground", children: repo.isPrivate ? "private" : "public" })] }));
|
|
235
332
|
}
|
|
236
333
|
// ---------------------------------------------------------------------------
|
|
237
334
|
// Loading skeleton
|