main #122
6 changed files with 100 additions and 117 deletions
|
@ -1,62 +1,24 @@
|
|||
import React from 'react';
|
||||
|
||||
const Credits: React.FC = () => {
|
||||
return (
|
||||
<div className="credits-container">
|
||||
<section id="credits" className="credits-section">
|
||||
<h1 className="title">Credits</h1>
|
||||
const Credits: React.FC = () => (
|
||||
<div className="credits-container">
|
||||
<section id="credits" className="credits-section">
|
||||
<h1 className="title">Credits</h1>
|
||||
<h2 className="subtitle">Icons</h2>
|
||||
<p className="paragraph">This project utilizes the following icon resources:</p>
|
||||
<CreditLink href="https://fontawesome.com/v4/license/" label="fontawesome" />
|
||||
<h2 className="subtitle">Fonts</h2>
|
||||
<p className="paragraph">The fonts used in this project are provided by:</p>
|
||||
<CreditLink href="https://github.com/itfoundry/Poppins" label="Poppins" />
|
||||
<CreditLink href="https://openfontlicense.org" label="Inconsolata, Merriweather, Noto Sans, Noto Serif, Playfair Display, Bangers, Caveat, Frederika the Great, Sofadi One, Zilla Slab Highlight" />
|
||||
<CreditLink href="http://www.apache.org/licenses/LICENSE-2.0" label="Roboto, Rock Salt" />
|
||||
<CreditLink href="https://ubuntu.com/legal/font-licence" label="Ubuntu" />
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
<h2 className="subtitle">Icons</h2>
|
||||
<p className="paragraph">
|
||||
This project utilizes the following icon resources:
|
||||
</p>
|
||||
<a
|
||||
href="https://fontawesome.com/v4/license/"
|
||||
className="credit-btn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
fontawesome
|
||||
</a>
|
||||
<h2 className="subtitle">Fonts</h2>
|
||||
<p className="paragraph">
|
||||
The fonts used in this project are provided by:
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/itfoundry/Poppins"
|
||||
className="credit-btn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Poppins
|
||||
</a>
|
||||
<a
|
||||
href="https://openfontlicense.org"
|
||||
className="credit-btn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Inconsolata, Merriweather, Noto Sans, Noto Serif, Playfair Display, Bangers, Caveat, Frederika the Great, Sofadi One, Zilla Slab Highlight
|
||||
</a>
|
||||
<a
|
||||
href="http://www.apache.org/licenses/LICENSE-2.0"
|
||||
className="credit-btn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Roboto, Rock Salt
|
||||
</a>
|
||||
<a
|
||||
href="https://ubuntu.com/legal/font-licence"
|
||||
className="credit-btn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Ubuntu
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const CreditLink = ({ href, label }: { href: string; label: string }) => (
|
||||
<a href={href} className="credit-btn" target="_blank" rel="noopener noreferrer">{label}</a>
|
||||
);
|
||||
|
||||
export default Credits;
|
||||
|
|
|
@ -20,13 +20,16 @@ const Header: React.FC<HeaderProps> = ({
|
|||
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
||||
const toggleRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// Pages that will be displayed in the menu
|
||||
const pages: ('AI' | 'FAQ' | 'Documentation' | 'Credits')[] = ['AI', 'FAQ', 'Documentation', 'Credits'];
|
||||
|
||||
// Toggle menu state
|
||||
const toggleMenu = () => {
|
||||
setMenuOpen((prevMenuOpen) => !prevMenuOpen);
|
||||
};
|
||||
|
||||
// Handle button click
|
||||
const buttonClicked = (page: "AI" | "Documentation" | "FAQ" | "Credits") => {
|
||||
const handleViewChange = (page: 'AI' | 'FAQ' | 'Documentation' | 'Credits') => {
|
||||
onViewChange(page);
|
||||
setMenuOpen(false); // Close the menu when a button is clicked
|
||||
};
|
||||
|
@ -35,9 +38,9 @@ const Header: React.FC<HeaderProps> = ({
|
|||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node) &&
|
||||
toggleRef.current &&
|
||||
toggleRef.current &&
|
||||
!toggleRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setMenuOpen(false);
|
||||
|
@ -72,10 +75,11 @@ const Header: React.FC<HeaderProps> = ({
|
|||
</button>
|
||||
)}
|
||||
<nav ref={dropdownRef} className={`nav-links ${menuOpen ? "active" : ""}`}>
|
||||
<button onClick={() => buttonClicked("AI")} className="nav-btn">Chat</button>
|
||||
<button onClick={() => buttonClicked("FAQ")} className="nav-btn">FAQ</button>
|
||||
<button onClick={() => buttonClicked("Documentation")} className="nav-btn">Documentation</button>
|
||||
<button onClick={() => buttonClicked("Credits")} className="nav-btn">Credits</button>
|
||||
{pages.map(page => (
|
||||
<button key={page} onClick={() => handleViewChange(page)} className="nav-btn">
|
||||
{page}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
<div ref={toggleRef} className={`hamburger ${menuOpen ? "open" : ""}`} onClick={toggleMenu}>
|
||||
<span></span>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, ForwardedRef, useEffect } from 'react';
|
||||
import "../styles/variables.css"
|
||||
import "../styles/variables.css";
|
||||
|
||||
interface InputProps {
|
||||
message: string;
|
||||
|
@ -13,33 +13,36 @@ const InputFrontend = React.forwardRef<HTMLDivElement, InputProps>(
|
|||
({ message, onSendClick, onMicClick, inputDisabled, isRecording }, ref: ForwardedRef<HTMLDivElement>) => {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
// Sync the inputValue state with the message prop
|
||||
useEffect(() => {
|
||||
setInputValue(message);
|
||||
}, [message]);
|
||||
|
||||
// Handle the input change
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(e.target.value);
|
||||
};
|
||||
|
||||
// Handle 'Enter' key press
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (!inputDisabled) {
|
||||
if (event.key === 'Enter') {
|
||||
onSendClick(inputValue, false); // Call the function passed via props
|
||||
setInputValue(''); // Optionally clear input after submission
|
||||
event.preventDefault(); // Prevent default action (e.g., form submission)
|
||||
}
|
||||
if (!inputDisabled && event.key === 'Enter') {
|
||||
onSendClick(inputValue, false); // Send the message on Enter
|
||||
setInputValue(''); // Clear the input after submission
|
||||
event.preventDefault(); // Prevent the default Enter action
|
||||
}
|
||||
};
|
||||
|
||||
// Handle the Send button click
|
||||
const handleSendClick = () => {
|
||||
if (inputValue.trim() !== "") {
|
||||
onSendClick(inputValue, false); // Send message to parent component
|
||||
onSendClick(inputValue, false); // Send message
|
||||
setInputValue(''); // Clear input after sending
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="input" id="inputForm" ref={ref}>
|
||||
{/* Input field for typing messages */}
|
||||
<input
|
||||
type="text"
|
||||
name="user_message"
|
||||
|
@ -47,13 +50,22 @@ const InputFrontend = React.forwardRef<HTMLDivElement, InputProps>(
|
|||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
className='textInputField'
|
||||
className="textInputField"
|
||||
disabled={inputDisabled} // Disable when inputDisabled is true
|
||||
/>
|
||||
<button type="button" onClick={handleSendClick} disabled={inputDisabled ? true : false}>
|
||||
<svg style={{ fill: "var(--text-color)" }} viewBox="0 0 512 512" width={20}><path d="M498.1 5.6c10.1 7 15.4 19.1 13.5 31.2l-64 416c-1.5 9.7-7.4 18.2-16 23s-18.9 5.4-28 1.6L284 427.7l-68.5 74.1c-8.9 9.7-22.9 12.9-35.2 8.1S160 493.2 160 480l0-83.6c0-4 1.5-7.8 4.2-10.8L331.8 202.8c5.8-6.3 5.6-16-.4-22s-15.7-6.4-22-.7L106 360.8 17.7 316.6C7.1 311.3 .3 300.7 0 288.9s5.9-22.8 16.1-28.7l448-256c10.7-6.1 23.9-5.5 34 1.4z" /></svg>
|
||||
|
||||
{/* Send button */}
|
||||
<button type="button" onClick={handleSendClick} disabled={inputDisabled}>
|
||||
<svg style={{ fill: "var(--text-color)" }} viewBox="0 0 512 512" width={20}>
|
||||
<path d="M498.1 5.6c10.1 7 15.4 19.1 13.5 31.2l-64 416c-1.5 9.7-7.4 18.2-16 23s-18.9 5.4-28 1.6L284 427.7l-68.5 74.1c-8.9 9.7-22.9 12.9-35.2 8.1S160 493.2 160 480l0-83.6c0-4 1.5-7.8 4.2-10.8L331.8 202.8c5.8-6.3 5.6-16-.4-22s-15.7-6.4-22-.7L106 360.8 17.7 316.6C7.1 311.3 .3 300.7 0 288.9s5.9-22.8 16.1-28.7l448-256c10.7-6.1 23.9-5.5 34 1.4z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Microphone button */}
|
||||
<button className={`microphone-button ${isRecording ? "red" : "var(--input-button-color)"}`} type="button" onClick={onMicClick}>
|
||||
<svg style={{ fill: "var(--text-color)" }} viewBox="0 0 384 512" width={15}><path d="M192 0C139 0 96 43 96 96l0 160c0 53 43 96 96 96s96-43 96-96l0-160c0-53-43-96-96-96zM64 216c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 40c0 89.1 66.2 162.7 152 174.4l0 33.6-48 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l72 0 72 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-48 0 0-33.6c85.8-11.7 152-85.3 152-174.4l0-40c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 40c0 70.7-57.3 128-128 128s-128-57.3-128-128l0-40z" /></svg>
|
||||
<svg style={{ fill: "var(--text-color)" }} viewBox="0 0 384 512" width={15}>
|
||||
<path d="M192 0C139 0 96 43 96 96l0 160c0 53 43 96 96 96s96-43 96-96l0-160c0-53-43-96-96-96zM64 216c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 40c0 89.1 66.2 162.7 152 174.4l0 33.6-48 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l72 0 72 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-48 0 0-33.6c85.8-11.7 152-85.3 152-174.4l0-40c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 40c0 70.7-57.3 128-128 128s-128-57.3-128-128l0-40z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -123,50 +123,39 @@ const ModelSection: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
|
||||
setIsOpenSourceMode(localStorage.getItem("openSourceMode"))
|
||||
const temp = localStorage.getItem("activeSelectedAIFunction") || ""
|
||||
setActiveSelectedAIFunction(temp)
|
||||
if (!localStorage.getItem('selectedModelDropdown')) {
|
||||
localStorage.setItem("selectedModelDropdown", "Offline Fast")
|
||||
}
|
||||
|
||||
if (!localStorage.getItem("activeSelectedAIFunction")) {
|
||||
setActiveSelectedAIFunction('Code')
|
||||
localStorage.setItem('activeSelectedAIFunction', 'Code')
|
||||
}
|
||||
|
||||
if (!localStorage.getItem("model")) {
|
||||
localStorage.setItem("model", 'starcoder2')
|
||||
}
|
||||
|
||||
if (!localStorage.getItem("radioSelection")) {
|
||||
localStorage.setItem("radioSelection", 'None')
|
||||
}
|
||||
|
||||
if (!localStorage.getItem("type")) {
|
||||
localStorage.setItem("type", 'local')
|
||||
}
|
||||
|
||||
const defaultValues = {
|
||||
selectedModelDropdown: 'Offline Fast',
|
||||
activeSelectedAIFunction: 'Code',
|
||||
model: 'starcoder2',
|
||||
radioSelection: 'None',
|
||||
type: 'local',
|
||||
};
|
||||
|
||||
Object.entries(defaultValues).forEach(([key, value]) => {
|
||||
if (!localStorage.getItem(key)) {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
setIsOpenSourceMode(localStorage.getItem("openSourceMode"));
|
||||
setActiveSelectedAIFunction(localStorage.getItem("activeSelectedAIFunction") || '');
|
||||
setRadioSelection(localStorage.getItem("radioSelection") || '');
|
||||
setSelectedModelDropdown(localStorage.getItem('selectedModelDropdown') || '');
|
||||
|
||||
const handleStorageChange = () => {
|
||||
setSelectedModelDropdown(localStorage.getItem('selectedModelDropdown') || '');
|
||||
};
|
||||
|
||||
|
||||
// Update immediately when localStorage changes
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('storage', handleStorageChange);
|
||||
}
|
||||
window.addEventListener('storage', handleStorageChange);
|
||||
|
||||
setRadioSelection(localStorage.getItem('radioSelection') || '');
|
||||
setSelectedModelDropdown(localStorage.getItem('selectedModelDropdown') || '');
|
||||
// Cleanup listener on component unmount
|
||||
return () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.removeEventListener('storage', handleStorageChange);
|
||||
}
|
||||
window.removeEventListener('storage', handleStorageChange);
|
||||
};
|
||||
}
|
||||
}, []); // Dependency array can remain empty if you only want this to run on mount
|
||||
}, []);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
|
@ -271,7 +260,6 @@ const ModelSection: React.FC = () => {
|
|||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Model Grid with Cards */}
|
||||
<div className="grid">
|
||||
{selectedAIFunction.map(
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
changeSettings,
|
||||
createAccount,
|
||||
deleteAccount,
|
||||
getSettings
|
||||
} from '../../backend/database';
|
||||
import ThemeDropdown from './DropDownTheme';
|
||||
|
||||
|
@ -298,6 +299,11 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = (
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
let useName = localStorage.getItem("accountName")
|
||||
let usePassword = localStorage.getItem("accountPassword")
|
||||
if (useName && usePassword) {
|
||||
importDatabase(useName, usePassword)
|
||||
}
|
||||
const savedTheme = localStorage.getItem('selectedTheme');
|
||||
if (savedTheme) {
|
||||
setSelectedTheme(savedTheme);
|
||||
|
@ -305,6 +311,10 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = (
|
|||
}
|
||||
}, []); // Runs only once when the component mounts
|
||||
|
||||
const importDatabase = async (useName : string, usePassword : string) => {
|
||||
const databaseSettings = await getSettings(useName, usePassword)
|
||||
importSettings(databaseSettings)
|
||||
}
|
||||
// Effect hooks to update localStorage whenever any state changes
|
||||
useEffect(() => {
|
||||
// Flatten nested objects
|
||||
|
@ -355,7 +365,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = (
|
|||
}
|
||||
|
||||
if (await createAccount(useName, useEmail, usePassword)) {
|
||||
if (await changeSettings(useName, usePassword, settings)) {
|
||||
if (await changeSettings(useName, usePassword, handleExport)) {
|
||||
localStorage.setItem("currentName", useName)
|
||||
localStorage.setItem("currentPassword", usePassword)
|
||||
localStorage.setItem("currentEmail", useEmail)
|
||||
|
@ -714,7 +724,6 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = (
|
|||
|
||||
const handleExport = () => {
|
||||
const settingsData = exportSettings();
|
||||
|
||||
// Create a blob and download the exported settings
|
||||
const blob = new Blob([settingsData], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
@ -748,7 +757,7 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = (
|
|||
<div className="settings-main">
|
||||
{/* Dropdown for selections in responsive mode */}
|
||||
<div className="settings-option dropdown">
|
||||
<div className="dropdown-header"><h1>Select a Setting</h1></div>
|
||||
<div className="dropdown-header"><h2>Select a Setting</h2></div>
|
||||
<select onChange={(e) => setActiveSection(e.target.value)} value={activeSection}>
|
||||
<option value="general">General</option>
|
||||
<option value="privacy">Privacy</option>
|
||||
|
@ -763,10 +772,13 @@ const Settings: React.FC<{ closeSettings: () => void; accountName: string }> = (
|
|||
{renderSettingsContent()}
|
||||
<button className="close-popup" onClick={closeSettings}>Close</button>
|
||||
<button className="apply" onClick={async () => {
|
||||
// Your existing logic for applying settings
|
||||
getAllLocalStorageItems();
|
||||
closeSettings();
|
||||
window.location.reload();
|
||||
}}>
|
||||
Apply
|
||||
Apply
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -152,6 +152,11 @@
|
|||
top: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.slider-option{
|
||||
width: fit-content;
|
||||
margin: 10px 10px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive adjustments for the settings */
|
||||
|
|
Loading…
Reference in a new issue