[REPO REFACTOR]: changed to a better git repository structure with branches

This commit is contained in:
2025-11-01 05:49:49 +01:00
parent 4d0f44e995
commit 589215b2bc
76 changed files with 3529 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
import Editor from "@monaco-editor/react";
import { useTheme } from "@/hooks/useTheme";
import { useRef } from "react";
import PropTypes from "prop-types";
import { loader } from '@monaco-editor/react';
loader.config({
'vs/nls': {
availableLanguages: { '*': 'es' },
},
});
const CodeEditor = ({ className = "", syntax, readOnly, onChange, value }) => {
const { theme } = useTheme();
const editorRef = useRef(null);
const onMount = (editor) => {
editorRef.current = editor;
editor.focus();
}
return (
<div className={`code-editor ${className}`}>
<Editor
language={syntax || "plaintext"}
value={value || ""}
theme={theme === "dark" ? "vs-dark" : "vs-light"}
onChange={(value) => onChange?.(value)}
onMount={onMount}
options={{
minimap: { enabled: false },
automaticLayout: true,
fontFamily: 'Fira Code',
fontLigatures: true,
fontSize: 18,
lineHeight: 1.5,
scrollbar: { verticalScrollbarSize: 0 },
wordWrap: "on",
formatOnPaste: true,
suggest: {
showFields: true,
showFunctions: true,
},
readOnly: readOnly || false,
}}
/>
</div>
);
};
CodeEditor.propTypes = {
className: PropTypes.string,
syntax: PropTypes.string,
readOnly: PropTypes.bool,
onChange: PropTypes.func,
value: PropTypes.string,
};
export default CodeEditor;

View File

