Browse Source

added drag and drop and updated UI

pull/75/head
jad2121 9 months ago
parent
commit
8b28b79b9f
  1. 4
      client/gui/README.md
  2. 17
      client/gui/index.html
  3. 32
      client/gui/main.js
  4. 135
      client/gui/static/js/index.js
  5. 76
      client/gui/static/stylesheet/style.css

4
client/gui/README.md

@ -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.

17
client/gui/index.html

@ -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">

32
client/gui/main.js

@ -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) {

135
client/gui/static/js/index.js

@ -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");
}
}
});

76
client/gui/static/stylesheet/style.css

@ -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…
Cancel
Save