first commit

This commit is contained in:
天クマ 2026-02-06 20:23:32 -03:00
commit 843e6ea7e5
4 changed files with 600 additions and 0 deletions

BIN
background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

322
input.js Normal file
View file

@ -0,0 +1,322 @@
const expButton = document.getElementById("export");
class Variation {
constructor(name, text, color, textColor) {
this.name = name;
this.text = text;
this.color = color;
this.textColor = textColor;
}
split() {
return this.text.split(/\r?\n/);
}
}
let variations = [];
let userText = [];
let name = "new project";
const box = document.getElementById("inputBox");
const project = document.getElementById("project");
const newProject = document.getElementById("new");
const cpResult = document.getElementById("copy");
project.addEventListener('click', () => {
const result = prompt("Type the new name.", name);
if (result) {
name = result;
reload();
}
})
cpResult.addEventListener('click', async () => {
const text = userText.join("\n");
await navigator.clipboard.writeText(text);
alert(text);
})
const newVname = document.querySelector("#newVname");
const newVtext = document.querySelector("#newVtext");
const newVcolor = document.querySelector("#newVcolor");
const newVtcolor = document.querySelector("#newVtcolor");
const newVadd = document.querySelector("#newVadd");
const variationsList = document.querySelector("#variations");
newVadd.addEventListener('click', e => {
if (newVname.value && newVtext.value && newVcolor.value && newVtcolor.value) {
variations.push(new Variation(newVname.value, newVtext.value, newVcolor.value, newVtcolor.value));
reload();
} else {
alert("Fill all fields.")
}
})
expButton.addEventListener('click', () => {
const save = {
name: name,
vars: variations,
usrInput: userText
}
const json = JSON.stringify(save);
const blob = new Blob([json], {type: 'application/json'});
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = `lyGEN-${name}.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
})
const blankline = document.createElement("p");
blankline.textContent = "Blank line";
blankline.className = "stringParagraph blankline";
function closeAllPopups() {
const popups = document.querySelectorAll("aside");
popups.forEach(popup => {
popup.classList.add("closed");
});
}
function togglePopup(tag) {
const doc = document.querySelector(tag);
if (doc.classList.contains('closed')) {
closeAllPopups()
}
doc.classList.toggle("closed");
}
function reload() {
listVariations();
showVariations();
autosave();
project.textContent = name;
}
function populatePreview() {
const doc = document.querySelector("#previewTextBox");
doc.innerHTML = "";
userText.forEach(line => {
e = document.createElement('p');
e.textContent = line;
e.id = "previewText";
doc.appendChild(e);
});
}
newProject.addEventListener('click', () => {
if(confirm("The current project will be deleted, continue?")) {
variations = [];
name = "new project";
userText = [];
reload();
}
})
function createFileInput() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json,application/json';
input.style.display = 'none';
input.addEventListener('change', handleFileUpload);
document.body.appendChild(input);
input.click();
input.addEventListener('blur', () => {
document.body.removeChild(input);
});
}
const importButton = document.getElementById('import');
importButton.addEventListener('click', createFileInput);
function autosave() {
console.log("Autosaving...");
const save = {
name: name,
vars: variations,
usrInput: userText
}
const json = JSON.stringify(save);
localStorage.setItem("autosave", json);
}
function loadAutoSave() {
const saveData = localStorage.getItem("autosave");
if (saveData) {
try {
const save = JSON.parse(saveData);
variations = save.vars.map(v =>
new Variation(v.name, v.text, v.color, v.textColor)
);
name = save.name || "new project";
userText = save.usrInput || [];
reload();
} catch (error) {
console.error("Error loading autosave:", error);
localStorage.removeItem("autosave");
}
}
}
function handleFileUpload(event) {
const file = event.target.files[0];
if (file) {
if (file.type !== 'application/json' && !file.name.endsWith('.json')) {
alert('Please select a JSON file');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
try {
const saveData = JSON.parse(e.target.result);
if (!saveData.vars || !Array.isArray(saveData.vars)) {
throw new Error('Invalid file structure');
}
variations = saveData.vars.map(v =>
new Variation(
v.name || 'Unnamed Variation',
v.text || '',
v.color || '#FFFFFF',
v.textColor || '#000000'
)
);
name = saveData.name || "new project";
userText = saveData.usrInput || [];
reload();
alert('File successfully loaded');
} catch (error) {
console.error("Error parsing file:", error);
alert('Invalid file format. Please select a valid LyGEN project file.');
}
};
reader.onerror = function() {
alert('Error reading file');
};
reader.readAsText(file);
}
}
function listVariations() {
variationsList.innerHTML = "";
variations.forEach(variation => {
const div = document.createElement("div");
div.style.color = variation.textColor;
div.style.backgroundColor = variation.color;
div.className = "variation";
const pName = document.createElement("p");
pName.textContent = variation.name;
pName.className = "variationName";
const copy = document.createElement("p");
copy.textContent = "cp";
copy.className = "variationCopy";
const remove = document.createElement("p");
remove.textContent = "d";
remove.className = "variationDelete";
const up = document.createElement("p");
up.textContent = "↑";
up.className = "variationUp";
div.appendChild(pName);
div.appendChild(copy);
div.appendChild(remove);
div.appendChild(up);
variationsList.appendChild(div);
remove.addEventListener('click', () => {
variations = variations.filter(v => v !== variation);
reload();
})
copy.addEventListener('click', async () => {
await navigator.clipboard.writeText(variation.text);
alert(variation.text);
})
function moveItem(array, index, shift) {
if (index > 0) {
const item = array.splice(index, 1)[0];
array.splice(index + shift, 0, item);
}
return array;
}
up.addEventListener('click', () => {
variations = moveItem(variations, variations.indexOf(variation), -1);
reload();
})
});
}
function showVariations() {
box.innerHTML = '';
const maxLenght = Math.max(...variations.map(variation => variation.split().length));
for (let i = 0; i < maxLenght; i++) {
const pContainer = document.createElement("div");
pContainer.className = `paragraphContainer paragraphContainer${i}`;
var styleElem = document.head.appendChild(document.createElement("style"));
styleElem.innerHTML = `.paragraphContainer${i}:before {content: "${i + 1}";}`;
variations.forEach(variation => {
if (variation.split()[i]) {
const p = document.createElement("p");
p.className = `string-${variation.name}-paragraph stringParagraph`;
p.textContent = variation.split()[i];
p.style.backgroundColor = variation.color;
p.style.color = variation.textColor;
pContainer.appendChild(p);
} else {
pContainer.appendChild(blankline);
}
});
const ibox = document.createElement("input");
ibox.type = "text";
ibox.className = `ibox ibox${i}`
ibox.value = userText[i] ?? "";
ibox.dataset.lineNumber = i;
ibox.placeholder = "...";
ibox.addEventListener('change', () => {
userText[ibox.dataset.lineNumber] = ibox.value;
autosave();
});
ibox.addEventListener('change', () => {
populatePreview();
});
pContainer.appendChild(ibox);
box.appendChild(pContainer);
}
}
loadAutoSave();
populatePreview();
reload();

52
main.html Normal file
View file

@ -0,0 +1,52 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="input.js" defer></script>
<title>lyGEN</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<header>
<h1 class="movingGradient">lyGEN</h1>
<div id="headerOptions">
<p class="movingGradient">
<a id="new">New</a> -
<a id="export">Export</a> -
<a id="import">Import</a> -
<a id="copy">Copy result</a> -
<a id="optinsbtn" onclick="togglePopup('#options')">Options</a> -
<a id="optinsbtn" onclick="togglePopup('#preview')">Preview</a>
</p>
<p id="project"></p>
</div>
</header>
<div id="inputBox"></div>
</main>
<aside class="closed" id="options">
<h2>Options</h2>
<div id="settingsOptions">
<div id="newVariation">
<h3>New variation</h3>
<p>Name</p>
<input type="text" id="newVname">
<p>Content</p>
<textarea style="resize: vertical; height: 20em;" type="text" name="" id="newVtext"></textarea>
<p>Color</p>
<input type="color" name="" id="newVcolor">
<p>Text color</p>
<input type="color" name="" id="newVtcolor">
<button id="newVadd">Add</button>
</div>
<div id="variations"></div>
</div>
</aside>
<aside class="closed" id="Preview">
<h2>Preview</h2>
<div id="previewTextBox"></div>
</aside>
</body>
</html>

226
style.css Normal file
View file

@ -0,0 +1,226 @@
* {
padding: 0;
margin: 0;
box-sizing: border-box;
user-select: none;
/* scrollbar-width: thich; */
scrollbar-color: white black;
--border-contrast: darkgray;
}
h1, h2 {
margin-bottom: 1em;
}
#previewText {
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-word;
white-space: normal;
margin-bottom: .6em;
user-select: text;
}
#previewText::after {
content: "↩";
color: rgba(255, 255, 255, 0.4);
}
body {
background-color: black;
color: white;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.stringParagraph {
width: fit-content;
padding: .2em;
border-left: medium solid white;
transition: .2s;
}
/* .stringParagraph:hover {
border-left-width: 1.05em;
}
.stringParagraph:hover::before {
content: ">";
color: black;
margin-left: -1em;
} */
.paragraphContainer::before {
color: white;
/* text-decoration:underline; */
border: medium solid white;
border-bottom: none;
padding: .2em;
padding-bottom: 0px;
}
#inputBox {
display: flex;
flex-direction: column;
gap: 1em;
padding: 1em;
padding-right: 1em;
}
.ibox {
transition: .2s;
width: 100%;
border: none;
border-left: medium solid white;
font-size: 1em;
color: lightgray;
}
.ibox:focus {
border-left-width: 1em;
outline: none;
min-width: calc(3ch + 1em);
}
input, textarea, button {
transition: .2s;
background-color: black;
border: medium solid var(--border-contrast);
color: white;
padding: .2em;
resize: none;
}
input:focus, textarea:focus, button:hover, button:focus {
outline: none;
background-color: white;
color: black;
}
body {
display: flex;
}
main {
flex-grow: 1;
overflow-x:hidden;
}
aside h2 {
font-size: xx-large;
}
aside {
position: fixed;
height: 100vh;
right: 0;
transition: 1s;
width: 25vw;
padding: 1em;
background-color: white;
color: black;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
overflow-y: auto;
}
aside::before {
filter: grayscale(1);
content: "";
position: absolute;
inset: 0;
opacity: .2;
background-size: contain;
background-position: left bottom;
background-image: url('/background.png');
background-repeat: no-repeat;
z-index: 0;
pointer-events: none
}
aside > * {
position: relative;
z-index: 1;
}
aside.closed {
transform: translateX(100%);
pointer-events: none;
}
header {
padding: .4em;
border-bottom: medium solid white;
display: flex;
}
header h1 {
margin-bottom: 0px;
margin-right: .6em;
}
#headerOptions a {
cursor: pointer;
}
#settingsOptions {
display: flex;
flex-direction: column;
gap: 1em;
}
#newVariation {
display: flex;
flex-direction: column;
gap: .4em;
}
.blankline {
font-style: italic;
}
#variations {
display: flex;
flex-direction: column;
gap: 1em;
}
.variation {
display: flex;
border: medium solid white;
padding: .4em;
}
.variationName {
flex-grow: 1;
}
.variationDelete {
color: red;
cursor: pointer;
}
#project {
color: lightgray;
}
.movingGradient {
background: linear-gradient(to right, white, darkgray, white);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: movingGradientAnimation 6s ease-in-out infinite;
background-size: 400% 100%;
}
@keyframes movingGradientAnimation {
0%,100% {
background-position: 0 0;
}
50% {
background-position: 100% 0;
}
}