testeranto
Version:
the AI powered BDD test framework for typescript projects
384 lines (383 loc) • 26.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.GitIntegrationView = void 0;
/* eslint-disable @typescript-eslint/no-unused-vars */
const react_1 = __importStar(require("react"));
const react_bootstrap_1 = require("react-bootstrap");
const react_2 = require("@monaco-editor/react");
const GitIntegrationView = ({ mode, setMode, fileService, remoteStatus, changes, currentBranch, isLoading, setError, loadChanges, loadGitStatus, error, getStatusBadgeVariant, commitSummary, setCommitSummary, commitDescription, setCommitDescription, handleSaveChanges, isCommitting, handleShareChanges, getSyncStatusVariant, handleGetUpdates, isPulling, isPushing, setIsPushing, getSyncStatusText, }) => {
const [selectedFile, setSelectedFile] = (0, react_1.useState)(null);
const [originalContent, setOriginalContent] = (0, react_1.useState)("");
const [modifiedContent, setModifiedContent] = (0, react_1.useState)("");
const [isDiffLoading, setIsDiffLoading] = (0, react_1.useState)(false);
// Load diff when a file is selected
const loadFileDiff = (0, react_1.useCallback)(async (filePath) => {
console.log("loadFileDiff - filePath:", filePath);
if (mode === "static")
return;
setIsDiffLoading(true);
try {
setSelectedFile(filePath);
try {
const currentContent = await fileService.readFile(filePath);
setModifiedContent(currentContent);
}
catch (error) {
console.error("Error reading file:", error);
setModifiedContent("");
setError(`Cannot read file: ${filePath}. The development server may not be running or the file API endpoints may not be implemented. Check that the server is running and has the /api/files/read endpoint.`);
}
setOriginalContent("");
}
catch (error) {
console.error("Failed to load file diff:", error);
setError("Failed to load file content for diff");
}
finally {
setIsDiffLoading(false);
}
}, [fileService, mode, setError]);
// Auto-select first file when changes load
(0, react_1.useEffect)(() => {
if (changes.length > 0 && !selectedFile) {
console.log("First change path:", changes[0].path);
loadFileDiff(changes[0].path);
}
}, [changes, selectedFile, loadFileDiff]);
// const loadChanges = async (event?: React.MouseEvent) => {
// // Prevent default behavior if event exists
// if (event) {
// event.preventDefault();
// }
// try {
// setIsLoading(true);
// setError(null);
// const changes = await fileService.getChanges();
// setChanges(changes);
// } catch (err) {
// const errorMessage =
// err instanceof Error ? err.message : "Failed to load changes";
// console.error("Failed to load changes:", err);
// setError(errorMessage);
// } finally {
// setIsLoading(false);
// }
// };
// const loadGitStatus = async (event?: React.MouseEvent) => {
// // Prevent default behavior if event exists
// if (event) {
// event.preventDefault();
// }
// try {
// setError(null);
// const branch = await fileService.getCurrentBranch();
// const status = await fileService.getRemoteStatus();
// setCurrentBranch(branch);
// setRemoteStatus(status);
// } catch (err) {
// const errorMessage =
// err instanceof Error ? err.message : "Failed to load git status";
// console.error("Failed to load git status:", err);
// setError(errorMessage);
// }
// };
// const handleSaveChanges = async () => {
// if (!commitSummary.trim()) {
// setError("Please provide a commit summary");
// return;
// }
// try {
// setIsCommitting(true);
// setError(null);
// await fileService.commitChanges(commitSummary, commitDescription);
// setCommitSummary("");
// setCommitDescription("");
// await loadChanges();
// await loadGitStatus();
// } catch (err) {
// const errorMessage =
// err instanceof Error ? err.message : "Failed to commit changes";
// console.error("Failed to commit changes:", err);
// setError(errorMessage);
// } finally {
// setIsCommitting(false);
// }
// };
// const handleShareChanges = async () => {
// if (!commitSummary.trim()) {
// setError("Please provide a commit summary");
// return;
// }
// try {
// setIsCommitting(true);
// setIsPushing(true);
// setError(null);
// await fileService.commitChanges(commitSummary, commitDescription);
// await fileService.pushChanges();
// setCommitSummary("");
// setCommitDescription("");
// await loadChanges();
// await loadGitStatus();
// } catch (err) {
// const errorMessage =
// err instanceof Error ? err.message : "Failed to share changes";
// console.error("Failed to share changes:", err);
// setError(errorMessage);
// } finally {
// setIsCommitting(false);
// setIsPushing(false);
// }
// };
// const handleGetUpdates = async () => {
// try {
// setIsPulling(true);
// setError(null);
// await fileService.pullChanges();
// await loadChanges();
// await loadGitStatus();
// } catch (err) {
// const errorMessage =
// err instanceof Error ? err.message : "Failed to get updates";
// console.error("Failed to get updates:", err);
// setError(errorMessage);
// } finally {
// setIsPulling(false);
// }
// };
// const getStatusBadgeVariant = (status: string) => {
// switch (status) {
// case "modified":
// return "warning";
// case "added":
// return "success";
// case "deleted":
// return "danger";
// case "conflicted":
// return "danger";
// default:
// return "secondary";
// }
// };
// const getSyncStatusText = () => {
// if (remoteStatus.ahead > 0 && remoteStatus.behind > 0) {
// return `${remoteStatus.ahead} ahead, ${remoteStatus.behind} behind`;
// } else if (remoteStatus.ahead > 0) {
// return `${remoteStatus.ahead} ahead`;
// } else if (remoteStatus.behind > 0) {
// return `${remoteStatus.behind} behind`;
// } else {
// return "Up to date";
// }
// };
// const getSyncStatusVariant = () => {
// if (remoteStatus.behind > 0) return "warning";
// if (remoteStatus.ahead > 0) return "info";
// return "success";
// };
// if (error) console.error(error);
return (react_1.default.createElement(react_bootstrap_1.Container, { fluid: true },
react_1.default.createElement(react_bootstrap_1.Row, { className: "mb-4" },
react_1.default.createElement(react_bootstrap_1.Col, null,
react_1.default.createElement("div", { className: "d-flex align-items-center gap-2" },
react_1.default.createElement(react_bootstrap_1.Badge, { bg: mode === "static"
? "secondary"
: mode === "dev"
? "success"
: "primary" },
mode.toUpperCase(),
" MODE"),
react_1.default.createElement("select", { className: "form-select form-select-sm", style: { width: "auto" }, value: mode, onChange: (e) => setMode(e.target.value) },
react_1.default.createElement("option", { value: "static" }, "Static (Read-only)"),
react_1.default.createElement("option", { value: "dev" }, "Development (Read-write)"),
react_1.default.createElement("option", { value: "git" }, "Git Remote"))),
mode === "static" && (react_1.default.createElement(react_bootstrap_1.Alert, { variant: "info", className: "mt-2" },
react_1.default.createElement("small", null, "Static mode: Read-only access. Git operations are not available in this mode."))),
mode === "git" && (react_1.default.createElement(react_bootstrap_1.Alert, { variant: "warning", className: "mt-2" },
react_1.default.createElement("small", null, "Git Remote mode: Git-based collaboration. Some features may be limited."))))),
error && (react_1.default.createElement(react_bootstrap_1.Alert, { variant: "danger", onClose: () => setError(null), dismissible: true },
react_1.default.createElement("div", null, error),
react_1.default.createElement("small", { className: "text-muted" }, "Check the browser console for more details"))),
mode !== "static" && (react_1.default.createElement(react_bootstrap_1.Row, null,
react_1.default.createElement(react_bootstrap_1.Col, { md: 4 },
react_1.default.createElement(react_bootstrap_1.Card, null,
react_1.default.createElement(react_bootstrap_1.Card.Header, { className: "d-flex justify-content-between align-items-center" },
react_1.default.createElement("h5", { className: "mb-0" }, "Changes"),
react_1.default.createElement(react_bootstrap_1.Button, { variant: "outline-secondary", size: "sm", onClick: loadChanges, disabled: isLoading }, isLoading ? react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border", size: "sm" }) : "↻")),
react_1.default.createElement(react_bootstrap_1.Card.Body, { style: { maxHeight: "400px", overflowY: "auto" } }, isLoading ? (react_1.default.createElement("div", { className: "text-center" },
react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border" }),
react_1.default.createElement("div", null, "Loading changes..."))) : changes.length === 0 ? (react_1.default.createElement("div", { className: "text-center text-muted" }, "No changes detected")) : (react_1.default.createElement("div", null, changes.map((change, index) => (react_1.default.createElement("div", { key: index, className: "d-flex align-items-center mb-2" },
react_1.default.createElement(react_bootstrap_1.Badge, { bg: getStatusBadgeVariant(change.status), className: "me-2" }, change.status.charAt(0).toUpperCase() +
change.status.slice(1)),
react_1.default.createElement("span", { className: "small text-truncate" }, change.path))))))))),
react_1.default.createElement(react_bootstrap_1.Col, { md: 4 },
react_1.default.createElement(react_bootstrap_1.Card, null,
react_1.default.createElement(react_bootstrap_1.Card.Header, null,
react_1.default.createElement("h5", null, "Commit Changes")),
react_1.default.createElement(react_bootstrap_1.Card.Body, null,
react_1.default.createElement("div", { className: "mb-3" },
react_1.default.createElement("label", { htmlFor: "summary", className: "form-label" }, "Summary *"),
react_1.default.createElement("input", { type: "text", className: "form-control", id: "summary", placeholder: "What did you change?", value: commitSummary, onChange: (e) => setCommitSummary(e.target.value), disabled: mode === "static" }),
react_1.default.createElement("div", { className: "form-text" },
commitSummary.length,
"/72 characters")),
react_1.default.createElement("div", { className: "mb-3" },
react_1.default.createElement("label", { htmlFor: "description", className: "form-label" }, "Description"),
react_1.default.createElement("textarea", { className: "form-control", id: "description", rows: 3, placeholder: "Why did you change it?", value: commitDescription, onChange: (e) => setCommitDescription(e.target.value), disabled: mode === "static" })),
react_1.default.createElement("div", { className: "d-grid gap-2" },
react_1.default.createElement(react_bootstrap_1.Button, { variant: "primary", onClick: handleSaveChanges, disabled: mode === "static" ||
isCommitting ||
changes.length === 0 ||
!commitSummary.trim() }, isCommitting ? (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border", size: "sm", className: "me-2" }),
"Saving...")) : ("Save to Computer")),
react_1.default.createElement(react_bootstrap_1.Button, { variant: "success", onClick: handleShareChanges, disabled: mode === "static" ||
isCommitting ||
isPushing ||
changes.length === 0 ||
!commitSummary.trim() }, isPushing ? (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border", size: "sm", className: "me-2" }),
"Sharing...")) : ("Save & Share")))))),
react_1.default.createElement(react_bootstrap_1.Col, { md: 4 },
react_1.default.createElement(react_bootstrap_1.Card, null,
react_1.default.createElement(react_bootstrap_1.Card.Header, { className: "d-flex justify-content-between align-items-center" },
react_1.default.createElement("h5", { className: "mb-0" }, "Sync with Remote"),
react_1.default.createElement(react_bootstrap_1.Button, { variant: "outline-secondary", size: "sm", onClick: (e) => loadGitStatus(e) }, "\u21BB")),
react_1.default.createElement(react_bootstrap_1.Card.Body, null,
react_1.default.createElement("div", { className: "text-center mb-3" },
react_1.default.createElement(react_bootstrap_1.Badge, { bg: getSyncStatusVariant() }, getSyncStatusText()),
react_1.default.createElement("div", { className: "small text-muted mt-1" },
"Branch: ",
currentBranch)),
react_1.default.createElement("div", { className: "d-grid gap-2" },
react_1.default.createElement(react_bootstrap_1.Button, { variant: "outline-primary", onClick: handleGetUpdates, disabled: mode === "static" || isPulling }, isPulling ? (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border", size: "sm", className: "me-2" }),
"Updating...")) : ("Get Updates")),
react_1.default.createElement(react_bootstrap_1.Button, { variant: "outline-success", disabled: mode === "static" || remoteStatus.ahead === 0, onClick: async (e) => {
e.preventDefault();
try {
setIsPushing(true);
setError(null);
await fileService.pushChanges();
await loadGitStatus();
}
catch (err) {
const errorMessage = err instanceof Error
? err.message
: "Failed to push changes";
console.error("Failed to push changes:", err);
setError(errorMessage);
}
finally {
setIsPushing(false);
}
} }, isPushing ? (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border", size: "sm", className: "me-2" }),
"Sharing...")) : (`Share Changes (${remoteStatus.ahead})`))),
react_1.default.createElement("div", { className: "mt-3" },
react_1.default.createElement("small", { className: "text-muted" },
"Connected to: origin/",
currentBranch)))),
react_1.default.createElement(react_bootstrap_1.Card, null,
react_1.default.createElement(react_bootstrap_1.Card.Header, { className: "d-flex justify-content-between align-items-center" },
react_1.default.createElement("h5", { className: "mb-0" }, "Changes"),
react_1.default.createElement(react_bootstrap_1.Button, { variant: "outline-secondary", size: "sm", onClick: loadChanges, disabled: isLoading }, isLoading ? react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border", size: "sm" }) : "↻")),
react_1.default.createElement(react_bootstrap_1.Card.Body, { style: { maxHeight: "400px", overflowY: "auto" } }, isLoading ? (react_1.default.createElement("div", { className: "text-center" },
react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border" }),
react_1.default.createElement("div", null, "Loading changes..."))) : changes.length === 0 ? (react_1.default.createElement("div", { className: "text-center text-muted" }, "No changes detected")) : (react_1.default.createElement("div", null, changes.map((change, index) => (react_1.default.createElement("div", { key: index, className: `d-flex align-items-center mb-2 ${selectedFile === change.path
? "bg-light rounded p-1"
: ""}`, style: { cursor: "pointer" }, onClick: () => loadFileDiff(change.path) },
react_1.default.createElement(react_bootstrap_1.Badge, { bg: getStatusBadgeVariant(change.status), className: "me-2" }, change.status.charAt(0).toUpperCase() +
change.status.slice(1)),
react_1.default.createElement("span", { className: "small text-truncate" }, change.path))))))))),
react_1.default.createElement(react_bootstrap_1.Col, { md: 8 },
react_1.default.createElement(react_bootstrap_1.Card, { className: "mb-3" },
react_1.default.createElement(react_bootstrap_1.Card.Header, null,
react_1.default.createElement("h5", null, "Changes Preview")),
react_1.default.createElement(react_bootstrap_1.Card.Body, { style: { height: "400px" } }, isDiffLoading ? (react_1.default.createElement("div", { className: "text-center" },
react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border" }),
react_1.default.createElement("div", null, "Loading diff..."))) : selectedFile ? (modifiedContent ? (react_1.default.createElement(react_2.Editor, { height: "100%", language: "typescript", original: originalContent, modified: modifiedContent, options: {
readOnly: true,
renderSideBySide: true,
minimap: { enabled: false },
scrollBeyondLastLine: false,
fontSize: 12,
lineNumbers: "on",
folding: true,
glyphMargin: false,
lineDecorationsWidth: 10,
lineNumbersMinChars: 3,
scrollbar: {
vertical: "auto",
horizontal: "auto",
},
renderLineHighlight: "all",
} })) : (react_1.default.createElement("div", { className: "text-center text-muted" },
react_1.default.createElement("p", null, "Could not load file content."),
react_1.default.createElement("small", null, "Check that the development server is running and has file API endpoints implemented.")))) : (react_1.default.createElement("div", { className: "text-center text-muted" }, "Select a file to view changes")))),
react_1.default.createElement(react_bootstrap_1.Card, null,
react_1.default.createElement(react_bootstrap_1.Card.Header, null,
react_1.default.createElement("h5", null, "Commit Changes")),
react_1.default.createElement(react_bootstrap_1.Card.Body, null,
react_1.default.createElement("div", { className: "mb-3" },
react_1.default.createElement("label", { htmlFor: "summary", className: "form-label" }, "Summary *"),
react_1.default.createElement("input", { type: "text", className: "form-control", id: "summary", placeholder: "What did you change?", value: commitSummary, onChange: (e) => setCommitSummary(e.target.value), disabled: mode === "static" }),
react_1.default.createElement("div", { className: "form-text" },
commitSummary.length,
"/72 characters")),
react_1.default.createElement("div", { className: "mb-3" },
react_1.default.createElement("label", { htmlFor: "description", className: "form-label" }, "Description"),
react_1.default.createElement("textarea", { className: "form-control", id: "description", rows: 3, placeholder: "Why did you change it?", value: commitDescription, onChange: (e) => setCommitDescription(e.target.value), disabled: mode === "static" })),
react_1.default.createElement("div", { className: "d-grid gap-2" },
react_1.default.createElement(react_bootstrap_1.Button, { variant: "primary", onClick: handleSaveChanges, disabled: mode === "static" ||
isCommitting ||
changes.length === 0 ||
!commitSummary.trim() }, isCommitting ? (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border", size: "sm", className: "me-2" }),
"Saving...")) : ("Save to Computer")),
react_1.default.createElement(react_bootstrap_1.Button, { variant: "success", onClick: handleShareChanges, disabled: mode === "static" ||
isCommitting ||
isPushing ||
changes.length === 0 ||
!commitSummary.trim() }, isPushing ? (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement(react_bootstrap_1.Spinner, { animation: "border", size: "sm", className: "me-2" }),
"Sharing...")) : ("Save & Share")))))))),
mode === "static" && (react_1.default.createElement(react_bootstrap_1.Row, null,
react_1.default.createElement(react_bootstrap_1.Col, null,
react_1.default.createElement(react_bootstrap_1.Alert, { variant: "info", className: "text-center" },
react_1.default.createElement("h5", null, "Git Operations Not Available"),
react_1.default.createElement("p", null, "Git functionality is disabled in Static Mode. Switch to Development or Git Remote mode to access version control features.")))))));
};
exports.GitIntegrationView = GitIntegrationView;