mirror of
https://github.com/danielmiessler/fabric
synced 2024-11-08 07:11:06 +00:00
added drag and drop and updated UI
This commit is contained in:
parent
10b39ade6d
commit
8b28b79b9f
@ -16,6 +16,6 @@ Fabric is not just a tool; it's a transformative step towards integrating the po
|
||||
2. Start the application:
|
||||
`npm start`
|
||||
|
||||
## Contributors
|
||||
Contributing
|
||||
|
||||
- Credit to Jonathan Dunn (@xssdoctor) for the first version of the GUI client.
|
||||
We welcome contributions to Fabric! For details on our code of conduct and the process for submitting pull requests, please read the CONTRIBUTING.md.
|
||||
|
@ -9,7 +9,13 @@
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
||||
<a class="navbar-brand" href="#">Fabric</a>
|
||||
<a class="navbar-brand" href="#">
|
||||
<img
|
||||
src="static/images/fabric-logo-gif.gif"
|
||||
alt="Fabric Logo"
|
||||
height="40"
|
||||
/>
|
||||
</a>
|
||||
<button id="configButton" class="btn btn-outline-success my-2 my-sm-0">
|
||||
Config
|
||||
</button>
|
||||
@ -17,7 +23,7 @@
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#navbarCollapse"
|
||||
data-target="#navbarCollap se"
|
||||
aria-controls="navbarCollapse"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
@ -32,7 +38,12 @@
|
||||
<main>
|
||||
<div class="container" id="my-form">
|
||||
<select class="form-control" id="patternSelector"></select>
|
||||
<textarea rows="5" class="form-control" id="userInput"></textarea>
|
||||
<textarea
|
||||
rows="5"
|
||||
class="form-control"
|
||||
id="userInput"
|
||||
placeholder="start typing or drag a file (.txt, .svg, .pdf and .doc are currently supported)"
|
||||
></textarea>
|
||||
<button class="btn btn-primary" id="submit">Submit</button>
|
||||
</div>
|
||||
<div id="configSection" class="container hidden">
|
||||
|
@ -1,4 +1,6 @@
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require("electron");
|
||||
const pdfParse = require("pdf-parse");
|
||||
const mammoth = require("mammoth");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
@ -23,6 +25,36 @@ function createWindow() {
|
||||
win = null;
|
||||
});
|
||||
}
|
||||
ipcMain.on("process-complex-file", (event, filePath) => {
|
||||
const extension = path.extname(filePath).toLowerCase();
|
||||
let fileProcessPromise;
|
||||
|
||||
if (extension === ".pdf") {
|
||||
const dataBuffer = fs.readFileSync(filePath);
|
||||
fileProcessPromise = pdfParse(dataBuffer).then((data) => data.text);
|
||||
} else if (extension === ".docx") {
|
||||
fileProcessPromise = mammoth
|
||||
.extractRawText({ path: filePath })
|
||||
.then((result) => result.value)
|
||||
.catch((err) => {
|
||||
console.error("Error processing DOCX file:", err);
|
||||
throw new Error("Error processing DOCX file.");
|
||||
});
|
||||
} else {
|
||||
event.reply("file-response", "Error: Unsupported file type");
|
||||
return;
|
||||
}
|
||||
|
||||
fileProcessPromise
|
||||
.then((extractedText) => {
|
||||
// Sending the extracted text back to the frontend.
|
||||
event.reply("file-response", extractedText);
|
||||
})
|
||||
.catch((error) => {
|
||||
// Handling any errors during file processing and sending them back to the frontend.
|
||||
event.reply("file-response", `Error processing file: ${error.message}`);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on("start-query-openai", (event, system, user) => {
|
||||
if (system == null || user == null) {
|
||||
|
@ -8,6 +8,70 @@ document.addEventListener("DOMContentLoaded", async function () {
|
||||
const configSection = document.getElementById("configSection");
|
||||
const saveApiKeyButton = document.getElementById("saveApiKey");
|
||||
const apiKeyInput = document.getElementById("apiKeyInput");
|
||||
const originalPlaceholder = userInput.placeholder;
|
||||
const copyButton = document.getElementById("copyButton");
|
||||
|
||||
async function submitQuery(userInputValue) {
|
||||
userInput.value = ""; // Clear the input after submitting
|
||||
systemCommand = await window.electronAPI.invoke(
|
||||
"get-pattern-content",
|
||||
patternSelector.value
|
||||
);
|
||||
responseContainer.innerHTML = ""; // Clear previous responses
|
||||
responseContainer.classList.remove("hidden");
|
||||
window.electronAPI.send(
|
||||
"start-query-openai",
|
||||
systemCommand,
|
||||
userInputValue
|
||||
);
|
||||
}
|
||||
|
||||
function fallbackCopyTextToClipboard(text) {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
|
||||
// Avoid scrolling to bottom
|
||||
textArea.style.top = "0";
|
||||
textArea.style.left = "0";
|
||||
textArea.style.position = "fixed";
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
function copyToClipboard() {
|
||||
try {
|
||||
if (responseContainer.textContent) {
|
||||
text = responseContainer.textContent;
|
||||
}
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(function () {
|
||||
console.log("Text successfully copied to clipboard");
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error("Error in copying text: ", err);
|
||||
// Optionally, use fallback method here
|
||||
});
|
||||
} else {
|
||||
fallbackCopyTextToClipboard(text);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error in copying text: ", err);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const successful = document.execCommand("copy");
|
||||
const msg = successful ? "successful" : "unsuccessful";
|
||||
console.log("Fallback: Copying text command was " + msg);
|
||||
} catch (err) {
|
||||
console.error("Fallback: Oops, unable to copy", err);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
// Load patterns on startup
|
||||
try {
|
||||
@ -28,27 +92,23 @@ document.addEventListener("DOMContentLoaded", async function () {
|
||||
responseContainer.innerHTML += formattedMessage; // Append new data as it arrives
|
||||
});
|
||||
|
||||
window.electronAPI.on("file-response", (message) => {
|
||||
if (message.startsWith("Error")) {
|
||||
alert(message);
|
||||
return;
|
||||
}
|
||||
submitQuery(message);
|
||||
});
|
||||
|
||||
// Submit button click handler
|
||||
submitButton.addEventListener("click", async () => {
|
||||
responseContainer.innerHTML = ""; // Clear previous responses
|
||||
responseContainer.classList.remove("hidden");
|
||||
const selectedPattern = patternSelector.value;
|
||||
const systemCommand = await window.electronAPI.invoke(
|
||||
"get-pattern-content",
|
||||
selectedPattern
|
||||
);
|
||||
const userInputValue = userInput.value;
|
||||
window.electronAPI.send(
|
||||
"start-query-openai",
|
||||
systemCommand,
|
||||
userInputValue
|
||||
);
|
||||
submitQuery(userInputValue);
|
||||
});
|
||||
|
||||
// Theme changer click handler
|
||||
themeChanger.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
console.log("Theme changer clicked");
|
||||
document.body.classList.toggle("light-theme");
|
||||
themeChanger.innerText =
|
||||
themeChanger.innerText === "Dark" ? "Light" : "Dark";
|
||||
@ -86,4 +146,53 @@ document.addEventListener("DOMContentLoaded", async function () {
|
||||
);
|
||||
// Use systemCommand as part of the input for querying OpenAI
|
||||
});
|
||||
|
||||
userInput.addEventListener("dragover", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
// Add some visual feedback
|
||||
userInput.classList.add("drag-over");
|
||||
userInput.placeholder = "Drop file here";
|
||||
});
|
||||
|
||||
userInput.addEventListener("dragleave", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
// Remove visual feedback
|
||||
userInput.classList.remove("drag-over");
|
||||
userInput.placeholder = originalPlaceholder;
|
||||
});
|
||||
|
||||
userInput.addEventListener("drop", (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
const file = event.dataTransfer.files[0];
|
||||
userInput.classList.remove("drag-over");
|
||||
userInput.placeholder = originalPlaceholder;
|
||||
processFile(file);
|
||||
});
|
||||
|
||||
function processFile(file) {
|
||||
const fileType = file.type;
|
||||
const reader = new FileReader();
|
||||
let content = "";
|
||||
|
||||
reader.onload = (event) => {
|
||||
content = event.target.result;
|
||||
userInput.value = content;
|
||||
submitQuery(content);
|
||||
};
|
||||
|
||||
if (fileType === "text/plain" || fileType === "image/svg+xml") {
|
||||
reader.readAsText(file);
|
||||
} else if (
|
||||
fileType === "application/pdf" ||
|
||||
fileType.match(/wordprocessingml/)
|
||||
) {
|
||||
// For PDF and DOCX, we need to handle them in the main process due to complexity
|
||||
window.electronAPI.send("process-complex-file", file.path);
|
||||
} else {
|
||||
console.error("Unsupported file type");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -78,3 +78,79 @@ body {
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.drag-over {
|
||||
background-color: #505050; /* Slightly lighter than the regular background for visibility */
|
||||
border: 2px dashed #007bff; /* Dashed border with the primary button color for emphasis */
|
||||
box-shadow: 0 0 10px #007bff; /* Soft glow effect to highlight the area */
|
||||
color: #e0e0e0; /* Maintaining the light text color for readability */
|
||||
transition: background-color 0.3s ease, box-shadow 0.3s ease; /* Smooth transition for background and shadow changes */
|
||||
}
|
||||
|
||||
.light-theme .drag-over {
|
||||
background-color: #e6e6e6; /* Lighter background for light theme */
|
||||
border: 2px dashed #0066cc; /* Adjusted border color for light theme */
|
||||
box-shadow: 0 0 10px #0066cc; /* Soft glow effect for light theme */
|
||||
color: #333; /* Darker text for contrast in light theme */
|
||||
}
|
||||
|
||||
/* Existing dark theme styles for reference */
|
||||
.navbar-dark.bg-dark {
|
||||
background-color: #343a40 !important;
|
||||
}
|
||||
|
||||
/* Light theme styles */
|
||||
body.light-theme .navbar-dark.bg-dark {
|
||||
background-color: #e2e6ea !important; /* Slightly darker shade for better visibility */
|
||||
color: #000 !important; /* Keep dark text color for contrast */
|
||||
}
|
||||
|
||||
body.light-theme .navbar-dark .navbar-brand,
|
||||
body.light-theme .navbar-dark .btn-outline-success {
|
||||
color: #0056b3 !important; /* Darker color for better visibility and contrast */
|
||||
}
|
||||
|
||||
body.light-theme .navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'><path stroke='rgba(0, 0, 0, 0.75)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !important;
|
||||
/* Slightly darker stroke for the navbar-toggler-icon for better visibility */
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.navbar-brand img {
|
||||
height: 20px; /* Smaller logo for smaller screens */
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-toggler {
|
||||
padding: 0.25rem 0.5rem; /* Adjust padding for the toggle button */
|
||||
}
|
||||
}
|
||||
#responseContainer {
|
||||
position: relative; /* Needed for absolute positioning of the child button */
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
position: absolute;
|
||||
top: 10px; /* Adjust as needed */
|
||||
right: 10px; /* Adjust as needed */
|
||||
background-color: rgba(
|
||||
0,
|
||||
123,
|
||||
255,
|
||||
0.5
|
||||
); /* Bootstrap primary color with transparency */
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.copy-button:hover {
|
||||
background-color: rgba(
|
||||
0,
|
||||
123,
|
||||
255,
|
||||
0.8
|
||||
); /* Slightly less transparent on hover */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user