Function bodies 465 total
SmallDocIcon function · typescript · L303-L322 (20 LOC)src/components/FileIcons.tsx
function SmallDocIcon({ color }: { color: string }) {
return (
<svg width="16" height="16" viewBox="0 0 14 14" style={{ flexShrink: 0 }}>
<path
d="M3.5 1.5h4.5L11 4.5v8H3.5z"
stroke={color}
strokeWidth="0.9"
fill="none"
opacity={0.7}
/>
<path
d="M8 1.5v3h3"
stroke={color}
strokeWidth="0.9"
fill="none"
opacity={0.7}
/>
</svg>
);
}FlightCanvas function · typescript · L966-L1013 (48 LOC)src/components/FlightCanvas.tsx
export function FlightCanvas() {
const activeWorkspaceId = useWorkspaceStore((s) => s.activeWorkspaceId);
const hasActiveWorkspace = useWorkspaceStore(
(s) => !!s.activeWorkspaceId && s.workspaces.some((w) => w.id === s.activeWorkspaceId)
);
// Collect IDs of all workspaces that have flight layouts (or are active),
// as a stable string to avoid returning new arrays from the selector.
const mountedIdsString = useWorkspaceStore((s) => {
const wsIds = new Set(s.workspaces.map((w) => w.id));
const ids = new Set<string>();
for (const id of Object.keys(s.flightLayouts)) {
if (wsIds.has(id)) ids.add(id);
}
if (s.activeWorkspaceId && wsIds.has(s.activeWorkspaceId)) {
ids.add(s.activeWorkspaceId);
}
return Array.from(ids).join("\n");
});
const mountedIds = useMemo(
() => (mountedIdsString ? mountedIdsString.split("\n") : []),
[mountedIdsString]
);
if (!hasActiveWorkspace) {
return (
<div style={canvasStyles.empFlightHUD function · typescript · L19-L64 (46 LOC)src/components/FlightHUD.tsx
export function FlightHUD({ workspaceId, zoom, focusMode, onToggleFocus, autoFocus, onToggleAutoFocus }: FlightHUDProps) {
const addFlightPod = useWorkspaceStore((s) => s.addFlightPod);
const setFlightViewport = useWorkspaceStore((s) => s.setFlightViewport);
const updateFlightPod = useWorkspaceStore((s) => s.updateFlightPod);
const pods = useWorkspaceStore((s) => s.flightLayouts[workspaceId]?.pods);
const handleAddClaude = () => addFlightPod(workspaceId, "claude");
const handleAddTerminal = () => addFlightPod(workspaceId, "terminal");
const handleResetZoom = () => setFlightViewport(workspaceId, { zoom: 1.0 });
const handleResetSizes = () => {
if (!pods) return;
// In focus mode, cap width so ≥2 pods fit (same logic as navigateTo)
const container = document.querySelector("[data-flight-canvas]") as HTMLElement | null;
let maxW = Infinity;
if (focusMode && container) {
const rect = container.getBoundingClientRect();
const parentZoom = rectPodScriptDot function · typescript · L18-L148 (131 LOC)src/components/FlightPodFooter.tsx
function PodScriptDot({
repoPath,
scriptName,
scriptEntry,
}: {
repoPath: string;
scriptName: string;
scriptEntry: ScriptEntry | undefined;
}) {
const scriptRuns = useWorkspaceStore((s) => s.scriptRuns);
const runScript = useWorkspaceStore((s) => s.runScript);
const stopScript = useWorkspaceStore((s) => s.stopScript);
const clearScript = useWorkspaceStore((s) => s.clearScript);
const openStatusBarDrawer = useWorkspaceStore((s) => s.openStatusBarDrawer);
const statusBarDrawer = useWorkspaceStore((s) => s.statusBarDrawer);
const key = `${repoPath}:${scriptName}`;
const run = scriptRuns[key];
const isRunning = run?.status === "running";
const isWatcher = isWatcherScript(scriptName);
const command = scriptEntry?.command ?? scriptName;
const displayName = getDisplayName(scriptName);
const isDrawerOpen =
statusBarDrawer?.repoPath === repoPath &&
statusBarDrawer?.scriptName === scriptName;
let buildStatus: WatcherBuildStatus;
if (isWatcher)FlightPodFooter function · typescript · L151-L207 (57 LOC)src/components/FlightPodFooter.tsx
export function FlightPodFooter({ repoPath }: { repoPath: string }) {
const rallyConfig = useWorkspaceStore((s) => s.rallyConfigs[repoPath]);
const loadRallyConfig = useWorkspaceStore((s) => s.loadRallyConfig);
const [scriptCache, setScriptCache] = useState<ScriptEntry[]>([]);
useEffect(() => {
if (!rallyConfig) loadRallyConfig(repoPath);
api.listScripts(repoPath).then(setScriptCache).catch(() => {});
}, [repoPath, loadRallyConfig, rallyConfig]);
const scripts = rallyConfig?.statusBar ?? [];
if (scripts.length === 0) return null;
const repoName = repoPath.split("/").pop() ?? repoPath;
return (
<div
onContextMenu={(e) => e.preventDefault()}
style={{
height: 28,
display: "flex",
alignItems: "center",
gap: 0,
paddingLeft: 8,
paddingRight: 10,
paddingBottom: 2,
borderTop: "1px solid rgba(255, 255, 255, 0.06)",
flexShrink: 0,
overflow: "hidden",
userSelect: "snapToNeighbors function · typescript · L28-L88 (61 LOC)src/components/FlightPod.tsx
export function snapToNeighbors(
moving: SnapEdges,
others: SnapEdges[],
mode: "drag" | "resize",
): SnapEdges {
let { x, y, width, height } = moving;
const left = x;
const right = x + width;
const top = y;
const bottom = y + height;
let snappedX = false;
let snappedY = false;
for (const o of others) {
const oLeft = o.x;
const oRight = o.x + o.width;
const oTop = o.y;
const oBottom = o.y + o.height;
const gap = MIN_POD_GAP;
if (!snappedX) {
if (mode === "drag") {
// Snap with gap: right edge → other left edge (with gap between)
if (Math.abs(right + gap - oLeft) < SNAP_THRESHOLD) { x = oLeft - width - gap; snappedX = true; }
// Left edge → other right edge (with gap between)
else if (Math.abs(left - gap - oRight) < SNAP_THRESHOLD) { x = oRight + gap; snappedX = true; }
// Align left edges
else if (Math.abs(left - oLeft) < SNAP_THRESHOLD) { x = oLeft; snappedX = true; }
// AligpreventOverlap function · typescript · L96-L148 (53 LOC)src/components/FlightPod.tsx
export function preventOverlap(
moving: SnapEdges,
others: SnapEdges[],
rawPos?: { x: number; y: number },
): SnapEdges {
let { x, y } = moving;
const { width, height } = moving;
for (const o of others) {
// Check if they overlap (with MIN_POD_GAP margin)
const overlapX = x < o.x + o.width + MIN_POD_GAP && x + width + MIN_POD_GAP > o.x;
const overlapY = y < o.y + o.height + MIN_POD_GAP && y + height + MIN_POD_GAP > o.y;
if (overlapX && overlapY) {
// If we have the raw cursor position, check if the user is pushing
// deep enough into the overlap to signal they want stacking
if (rawPos) {
const rawOverlapX = rawPos.x < o.x + o.width + MIN_POD_GAP && rawPos.x + width + MIN_POD_GAP > o.x;
const rawOverlapY = rawPos.y < o.y + o.height + MIN_POD_GAP && rawPos.y + height + MIN_POD_GAP > o.y;
if (rawOverlapX && rawOverlapY) {
// Measure how deep the raw position is into the overlap zone
const depthRigWant fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
GitDiffContent function · typescript · L16-L256 (241 LOC)src/components/GitDiffOverlay.tsx
export function GitDiffContent({ rootPath }: GitDiffContentProps) {
const activeTab = useWorkspaceStore((s) => s.gitDiffActiveTab);
const setActiveTab = useWorkspaceStore((s) => s.setGitDiffActiveTab);
const scrollToFile = useWorkspaceStore((s) => s.gitDiffScrollToFile);
const gitStatus = useWorkspaceStore((s) => s.gitStatuses[rootPath]);
const mainBranch = useWorkspaceStore((s) => {
const ws = s.workspaces.find((w) => w.paths.includes(rootPath));
return ws?.main_branch ?? "main";
});
const {
unstagedFiles,
stagedFiles,
commits,
diffStatAdd,
diffStatDel,
loading,
fetchDiffs,
handleStage,
handleUnstage,
handleDiscard,
handleRevertAll,
revertConfirming,
handleStageAll,
handleUnstageAll,
handleCreatePr,
creatingPr,
unstagedCount,
stagedCount,
hasStaged,
hasPr,
createPrVisible,
} = useGitDiffActions({ rootPath, mainBranch });
const [commitModalOpen, setCommitModalOpen] = useStateMarkdownPreview function · typescript · L10-L34 (25 LOC)src/components/MarkdownPreview.tsx
function MarkdownPreview({ content, onScroll }, ref) {
const html = useMemo(() => renderMarkdown(content), [content]);
return (
<div
ref={ref}
style={styles.container}
onScroll={
onScroll
? (e) => {
const el = e.currentTarget;
onScroll(el.scrollTop, el.scrollHeight, el.clientHeight);
}
: undefined
}
>
<style>{markdownStyles}</style>
<div
className="md-body"
style={styles.body}
dangerouslySetInnerHTML={{ __html: html }}
/>
</div>
);
},paneHasActiveSession function · typescript · L28-L31 (4 LOC)src/components/PaneGroupView.tsx
function paneHasActiveSession(pane: Pane | undefined): boolean {
if (!pane) return false;
return !!(pane.ptyId && (pane.type === "claude" || pane.type === "terminal"));
}isClaudeCodeTitle function · typescript · L41-L44 (4 LOC)src/components/PaneGroupView.tsx
function isClaudeCodeTitle(title: string): boolean {
const lower = title.toLowerCase();
return lower === "claude" || lower.startsWith("claude ");
}paneLabel function · typescript · L46-L80 (35 LOC)src/components/PaneGroupView.tsx
function paneLabel(
pane: Pane,
isDirty: boolean,
workspacePath?: string,
): string {
// User-set custom title always takes priority for renamable pane types
if (
pane.customTitle &&
(pane.type === "terminal" ||
pane.type === "claude" ||
pane.type === "claude-launcher")
) {
return pane.customTitle;
}
if (pane.type === "claude-launcher") {
const cwd = pane.cwd || workspacePath || "";
return cwd.split("/").pop() || "Claude Code";
}
if (pane.type === "claude") {
const cwd = pane.cwd || workspacePath || "";
return cwd.split("/").pop() || "claude";
}
if (pane.type === "webview") return pane.title;
if (pane.type === "editor" || pane.type === "diff")
return isDirty ? `${pane.title} *` : pane.title;
if (pane.type === "terminal" && isClaudeCodeTitle(pane.title)) {
const cwd = pane.cwd || workspacePath || "";
return cwd.split("/").pop() || "claude";
}
if (pane.type === "terminal") {
const cwd = pane.cwd || wopaneTooltip function · typescript · L82-L89 (8 LOC)src/components/PaneGroupView.tsx
function paneTooltip(pane: Pane): string {
if (pane.type === "editor" && pane.filePath) return pane.filePath;
if (pane.type === "webview" && pane.webviewUrl) return pane.webviewUrl;
if (pane.type === "diff" && pane.cwd)
return pane.filePath ? `${pane.cwd}/${pane.filePath}` : pane.cwd;
if (pane.cwd) return pane.cwd;
return pane.title;
}startRename function · typescript · L174-L183 (10 LOC)src/components/PaneGroupView.tsx
function startRename(pane: Pane) {
if (
pane.type !== "terminal" &&
pane.type !== "claude" &&
pane.type !== "claude-launcher"
)
return;
setRenamingPaneId(pane.id);
setRenameValue(pane.customTitle || paneLabel(pane, false, workspacePath));
}commitRename function · typescript · L185-L191 (7 LOC)src/components/PaneGroupView.tsx
function commitRename(paneId: string) {
const trimmed = renameValue.trim();
if (trimmed) {
transformPane(workspaceId, groupId, paneId, { customTitle: trimmed });
}
setRenamingPaneId(null);
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
cancelRename function · typescript · L193-L195 (3 LOC)src/components/PaneGroupView.tsx
function cancelRename() {
setRenamingPaneId(null);
}handleAction function · typescript · L230-L243 (14 LOC)src/components/PaneGroupView.tsx
function handleAction(actionType: PendingAction["type"]) {
if (actionType === "terminal") {
window.dispatchEvent(
new CustomEvent<RequestNewTerminalCwdDetail>(
REQUEST_NEW_TERMINAL_CWD_EVENT,
{ detail: { workspaceId, groupId } },
),
);
return;
}
const activePane = group.panes.find((p) => p.id === activePaneId);
executeAction(actionType, activePane?.cwd || workspacePath);
}handleTabMouseDown function · typescript · L279-L407 (129 LOC)src/components/PaneGroupView.tsx
function handleTabMouseDown(e: React.MouseEvent, paneId: string) {
// Only left click
if (e.button !== 0) return;
// Don't start drag from close button
if ((e.target as HTMLElement).closest("[data-close]")) return;
e.preventDefault(); // Prevent text selection during drag
const startX = e.clientX;
const startY = e.clientY;
const tabsContainer = (e.currentTarget as HTMLElement).parentElement;
dragStartRef.current = { x: startX, y: startY, paneId };
let reordering = false;
let dropGap = -1;
// Drop indicator line (VS Code style)
const indicator = document.createElement("div");
indicator.style.cssText =
"position:fixed;width:2px;background:var(--text-primary);border-radius:1px;pointer-events:none;z-index:100;display:none;will-change:left;";
const onMouseMove = (ev: MouseEvent) => {
ev.preventDefault();
if (!dragStartRef.current) return;
const dx = ev.clientX - startX;
const dy = ev.clientY - stBreadcrumbBar function · typescript · L901-L974 (74 LOC)src/components/PaneGroupView.tsx
function BreadcrumbBar({
panes,
activePaneId,
workspacePaths,
}: {
panes: Pane[];
activePaneId: string;
workspacePaths: string[];
}) {
const activePane = panes.find((p) => p.id === activePaneId);
if (!activePane || activePane.type !== "editor" || !activePane.filePath)
return null;
const filePath = activePane.filePath;
// Find which workspace root this file belongs to
const matchingRoot = workspacePaths.find((p) => filePath.startsWith(p + "/"));
if (!matchingRoot) return null;
const rootName = matchingRoot.split("/").pop() ?? matchingRoot;
const relativePath = filePath.slice(matchingRoot.length + 1);
const segments = relativePath.split("/");
const isFile = (i: number) => i === segments.length - 1;
return (
<div style={bcStyles.bar}>
<svg
width="18"
height="13"
viewBox="0 0 18 14"
fill="none"
style={{ flexShrink: 0, position: "relative", top: 1 }}
>
<path
d="M1.5 3C1.5 2.1shouldCacheInactivePane function · typescript · L1009-L1012 (4 LOC)src/components/PaneGroupView.tsx
function shouldCacheInactivePane(pane: Pane | undefined): boolean {
if (!pane) return false;
return pane.type === "terminal" || pane.type === "claude" || pane.type === "webview";
}handleFocusGroup function · typescript · L1074-L1084 (11 LOC)src/components/PaneGroupView.tsx
function handleFocusGroup(e: Event) {
const targetId = (e as CustomEvent).detail;
if (targetId === groupId && contentRef.current) {
requestAnimationFrame(() => {
const textarea = contentRef.current?.querySelector(
"textarea.xterm-helper-textarea",
) as HTMLTextAreaElement | null;
textarea?.focus();
});
}
}PaneLayout function · typescript · L52-L100 (49 LOC)src/components/PaneLayout.tsx
export function PaneLayout() {
const activeWorkspaceId = useWorkspaceStore((s) => s.activeWorkspaceId);
const hasActiveWorkspace = useWorkspaceStore(
(s) => !!s.activeWorkspaceId && s.workspaces.some((w) => w.id === s.activeWorkspaceId)
);
// Return a stable string (joined IDs) so Zustand's Object.is check prevents
// re-renders when the set of mounted workspaces hasn't actually changed.
// See PITFALLS.md — returning a new array from a selector causes render loops.
const mountedIdsString = useWorkspaceStore((s) => {
const wsIds = new Set(s.workspaces.map((w) => w.id));
const ids = new Set<string>();
for (const id of Object.keys(s.layouts)) {
if (wsIds.has(id)) ids.add(id);
}
if (s.activeWorkspaceId && wsIds.has(s.activeWorkspaceId)) {
ids.add(s.activeWorkspaceId);
}
return Array.from(ids).join("\n");
});
const mountedWorkspaceIds = useMemo(
() => (mountedIdsString ? mountedIdsString.split("\n") : []),
[mountedIdsStPortPill function · typescript · L9-L26 (18 LOC)src/components/PortPill.tsx
export function PortPill({ port: p, onClick }: PortPillProps) {
return (
<span
key={p.port}
onMouseDown={(e) => {
e.stopPropagation();
}}
onClick={(e) => {
e.stopPropagation();
onClick(p.url);
}}
title={`Open ${p.url}`}
style={pillStyle}
>
:{p.port}
</span>
);
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
shortenPath function · typescript · L18-L20 (3 LOC)src/components/ProductChatPanel.tsx
function shortenPath(p: string): string {
return p.replace(/^\/Users\/[^/]+/, "~");
}buildFileTree function · typescript · L26-L75 (50 LOC)src/components/PrReviewOverlay.tsx
function buildFileTree(files: DiffFile[]): FileTreeNode[] {
const root: FileTreeNode[] = [];
for (const file of files) {
const fp = file.newPath || file.oldPath;
const parts = fp.split("/");
let current = root;
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const pathSoFar = parts.slice(0, i + 1).join("/");
const isLeaf = i === parts.length - 1;
let existing = current.find((n) => n.name === part && n.path === pathSoFar);
if (!existing) {
existing = { name: part, path: pathSoFar, children: [] };
if (isLeaf) existing.file = file;
current.push(existing);
}
current = existing.children;
}
}
// Collapse single-child directories: src/components → src/components
function collapse(nodes: FileTreeNode[]): FileTreeNode[] {
return nodes.map((node) => {
node.children = collapse(node.children);
if (node.children.length === 1 && !node.file && !node.children[0].file) {
collapse function · typescript · L50-L59 (10 LOC)src/components/PrReviewOverlay.tsx
function collapse(nodes: FileTreeNode[]): FileTreeNode[] {
return nodes.map((node) => {
node.children = collapse(node.children);
if (node.children.length === 1 && !node.file && !node.children[0].file) {
const child = node.children[0];
return { ...child, name: `${node.name}/${child.name}` };
}
return node;
});
}sortTree function · typescript · L62-L72 (11 LOC)src/components/PrReviewOverlay.tsx
function sortTree(nodes: FileTreeNode[]): FileTreeNode[] {
nodes.sort((a, b) => {
const aIsDir = a.children.length > 0 || !a.file;
const bIsDir = b.children.length > 0 || !b.file;
if (aIsDir && !bIsDir) return -1;
if (!aIsDir && bIsDir) return 1;
return a.name.localeCompare(b.name);
});
for (const n of nodes) sortTree(n.children);
return nodes;
}buildTimeline function · typescript · L82-L95 (14 LOC)src/components/PrReviewOverlay.tsx
function buildTimeline(comments: PrComment[], reviews: PrReview[]): TimelineItem[] {
const items: TimelineItem[] = [
...comments.map((c) => ({ kind: "comment" as const, data: c })),
...reviews
.filter((r) => r.body.trim().length > 0 || r.state !== "COMMENTED")
.map((r) => ({ kind: "review" as const, data: r })),
];
items.sort(
(a, b) =>
new Date(a.data.created_at).getTime() -
new Date(b.data.created_at).getTime(),
);
return items;
}getStatusColor function · typescript · L759-L764 (6 LOC)src/components/PrReviewOverlay.tsx
function getStatusColor(file: DiffFile): string {
return file.isNew ? statusColors.new
: file.isDeleted ? statusColors.deleted
: file.isRenamed ? statusColors.renamed
: statusColors.modified;
}getStatusLetter function · typescript · L765-L767 (3 LOC)src/components/PrReviewOverlay.tsx
function getStatusLetter(file: DiffFile): string {
return file.isNew ? "A" : file.isDeleted ? "D" : file.isRenamed ? "R" : "M";
}ConversationTab function · typescript · L857-L951 (95 LOC)src/components/PrReviewOverlay.tsx
function ConversationTab({
details,
loading,
timeline,
bodyHtml,
}: {
details: PrDetails | null;
loading: boolean;
timeline: TimelineItem[];
bodyHtml: string | null;
}) {
if (loading) {
return <div style={st.empty}>Loading...</div>;
}
if (!details) {
return <div style={st.empty}>No PR data</div>;
}
const hasTimeline = timeline.length > 0;
if (!bodyHtml && !hasTimeline) {
return (
<div style={st.empty}>
No description or comments
</div>
);
}
return (
<div style={st.conversationList}>
{/* PR description rendered as markdown */}
{bodyHtml && (
<div style={st.conversationCard}>
<div style={st.conversationHeader}>
<span style={st.conversationAuthor}>{details.author}</span>
<span style={st.conversationLabel}>author</span>
{details.created_at && (
<span style={st.conversationDate}>{relativeTime(details.created_at)}</span>
)}
All rows scored by the Repobility analyzer (https://repobility.com)
fuzzyMatch function · typescript · L37-L62 (26 LOC)src/components/QuickOpen.tsx
function fuzzyMatch(query: string, target: string): FuzzyResult | null {
const lowerQuery = query.toLowerCase();
const lowerTarget = target.toLowerCase();
const indices: number[] = [];
let qi = 0;
let score = 0;
let lastMatchIndex = -1;
for (let ti = 0; ti < target.length && qi < lowerQuery.length; ti++) {
if (lowerTarget[ti] === lowerQuery[qi]) {
indices.push(ti);
if (lastMatchIndex === ti - 1) score += 10;
if (ti === 0 || "/\\-_ .".includes(target[ti - 1])) score += 5;
if (target[ti] === query[qi]) score += 3;
if (lastMatchIndex >= 0) {
const gap = ti - lastMatchIndex - 1;
if (gap > 0) score -= gap;
}
lastMatchIndex = ti;
qi++;
}
}
if (qi < lowerQuery.length) return null;
return { score, indices };
}baseName function · typescript · L64-L67 (4 LOC)src/components/QuickOpen.tsx
function baseName(path: string): string {
const i = path.lastIndexOf("/");
return i >= 0 ? path.slice(i + 1) : path;
}parentDir function · typescript · L69-L72 (4 LOC)src/components/QuickOpen.tsx
function parentDir(path: string): string {
const i = path.lastIndexOf("/");
return i > 0 ? path.slice(0, i) : path;
}HighlightedText function · typescript · L76-L109 (34 LOC)src/components/QuickOpen.tsx
function HighlightedText({
text,
indices,
}: {
text: string;
indices: Set<number>;
}) {
const spans: React.ReactNode[] = [];
let run = "";
let runHighlighted = false;
for (let i = 0; i <= text.length; i++) {
const isHighlighted = indices.has(i);
if (i === text.length || isHighlighted !== runHighlighted) {
if (run) {
spans.push(
runHighlighted ? (
<span key={i} style={{ color: "#2aaaff", fontWeight: 700 }}>
{run}
</span>
) : (
<span key={i}>{run}</span>
),
);
}
run = i < text.length ? text[i] : "";
runHighlighted = isHighlighted;
} else {
run += text[i];
}
}
return <>{spans}</>;
}FolderRowIcon function · typescript · L111-L122 (12 LOC)src/components/QuickOpen.tsx
function FolderRowIcon() {
return (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" style={{ flexShrink: 0 }}>
<path
d="M2 4.5C2 3.67 2.67 3 3.5 3H6.4L7.5 4.3H12.5C13.33 4.3 14 4.97 14 5.8V11.5C14 12.33 13.33 13 12.5 13H3.5C2.67 13 2 12.33 2 11.5V4.5Z"
stroke="#8aa9d6"
strokeWidth="1.2"
strokeLinejoin="round"
/>
</svg>
);
}RepoSwitcher function · typescript · L10-L131 (122 LOC)src/components/RepoSwitcher.tsx
export function RepoSwitcher({ workspaceId, rootPath }: RepoSwitcherProps) {
const [open, setOpen] = useState(false);
const [dropdownPos, setDropdownPos] = useState<{ top: number; left: number } | null>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
const workspace = useWorkspaceStore((s) => s.workspaces.find((w) => w.id === workspaceId));
const setActivePathIndex = useWorkspaceStore((s) => s.setActivePathIndex);
const refreshGitStatusForPath = useWorkspaceStore((s) => s.refreshGitStatusForPath);
const paths = workspace?.paths ?? [];
const mainBranch = workspace?.main_branch ?? "main";
const currentBasename = rootPath.split("/").pop() || rootPath;
const openDropdown = useCallback(() => {
if (buttonRef.current) {
const rect = buttonRef.current.getBoundingClientRect();
setDropdownPos({ top: rect.bottom + 4, left: rect.left });
}
setOpen(true);
}, []);
const handleSelect = uscollectPeerHandles function · typescript · L25-L78 (54 LOC)src/components/ResizeHandle.tsx
function collectPeerHandles(handle: HTMLElement, direction: SplitDirection): PeerHandle[] {
const parent = handle.parentElement;
if (!parent) return [];
const grandparent = parent.parentElement;
if (!grandparent) return [];
const isVertical = direction === "vertical";
const peers: PeerHandle[] = [];
const greatGrandparent = grandparent.parentElement;
if (!greatGrandparent) return peers;
const siblingContainers = greatGrandparent.children;
for (let i = 0; i < siblingContainers.length; i++) {
const sibling = siblingContainers[i] as HTMLElement;
if (sibling === grandparent) continue;
const handles = sibling.querySelectorAll<HTMLElement>('[style*="cursor"]');
for (const peerHandle of handles) {
const cursor = peerHandle.style.cursor;
const isPeerSameDirection =
(isVertical && cursor === "row-resize") ||
(!isVertical && cursor === "col-resize");
if (!isPeerSameDirection) continue;
const peerParent = peerHandleResizeHandle function · typescript · L80-L299 (220 LOC)src/components/ResizeHandle.tsx
export function ResizeHandle({ direction, ratio, onResize }: ResizeHandleProps) {
const handleRef = useRef<HTMLDivElement>(null);
const lineRef = useRef<HTMLDivElement>(null);
const dragging = useRef(false);
const onResizeRef = useRef(onResize);
onResizeRef.current = onResize;
const ratioRef = useRef(ratio);
ratioRef.current = ratio;
const isRowHandle = direction === "vertical";
const resetLine = useCallback(() => {
if (lineRef.current) lineRef.current.style.background = "var(--border)";
}, []);
const highlightLine = useCallback(() => {
if (lineRef.current) lineRef.current.style.background = "var(--resize-hover)";
}, []);
const onMouseDown = useCallback(
(e: React.MouseEvent) => {
e.preventDefault();
dragging.current = true;
const handle = handleRef.current;
if (!handle) return;
const parent = handle.parentElement;
if (!parent) return;
const rect = parent.getBoundingClientRect();
const handleRecWant fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
ScriptEditor function · typescript · L6-L81 (76 LOC)src/components/ScriptEditor.tsx
export function ScriptEditor() {
const [scripts, setScripts] = useState<RallyScriptInfo[]>([]);
const [loading, setLoading] = useState(true);
const activeWorkspaceId = useWorkspaceStore((s) => s.activeWorkspaceId);
const openFile = useWorkspaceStore((s) => s.openFile);
const refresh = async () => {
try {
const result = await api.listRallyScripts();
setScripts(result);
} catch (e) {
console.error("Failed to list rally scripts:", e);
} finally {
setLoading(false);
}
};
useEffect(() => {
refresh();
}, []);
const handleOpen = (script: RallyScriptInfo) => {
if (!activeWorkspaceId) return;
openFile(activeWorkspaceId, script.path);
};
const handleRestore = async (script: RallyScriptInfo) => {
try {
await api.restoreRallyScript(script.name);
await refresh();
} catch (e) {
console.error("Failed to restore script:", e);
}
};
const cliScripts = scripts.filter((s) => s.category === "scrSection function · typescript · L83-L97 (15 LOC)src/components/ScriptEditor.tsx
function Section({ title, subtitle, children }: {
title: string;
subtitle: string;
children: React.ReactNode;
}) {
return (
<div style={styles.section}>
<div style={styles.sectionHeader}>
<span style={styles.sectionTitle}>{title}</span>
<span style={styles.sectionSubtitle}>{subtitle}</span>
</div>
{children}
</div>
);
}basename function · typescript · L30-L34 (5 LOC)src/components/SearchPanel.tsx
function basename(path: string): string {
const trimmed = path.replace(/\/+$/, "");
const idx = trimmed.lastIndexOf("/");
return idx >= 0 ? trimmed.slice(idx + 1) : trimmed;
}dirname function · typescript · L36-L40 (5 LOC)src/components/SearchPanel.tsx
function dirname(path: string): string {
const trimmed = path.replace(/\/+$/, "");
const idx = trimmed.lastIndexOf("/");
return idx >= 0 ? trimmed.slice(0, idx) : "";
}findRepoForFile function · typescript · L42-L49 (8 LOC)src/components/SearchPanel.tsx
function findRepoForFile(filePath: string, sortedRoots: string[]): string {
for (const root of sortedRoots) {
if (filePath === root || filePath.startsWith(`${root}/`)) {
return root;
}
}
return dirname(filePath) || filePath;
}defaultSearchState function · typescript · L65-L79 (15 LOC)src/components/SearchPanel.tsx
function defaultSearchState(): PerWorkspaceSearchState {
return {
query: "",
caseSensitive: false,
wholeWord: false,
useRegex: false,
results: [],
hasSearched: false,
collapsedFiles: new Set(),
replaceOpen: false,
replaceValue: "",
preserveCase: false,
collapsedRepos: new Set(),
};
}dirname function · typescript · L22-L25 (4 LOC)src/components/SettingsPanel.tsx
function dirname(path: string): string {
const idx = path.lastIndexOf("/");
return idx > 0 ? path.slice(0, idx) : "/";
}GlobalConfigExplorer function · typescript · L226-L395 (170 LOC)src/components/SettingsPanel.tsx
export function GlobalConfigExplorer() {
const activeWorkspaceId = useWorkspaceStore((s) => s.activeWorkspaceId);
const openFile = useWorkspaceStore((s) => s.openFile);
const [claudeDir, setClaudeDir] = useState<string | null>(null);
const [loaded, setLoaded] = useState(false);
const [entries, setEntries] = useState<FileEntry[]>([]);
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set());
const [newItem, setNewItem] = useState<{
parentPath: string;
kind: "file" | "dir";
} | null>(null);
// Resolve ~/.claude/ directory on mount
useEffect(() => {
let cancelled = false;
invoke<ConfigFile[]>("list_claude_configs", { workspacePath: null })
.then((files) => {
if (cancelled) return;
const global = files.find(
(f) => f.file_type === "claude-md" && f.path.endsWith("/.claude/CLAUDE.md"),
);
const dir = global ? dirname(global.path) : null;
setClaudeDir(dir);
if (dir) {
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
getDisplayState function · typescript · L25-L87 (63 LOC)src/components/ShipStatusPill.tsx
function getDisplayState(session: ShipSession, livePrUrl: string | null) {
const hasSignal = !!session.signal;
const isManualReview = hasSignal && session.signal!.verdict === "manual_review";
const isAutoMerge = hasSignal && session.signal!.verdict === "auto_merge";
const hasError = session.exited && session.exitCode !== null && session.exitCode !== 0;
const isFinishing = session.exited && !hasSignal && !hasError;
if (isManualReview) {
return {
title: `Review Needed — PR #${session.signal!.pr_number}`,
subtitle: session.signal!.summary || "Manual review required",
extra: session.signal!.flagged_items?.length
? `${session.signal!.flagged_items.length} flagged item${session.signal!.flagged_items.length !== 1 ? "s" : ""}`
: null,
accentColor: "#f59e0b",
titleColor: "#e8b930",
prUrl: session.signal!.pr_url,
};
}
if (isAutoMerge) {
return {
title: `Merging PR #${session.signal!.pr_number}`,
subtitleShipStatusPill function · typescript · L89-L214 (126 LOC)src/components/ShipStatusPill.tsx
export function ShipStatusPill() {
const session = useWorkspaceStore((s) => s.shipSession);
const activeWorkspaceId = useWorkspaceStore((s) => s.activeWorkspaceId);
const prStatuses = useWorkspaceStore((s) => s.prStatuses);
const dismissShipSession = useWorkspaceStore((s) => s.dismissShipSession);
const dockShipSession = useWorkspaceStore((s) => s.dockShipSession);
const [expanded, setExpanded] = useState(false);
const panelRef = useRef<HTMLDivElement>(null);
// Close on outside click when expanded
useEffect(() => {
if (!expanded) return;
function handleClick(e: MouseEvent) {
if (panelRef.current && !panelRef.current.contains(e.target as Node)) {
setExpanded(false);
}
}
const timer = setTimeout(() => {
document.addEventListener("mousedown", handleClick);
}, 50);
return () => {
clearTimeout(timer);
document.removeEventListener("mousedown", handleClick);
};
}, [expanded]);
if (!session) return null;handleClick function · typescript · L101-L105 (5 LOC)src/components/ShipStatusPill.tsx
function handleClick(e: MouseEvent) {
if (panelRef.current && !panelRef.current.contains(e.target as Node)) {
setExpanded(false);
}
}