@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.
Files changed (160) hide show
  1. package/deployment-mode.d.ts +35 -0
  2. package/deployment-mode.d.ts.map +1 -0
  3. package/deployment-mode.js +41 -0
  4. package/deployment-mode.js.map +1 -0
  5. package/execution/ApprovalCard.d.ts +8 -6
  6. package/execution/ApprovalCard.d.ts.map +1 -1
  7. package/execution/ApprovalCard.js +34 -96
  8. package/execution/ApprovalCard.js.map +1 -1
  9. package/execution/ArtifactCard.d.ts +11 -1
  10. package/execution/ArtifactCard.d.ts.map +1 -1
  11. package/execution/ArtifactCard.js +22 -3
  12. package/execution/ArtifactCard.js.map +1 -1
  13. package/execution/ArtifactPreviewModal.d.ts.map +1 -1
  14. package/execution/ArtifactPreviewModal.js +1 -1
  15. package/execution/ArtifactPreviewModal.js.map +1 -1
  16. package/execution/ArtifactsWidget.d.ts +26 -19
  17. package/execution/ArtifactsWidget.d.ts.map +1 -1
  18. package/execution/ArtifactsWidget.js +24 -26
  19. package/execution/ArtifactsWidget.js.map +1 -1
  20. package/execution/McpToolDetail.d.ts +48 -0
  21. package/execution/McpToolDetail.d.ts.map +1 -0
  22. package/execution/McpToolDetail.js +159 -0
  23. package/execution/McpToolDetail.js.map +1 -0
  24. package/execution/MessageThread.d.ts +10 -1
  25. package/execution/MessageThread.d.ts.map +1 -1
  26. package/execution/MessageThread.js +19 -17
  27. package/execution/MessageThread.js.map +1 -1
  28. package/execution/SandboxContext.d.ts +32 -0
  29. package/execution/SandboxContext.d.ts.map +1 -0
  30. package/execution/SandboxContext.js +26 -0
  31. package/execution/SandboxContext.js.map +1 -0
  32. package/execution/ToolArgsView.d.ts +41 -0
  33. package/execution/ToolArgsView.d.ts.map +1 -0
  34. package/execution/ToolArgsView.js +134 -0
  35. package/execution/ToolArgsView.js.map +1 -0
  36. package/execution/ToolCallDetail.d.ts +11 -4
  37. package/execution/ToolCallDetail.d.ts.map +1 -1
  38. package/execution/ToolCallDetail.js +32 -101
  39. package/execution/ToolCallDetail.js.map +1 -1
  40. package/execution/ToolCallGroup.d.ts.map +1 -1
  41. package/execution/ToolCallGroup.js +3 -2
  42. package/execution/ToolCallGroup.js.map +1 -1
  43. package/execution/ToolCallItem.d.ts +2 -0
  44. package/execution/ToolCallItem.d.ts.map +1 -1
  45. package/execution/ToolCallItem.js +13 -3
  46. package/execution/ToolCallItem.js.map +1 -1
  47. package/execution/WriteBackCard.d.ts +34 -0
  48. package/execution/WriteBackCard.d.ts.map +1 -0
  49. package/execution/WriteBackCard.js +75 -0
  50. package/execution/WriteBackCard.js.map +1 -0
  51. package/execution/WriteBacksWidget.d.ts +49 -0
  52. package/execution/WriteBacksWidget.d.ts.map +1 -0
  53. package/execution/WriteBacksWidget.js +44 -0
  54. package/execution/WriteBacksWidget.js.map +1 -0
  55. package/execution/__tests__/file-path-resolver.test.d.ts +2 -0
  56. package/execution/__tests__/file-path-resolver.test.d.ts.map +1 -0
  57. package/execution/__tests__/file-path-resolver.test.js +180 -0
  58. package/execution/__tests__/file-path-resolver.test.js.map +1 -0
  59. package/execution/file-path-resolver.d.ts +3 -3
  60. package/execution/file-path-resolver.d.ts.map +1 -1
  61. package/execution/file-path-resolver.js +23 -12
  62. package/execution/file-path-resolver.js.map +1 -1
  63. package/execution/index.d.ts +16 -1
  64. package/execution/index.d.ts.map +1 -1
  65. package/execution/index.js +9 -1
  66. package/execution/index.js.map +1 -1
  67. package/execution/sandbox-path-normalizer.d.ts +46 -0
  68. package/execution/sandbox-path-normalizer.d.ts.map +1 -0
  69. package/execution/sandbox-path-normalizer.js +73 -0
  70. package/execution/sandbox-path-normalizer.js.map +1 -0
  71. package/execution/tool-categories.d.ts +35 -8
  72. package/execution/tool-categories.d.ts.map +1 -1
  73. package/execution/tool-categories.js +76 -10
  74. package/execution/tool-categories.js.map +1 -1
  75. package/execution/tool-rendering-primitives.d.ts +61 -0
  76. package/execution/tool-rendering-primitives.d.ts.map +1 -0
  77. package/execution/tool-rendering-primitives.js +106 -0
  78. package/execution/tool-rendering-primitives.js.map +1 -0
  79. package/execution/useArtifactContent.d.ts +5 -1
  80. package/execution/useArtifactContent.d.ts.map +1 -1
  81. package/execution/useArtifactContent.js +6 -2
  82. package/execution/useArtifactContent.js.map +1 -1
  83. package/execution/useWorkspaceWriteBacks.d.ts +40 -0
  84. package/execution/useWorkspaceWriteBacks.d.ts.map +1 -0
  85. package/execution/useWorkspaceWriteBacks.js +41 -0
  86. package/execution/useWorkspaceWriteBacks.js.map +1 -0
  87. package/github/GitHubRepoPicker.d.ts +5 -2
  88. package/github/GitHubRepoPicker.d.ts.map +1 -1
  89. package/github/GitHubRepoPicker.js +133 -36
  90. package/github/GitHubRepoPicker.js.map +1 -1
  91. package/github/index.d.ts +1 -0
  92. package/github/index.d.ts.map +1 -1
  93. package/github/index.js +1 -0
  94. package/github/index.js.map +1 -1
  95. package/github/useGitHubSearch.d.ts +20 -0
  96. package/github/useGitHubSearch.d.ts.map +1 -0
  97. package/github/useGitHubSearch.js +127 -0
  98. package/github/useGitHubSearch.js.map +1 -0
  99. package/index.d.ts +9 -6
  100. package/index.d.ts.map +1 -1
  101. package/index.js +7 -3
  102. package/index.js.map +1 -1
  103. package/internal/CloudFeatureNotice.d.ts +19 -0
  104. package/internal/CloudFeatureNotice.d.ts.map +1 -0
  105. package/internal/CloudFeatureNotice.js +21 -0
  106. package/internal/CloudFeatureNotice.js.map +1 -0
  107. package/mcp-server/McpServerDetailView.d.ts +15 -1
  108. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  109. package/mcp-server/McpServerDetailView.js +11 -3
  110. package/mcp-server/McpServerDetailView.js.map +1 -1
  111. package/package.json +4 -4
  112. package/provider.d.ts +14 -2
  113. package/provider.d.ts.map +1 -1
  114. package/provider.js +3 -2
  115. package/provider.js.map +1 -1
  116. package/session/index.d.ts +4 -0
  117. package/session/index.d.ts.map +1 -1
  118. package/session/index.js +2 -0
  119. package/session/index.js.map +1 -1
  120. package/session/useSessionArtifacts.d.ts +73 -0
  121. package/session/useSessionArtifacts.d.ts.map +1 -0
  122. package/session/useSessionArtifacts.js +95 -0
  123. package/session/useSessionArtifacts.js.map +1 -0
  124. package/session/useSessionWriteBacks.d.ts +56 -0
  125. package/session/useSessionWriteBacks.d.ts.map +1 -0
  126. package/session/useSessionWriteBacks.js +56 -0
  127. package/session/useSessionWriteBacks.js.map +1 -0
  128. package/src/deployment-mode.ts +46 -0
  129. package/src/execution/ApprovalCard.tsx +130 -283
  130. package/src/execution/ArtifactCard.tsx +40 -0
  131. package/src/execution/ArtifactPreviewModal.tsx +2 -0
  132. package/src/execution/ArtifactsWidget.tsx +51 -43
  133. package/src/execution/McpToolDetail.tsx +283 -0
  134. package/src/execution/MessageThread.tsx +18 -0
  135. package/src/execution/SandboxContext.ts +47 -0
  136. package/src/execution/ToolArgsView.tsx +279 -0
  137. package/src/execution/ToolCallDetail.tsx +54 -220
  138. package/src/execution/ToolCallGroup.tsx +3 -2
  139. package/src/execution/ToolCallItem.tsx +21 -3
  140. package/src/execution/WriteBackCard.tsx +210 -0
  141. package/src/execution/WriteBacksWidget.tsx +82 -0
  142. package/src/execution/__tests__/file-path-resolver.test.ts +295 -0
  143. package/src/execution/file-path-resolver.ts +24 -12
  144. package/src/execution/index.ts +38 -0
  145. package/src/execution/sandbox-path-normalizer.ts +80 -0
  146. package/src/execution/tool-categories.ts +89 -9
  147. package/src/execution/tool-rendering-primitives.tsx +253 -0
  148. package/src/execution/useArtifactContent.ts +6 -1
  149. package/src/execution/useWorkspaceWriteBacks.ts +56 -0
  150. package/src/github/GitHubRepoPicker.tsx +413 -108
  151. package/src/github/index.ts +5 -0
  152. package/src/github/useGitHubSearch.ts +162 -0
  153. package/src/index.ts +27 -0
  154. package/src/internal/CloudFeatureNotice.tsx +60 -0
  155. package/src/mcp-server/McpServerDetailView.tsx +24 -2
  156. package/src/provider.tsx +18 -2
  157. package/src/session/index.ts +12 -0
  158. package/src/session/useSessionArtifacts.ts +143 -0
  159. package/src/session/useSessionWriteBacks.ts +94 -0
  160. 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GACxB,wBAAwB,CAiE1B"}
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":"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"}
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
- * - Owner-grouped sections (personal repos first, then orgs)
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 300px max-height with scroll shadow indicators
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":"AAYA,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;AAuHD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE,qBAAqB,2CAoWvB"}
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
- * - Owner-grouped sections (personal repos first, then orgs)
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 300px max-height with scroll shadow indicators
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 { repos, isLoading, isBackgroundLoading, error, search, setSearch, fetchBranches, } = useGitHubRepos(token);
87
- // Branch selection state
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
- // Group and flatten repos for rendering + keyboard nav
102
- const groups = useMemo(() => groupRepos(repos, recentRepos), [repos, recentRepos]);
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
- }, [search]);
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
- }, [repos, updateScrollShadows]);
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
- }, [fetchBranches]);
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
- // Combobox keyboard handler all keyboard interaction goes through the
172
- // search input so focus never leaves it.
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 (search) {
195
- setSearch("");
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, search, setSearch]);
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
- // --- Compute flat index offsets per group for rendering ---
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: [_jsx("input", { ref: searchRef, type: "text", role: "combobox", "aria-expanded": true, "aria-controls": LIST_ID, "aria-activedescendant": focusIndex >= 0 ? `stgm-repo-${focusIndex}` : undefined, placeholder: "Search repositories...", value: search, onChange: (e) => setSearch(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 }), error && (_jsx("p", { className: "text-xs text-destructive", children: error })), _jsxs("div", { className: "relative", children: [canScrollUp && (_jsx("div", { className: "absolute inset-x-0 top-0 h-3 z-10 pointer-events-none", style: {
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: isLoading ? (_jsx(LoadingSkeleton, {})) : flatItems.length === 0 ? (_jsx("div", { className: "py-4 text-center text-xs text-muted-foreground", children: search
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