This commit is contained in:
Rajyavardhan Singh 2025-10-29 18:08:07 +07:00 committed by GitHub
commit e1e0f6e23e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 361 additions and 7 deletions

View file

@ -486,6 +486,8 @@
<i class="fas fa-folder"></i>
saved texts
</div>
</div>
<div class="buttonsTop2">
<input id="fileInput" type="file" class="hidden" accept=".txt" />
<label for="fileInput" class="button importText">
<i class="fas fa-file-import"></i>
@ -495,6 +497,10 @@
<i class="fas fa-filter"></i>
words filter
</div>
<div class="button customGenerator">
<i class="fas fa-cogs"></i>
custom generator
</div>
</div>
<div class="savedTexts hidden" style="display: none">
<div class="title">saved texts</div>
@ -759,7 +765,7 @@
<select class="layoutInput"></select>
</div>
<!-- <div class="tip">Use the dropdowns above to generate presets</div> -->
<button class="generateButton">generate</button>
<button class="generateButton">apply</button>
</div>
<div class="bottom">
@ -772,6 +778,79 @@
</div>
</div>
</dialog>
<dialog id="customGeneratorModal" class="modalWrapper hidden">
<div class="modal">
<div class="main">
<div class="group">
<div class="title">presets</div>
<select class="presetInput">
<option value="alphas">a-z</option>
<option value="numbers">0-9</option>
<option value="special">symbols</option>
<option value="bigrams">bigrams</option>
<option value="trigrams">trigrams</option>
</select>
<button class="generateButton">apply</button>
</div>
<div class="separator"></div>
<div class="tip">
Enter characters or strings separated by spaces. Random combinations
will be generated using these inputs.
</div>
<div class="group">
<div class="title">character set</div>
<textarea
class="characterInput"
id="characterInput"
autocomplete="off"
placeholder=""
title="characters"
></textarea>
</div>
<div class="group lengthgrid">
<div class="title">min length</div>
<div class="title">max length</div>
<input
class="wordLength minLengthInput"
autocomplete="off"
type="number"
value="2"
min="1"
title="min"
/>
<input
class="wordLength maxLengthInput"
autocomplete="off"
type="number"
value="5"
min="1"
title="max"
/>
</div>
<div class="group">
<div class="title">word count</div>
<input
class="wordCountInput"
autocomplete="off"
type="number"
value="100"
min="1"
title="word count"
/>
</div>
</div>
<div class="bottom">
<div class="tip">
"Set" replaces the current custom text with generated words, "Add"
appends generated words to the current custom text.
</div>
<button class="setButton">set</button>
<button class="addButton">add</button>
</div>
</div>
</dialog>
<dialog id="googleSignUpModal" class="modalWrapper hidden">
<form class="modal">
<div class="title">Account name</div>

View file