@@ -0,0 +1,235 @@
import { useState, useEffect } from "react";
import { Form, Button, Row, Col, FloatingLabel, Alert } from "react-bootstrap";
import '@/css/PastePanel.css';
import PasswordInput from "@/components/Auth/PasswordInput";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCode, faHeader } from "@fortawesome/free-solid-svg-icons";
import CodeEditor from "./CodeEditor";
import PublicPasteItem from "./PublicPasteItem";
import { useParams, useNavigate } from "react-router-dom";
import { useDataContext } from "@/hooks/useDataContext";
import PasswordModal from "@/components/Auth/PasswordModal.jsx";
const PastePanel = ({ onSubmit, publicPastes }) => {
const { paste_key } = useParams();
const navigate = useNavigate();
const { getData } = useDataContext();
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [syntax, setSyntax] = useState("");
const [burnAfter, setBurnAfter] = useState(false);
const [isPrivate, setIsPrivate] = useState(false);
const [password, setPassword] = useState("");
const [selectedPaste, setSelectedPaste] = useState(null);
const [error, setError] = useState(null);
const [showPasswordModal, setShowPasswordModal] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
const paste = {
title,
content,
syntax,
burn_after: burnAfter,
is_private: isPrivate,
password: password || null,
};
if (onSubmit) onSubmit(paste);
};
const handleSelectPaste = async (key) => {
navigate(`/${key}`);
};
const fetchPaste = async (key, pwd = "") => {
const url = `https://api.miarma.net/mpaste/v1/pastes/${key}`;
const { data, error } = await getData(url, {}, {
'X-Paste-Password': pwd
});
if (error) {
if (error?.status === 401) {
setShowPasswordModal(true);
return;
} else {
setError(error);
setSelectedPaste(null);
return;
}
}
setError(null);
setSelectedPaste(data);
setTitle(data.title);
setContent(data.content);
setSyntax(data.syntax || "plaintext");
};
useEffect(() => {
if (paste_key) fetchPaste(paste_key);
}, [paste_key]);
return (
<>
<div className="paste-panel border-0 flex-fill d-flex flex-column min-h-0 p-3">
{error &&
<Alert variant="danger" onClose={() => setError(null)} dismissible>
<strong>
<span className="text-danger">{
error.status == 404 ? "404: Paste no encontrada." :
"Ha ocurrido un error al cargar la paste."
}</span>
</strong>
</Alert>
}
<Form onSubmit={handleSubmit} className="flex-fill d-flex flex-column min-h-0">
<Row className="g-3 flex-fill min-h-0">
<Col xs={12} lg={2} className="order-last order-lg-first d-flex flex-column flex-fill min-h-0 overflow-hidden">
<div className="public-pastes d-flex flex-column flex-fill overflow-hidden">
<h4>pastes públicas</h4>
<hr />
<div className="overflow-auto flex-fill" style={{ scrollbarWidth: 'none' }}>
{publicPastes && publicPastes.length > 0 ? (
publicPastes.map((paste) => (
<PublicPasteItem
key={paste.paste_key}
paste={paste}
onSelect={handleSelectPaste}
/>
))
) : (
<p>No hay pastes públicas disponibles.</p>
)}
</div>
</div>
</Col>
<Col xs={12} lg={7} className="d-flex flex-column flex-fill min-h-0 overflow-hidden">
<CodeEditor
className="flex-fill custom-border rounded-4 overflow-hidden pt-4 pe-4"
syntax={syntax}
readOnly={!!selectedPaste}
onChange={selectedPaste ? undefined : setContent}
value={content}
/>
</Col>
<Col xs={12} lg={3} className="d-flex flex-column flex-fill min-h-0 overflow-hidden">
<div className="d-flex flex-column flex-fill gap-3 overflow-auto">
<FloatingLabel
controlId="titleInput"
label={
<span className={selectedPaste ? "text-white" : ""}>
<FontAwesomeIcon icon={faHeader} className="me-2" />
Título
</span>
}
>
<Form.Control
disabled={!!selectedPaste}
type="text"
placeholder="Título de la paste"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</FloatingLabel>
<FloatingLabel
controlId="syntaxSelect"
label={
<>
<FontAwesomeIcon icon={faCode} className="me-2" />
Sintaxis
</>
}
>
<Form.Select
disabled={!!selectedPaste}
value={syntax}
onChange={(e) => setSyntax(e.target.value)}
>
<option value="">Sin resaltado</option>
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
<option value="java">Java</option>
<option value="c">C</option>
<option value="cpp">C++</option>
<option value="bash">Bash</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="sql">SQL</option>
<option value="julia">Julia</option>
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="yaml">YAML</option>
<option value="php">PHP</option>
<option value="ruby">Ruby</option>
<option value="go">Go</option>
<option value="rust">Rust</option>
<option value="typescript">TypeScript</option>
<option value="kotlin">Kotlin</option>
<option value="swift">Swift</option>
<option value="csharp">C#</option>
<option value="perl">Perl</option>
<option value="r">R</option>
<option value="dart">Dart</option>
<option value="lua">Lua</option>
<option value="haskell">Haskell</option>
<option value="scala">Scala</option>
<option value="objectivec">Objective-C</option>
</Form.Select>
</FloatingLabel>
<Form.Check
type="switch"
disabled={!!selectedPaste}
id="burnAfter"
label="volátil"
checked={burnAfter}
onChange={(e) => setBurnAfter(e.target.checked)}
className="ms-1 d-flex gap-2 align-items-center"
/>
<Form.Check
type="switch"
disabled={!!selectedPaste}
id="isPrivate"
label="privado"
checked={isPrivate}
onChange={(e) => setIsPrivate(e.target.checked)}
className="ms-1 d-flex gap-2 align-items-center"
/>
{isPrivate && (
<PasswordInput onChange={(e) => setPassword(e.target.value)} />
)}
<div className="d-flex justify-content-end">
<Button
variant="primary"
type="submit"
disabled={!!selectedPaste}
>
Crear paste
</Button>
</div>
</div>
</Col>
</Row>
</Form>
</div>
<PasswordModal
show={showPasswordModal}
onClose={() => setShowPasswordModal(false)}
onSubmit={(pwd) => {
setShowPasswordModal(false);
fetchPaste(paste_key, pwd); // reintentas con la pass
}}
/>
</>
);
};
export default PastePanel;

View File

@@ -0,0 +1,29 @@
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
const trimContent = (text, maxLength = 80) => {
if (!text) return "";
return text.length <= maxLength ? text : text.slice(0, maxLength) + "...";
};
const PublicPasteItem = ({ paste, onSelect }) => {
return (
<div className="public-paste-item p-2 mb-2 rounded custom-border" style={{ cursor: "pointer" }} onClick={() => onSelect(paste.paste_key)}>
<h5 className="m-0">{paste.title}</h5>
<p className="m-0 text-truncate">{trimContent(paste.content, 100)}</p>
<small className="custom-text-muted">
{new Date(paste.created_at).toLocaleString()}
</small>
</div>
);
};
PublicPasteItem.propTypes = {
paste: PropTypes.shape({
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
created_at: PropTypes.string.isRequired,
}).isRequired,
};
export default PublicPasteItem;