@ -15,6 +15,13 @@
#testModesNotice {
font-size: 0.8rem;
}
#customTextModal {
.modal {
.buttonsTop2 {
grid-template-columns: 1fr;
}
}
}
#bannerCenter {
font-size: 0.85rem;
.banner.withImage {

View file

@ -231,6 +231,7 @@
.modal {
grid-template-areas:
"topButtons topButtons"
"topButtons2 topButtons2"
"textArea textArea"
"checkboxes checkboxes"
"ok ok";

View file

@ -43,9 +43,9 @@
.buttonsTop {
grid-template-columns: 1fr 1fr;
}
textarea {
min-height: 426px;
}
// textarea {
// min-height: 426px;
// }
}
}
.testActivity {

View file

@ -106,15 +106,20 @@ body.darkMode {
// "ok ok ok";
grid-template-areas:
"topButtons topButtons checkboxes"
"topButtons2 topButtons2 checkboxes"
"textArea textArea checkboxes"
"ok ok checkboxes";
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: min-content 1fr min-content;
grid-template-rows: min-content min-content 1fr min-content;
.buttonsTop {
grid-area: topButtons;
}
.buttonsTop2 {
grid-area: topButtons2;
}
.textAreaWrapper {
grid-area: textArea;
}
@ -170,7 +175,13 @@ body.darkMode {
.buttonsTop {
display: grid;
// grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.buttonsTop2 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 1rem;
}
@ -199,7 +210,7 @@ body.darkMode {
width: 100%;
border-radius: var(--roundness);
resize: vertical;
min-height: 589px;
min-height: 489px;
color: var(--text-color);
overflow-x: hidden;
overflow-y: scroll;
@ -445,6 +456,70 @@ body.darkMode {
}
}
#customGeneratorModal {
.modal {
max-width: 600px;
.main {
display: grid;
gap: 1.5rem;
}
.bottom {
display: grid;
gap: 1rem;
margin-top: 1rem;
}
.separator {
height: 0.25rem;
width: 100%;
background-color: var(--sub-alt-color);
border-radius: var(--roundness);
}
.group {
display: grid;
gap: 0.5rem;
.title {
color: var(--sub-color);
}
}
.lengthgrid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto 1fr;
column-gap: 1rem;
}
.tip {
color: var(--sub-color);
font-size: 0.8rem;
}
input,
textarea {
width: 100%;
padding: 0.5rem;
background: var(--sub-alt-color);
color: var(--text-color);
border: none;
border-radius: var(--roundness);
&::placeholder {
color: var(--sub-color);
}
}
textarea {
min-height: 100px;
resize: vertical;
}
}
}
#quoteRateModal {
.modal {
max-width: 800px;

View file

@ -0,0 +1,184 @@
import * as CustomText from "../test/custom-text";
import * as Notifications from "../elements/notifications";
import SlimSelect from "slim-select";
import AnimatedModal, {
HideOptions,
ShowOptions,
} from "../utils/animated-modal";
type Preset = {
display: string;
characters: string[];
};
const presets: Record<string, Preset> = {
alphas: {
display: "a-z",
characters: "abcdefghijklmnopqrstuvwxyz".split(""),
},
numbers: {
display: "0-9",
characters: "0123456789".split(""),
},
special: {
display: "symbols",
characters: "!@#$%^&*()_+-=[]{}|;:',.<>?/`~".split(""),
},
bigrams: {
display: "bigrams",
characters: [
"th",
"he",
"in",
"er",
"an",
"re",
"on",
"at",
"en",
"nd",
"ed",
"es",
"or",
"te",
"st",
"ar",
"ou",
"it",
"al",
"as",
],
},
trigrams: {
display: "trigrams",
characters: [
"the",
"and",
"ing",
"ion",
"tio",
"ent",
"ati",
"for",
"her",
"ter",
"ate",
"ver",
"all",
"con",
"res",
"are",
"rea",
"int",
],
},
};
let _presetSelect: SlimSelect | undefined = undefined;
export async function show(showOptions?: ShowOptions): Promise<void> {
void modal.show({
...showOptions,
beforeAnimation: async (modalEl) => {
_presetSelect = new SlimSelect({
select: "#customGeneratorModal .presetInput",
settings: {
contentLocation: modalEl,
},
});
},
});
}
function applyPreset(): void {
const presetName = $("#customGeneratorModal .presetInput").val() as string;
if (presetName !== undefined && presetName !== "" && presets[presetName]) {
const preset = presets[presetName];
$("#customGeneratorModal .characterInput").val(preset.characters.join(" "));
}
}
function hide(hideOptions?: HideOptions<OutgoingData>): void {
void modal.hide({
...hideOptions,
});
}
function generateWords(): string[] {
const characterInput = $(
"#customGeneratorModal .characterInput"
).val() as string;
const minLength =
parseInt($("#customGeneratorModal .minLengthInput").val() as string) || 2;
const maxLength =
parseInt($("#customGeneratorModal .maxLengthInput").val() as string) || 5;
const wordCount =
parseInt($("#customGeneratorModal .wordCountInput").val() as string) || 100;
if (!characterInput || characterInput.trim() === "") {
Notifications.add("Character set cannot be empty", 0);
return [];
}
const characters = characterInput.trim().split(/\s+/);
const generatedWords: string[] = [];
for (let i = 0; i < wordCount; i++) {
const wordLength =
Math.floor(Math.random() * (maxLength - minLength + 1)) + minLength;
let word = "";
for (let j = 0; j < wordLength; j++) {
const randomChar =
characters[Math.floor(Math.random() * characters.length)];
word += randomChar;
}
generatedWords.push(word);
}
return generatedWords;
}
async function apply(set: boolean): Promise<void> {
const generatedWords = generateWords();
if (generatedWords.length === 0) {
return;
}
const customText = generatedWords.join(
CustomText.getPipeDelimiter() ? "|" : " "
);
hide({
modalChainData: {
text: customText,
set,
},
});
}
async function setup(modalEl: HTMLElement): Promise<void> {
modalEl.querySelector(".setButton")?.addEventListener("click", () => {
void apply(true);
});
modalEl.querySelector(".addButton")?.addEventListener("click", () => {
void apply(false);
});
modalEl.querySelector(".generateButton")?.addEventListener("click", () => {
applyPreset();
});
}
type OutgoingData = {
text: string;
set: boolean;
};
const modal = new AnimatedModal<unknown, OutgoingData>({
dialogId: "customGeneratorModal",
setup,
});

View file

@ -6,6 +6,7 @@ import * as ChallengeController from "../controllers/challenge-controller";
import Config, * as UpdateConfig from "../config";
import * as Strings from "../utils/strings";
import * as WordFilterPopup from "./word-filter";
import * as CustomGeneratorPopup from "./custom-generator";
import * as PractiseWords from "../test/practise-words";
import * as Notifications from "../elements/notifications";
import * as SavedTextsPopup from "./saved-texts";
@ -560,6 +561,13 @@ async function setup(modalEl: HTMLElement): Promise<void> {
modalChain: modal as AnimatedModal,
});
});
modalEl
.querySelector(".button.customGenerator")
?.addEventListener("click", () => {
void CustomGeneratorPopup.show({
modalChain: modal as AnimatedModal,
});
});
modalEl
.querySelector(".button.showSavedTexts")
?.addEventListener("click", () => {