Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,22 +72,51 @@ <h1 id="headline"><a href="/" style="color: inherit;">Terminal.css</a></h1>
<div id="settings-terminal-theme">
<div class="settings-row" style="font-weight: 600;">
<div class="settings-cell">
<div class="settings-label" style="text-transform: uppercase;">Color Schema Presets</div>
<div class="settings-input"><select id="presets" name="presets"></select></div>
<div class="settings-label" style="text-transform: uppercase;">Color Schema Presets (light)</div>
<div class="settings-input"><select id="presets-light" name="presets-light"></select></div>
</div>
</div>
<div class="settings-row" style="font-weight: 600;">
<div class="settings-cell">
<div class="settings-label" style="text-transform: uppercase;">Color Schema Presets (dark)</div>
<div class="settings-input"><select id="presets-dark" name="presets-dark"></select></div>
</div>
</div>
<div class="settings-row">
<div class="settings-cell">
<div class="settings-label">Background (light)</div>
<div class="settings-input"><input type="color" name="background-light" oninput="updateLivePreview()"></div>
</div>
<div class="settings-cell">
<div class="settings-label">Foreground (light)</div>
<div class="settings-input"><input type="color" name="foreground-light" oninput="updateLivePreview()"></div>
</div>
<div class="settings-cell">
<div class="settings-label">Accent (light)</div>
<div class="settings-input"><input type="color" name="accent-light" oninput="updateLivePreview()"></div>
</div>
</div>
<div class="settings-row">
<div class="settings-cell">
<div class="settings-label">Background</div>
<div class="settings-input"><input type="color" name="background" oninput="setVariable('--background', this.value)"></div>
<div class="settings-label">Background (dark)</div>
<div class="settings-input"><input type="color" name="background-dark" oninput="updateLivePreview()"></div>
</div>
<div class="settings-cell">
<div class="settings-label">Foreground</div>
<div class="settings-input"><input type="color" name="foreground" oninput="setVariable('--foreground', this.value)"></div>
<div class="settings-label">Foreground (dark)</div>
<div class="settings-input"><input type="color" name="foreground-dark" oninput="updateLivePreview()"></div>
</div>
<div class="settings-cell">
<div class="settings-label">Accent</div>
<div class="settings-input"><input type="color" name="accent" oninput="setVariable('--accent', this.value)"></div>
<div class="settings-label">Accent (dark)</div>
<div class="settings-input"><input type="color" name="accent-dark" oninput="updateLivePreview()"></div>
</div>
</div>
<div class="settings-row">
<div class="settings-cell">
<div class="settings-label">Preview Theme</div>
<div class="settings-input">
<label style="margin-right: 20px;"><input type="radio" name="preview-theme" style="min-height: 1em; margin-right: 10px;" value="light" onchange="updateLivePreview()" checked> Light</label>
<label><input type="radio" name="preview-theme" style="min-height: 1em; margin-right: 10px;" value="dark" onchange="updateLivePreview()"> Dark</label>
</div>
</div>
</div>
</div>
Expand Down
54 changes: 48 additions & 6 deletions modules/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,35 @@ export class Standalone {
}

updateVariables() {
// Get light theme values (use names without '-light' suffix for standalone)
const bgLight = this.formData.get("background-light") || defaultValues["background"];
const fgLight = this.formData.get("foreground-light") || defaultValues["foreground"];
const accentLight = this.formData.get("accent-light") || defaultValues["accent"];

// Get dark theme values
const bgDark = this.formData.get("background-dark") || bgLight; // Default dark to light if not provided
const fgDark = this.formData.get("foreground-dark") || fgLight;
const accentDark = this.formData.get("accent-dark") || accentLight;

// Construct the hybrid CSS variables block
const variables = `:root {
--background: ${this.formData.get("background")};
--foreground: ${this.formData.get("foreground")};
--accent: ${this.formData.get("accent")};
--background: ${bgLight};
--foreground: ${fgLight};
--accent: ${accentLight};
--radius: ${defaultValues["radius"]};
--font-size: ${defaultValues["fontSize"]};
--line-height: ${defaultValues["lineHeight"]};
}

@media (prefers-color-scheme: dark) {
:root {
--background: ${bgDark};
--foreground: ${fgDark};
--accent: ${accentDark};
--radius: ${defaultValues["radius"]};
--font-size: ${defaultValues["fontSize"]};
--line-height: ${defaultValues["lineHeight"]};
}
}`;
this.styles = this.styles.replace(components.variables, variables.trim());
return this;
Expand Down Expand Up @@ -93,11 +115,31 @@ export class TerminalTheme {
}

updateVariables() {
// Get light theme values
const bgLight = this.formData.get("background-light") || defaultValues["background"];
const fgLight = this.formData.get("foreground-light") || defaultValues["foreground"];
const accentLight = this.formData.get("accent-light") || defaultValues["accent"];

// Get dark theme values
const bgDark = this.formData.get("background-dark") || bgLight; // Default dark to light if not provided
const fgDark = this.formData.get("foreground-dark") || fgLight;
const accentDark = this.formData.get("accent-dark") || accentLight;

// Construct the hybrid CSS variables block
const variables = `:root {
--background: ${this.formData.get("background")};
--foreground: ${this.formData.get("foreground")};
--accent: ${this.formData.get("accent")};
--background: ${bgLight};
--foreground: ${fgLight};
--accent: ${accentLight};
}

@media (prefers-color-scheme: dark) {
:root {
--background: ${bgDark};
--foreground: ${fgDark};
--accent: ${accentDark};
}
}`;
// Replace the placeholder in the base styles
this.styles = this.styles.replace(components.variables, variables.trim());
return this;
}
Expand Down
102 changes: 73 additions & 29 deletions modules/presets.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,89 @@
import { isDark } from "./helpers.js";

export const presetsInput = document.querySelector("#presets");
// Get references to both preset dropdowns
const presetsInputLight = document.querySelector("#presets-light");
const presetsInputDark = document.querySelector("#presets-dark");
const colorInputsLight = {
background: document.querySelector('input[name="background-light"]'),
foreground: document.querySelector('input[name="foreground-light"]'),
accent: document.querySelector('input[name="accent-light"]'),
};
const colorInputsDark = {
background: document.querySelector('input[name="background-dark"]'),
foreground: document.querySelector('input[name="foreground-dark"]'),
accent: document.querySelector('input[name="accent-dark"]'),
};


const res = await fetch("./presets.json");
const presets = await res.json();

// At the moment it takes less than 1ms to create a grouped list of presets.
// If it gets worse over time, I'll rewrite it.
const presetsList = [];
for (const [k, v] of Object.entries(presets)) {
const entry = v;
entry.name = k;
presetsList.push(entry);
}
// Populate function
function populatePresets(selectElement) {
// Add a default "Select Preset..." option
const defaultOption = new Option("Select Preset...", "");
defaultOption.disabled = true;
defaultOption.selected = true;
selectElement.add(defaultOption);

const grouped = Object.groupBy(presetsList, ({ background }) => {
return isDark(background) ? "Dark" : "Light";
});
const presetsList = [];
for (const [k, v] of Object.entries(presets)) {
const entry = { ...v, name: k }; // Clone and add name
presetsList.push(entry);
}

for (const [group, p] of Object.entries(grouped)) {
const groupOption = new Option(group, group);
groupOption.disabled = true;
presetsInput.add(groupOption, undefined);
const grouped = Object.groupBy(presetsList, ({ background }) => {
return isDark(background) ? "Dark" : "Light";
});

for (const v of p) {
const option = new Option(v.name, v.name);
presetsInput.add(option, undefined);
// Ensure consistent group order (Light first)
const groupOrder = ["Light", "Dark"];
for (const group of groupOrder) {
if (grouped[group]) {
const groupOption = new Option(group, group);
groupOption.disabled = true;
selectElement.add(groupOption, undefined);

for (const v of grouped[group]) {
const option = new Option(v.name, v.name);
selectElement.add(option, undefined);
}
}
}
}
// -----------------------------------------------------------------------------

const root = document.querySelector(":root");
const settings = document.querySelectorAll("#settings input");
// Populate both dropdowns
populatePresets(presetsInputLight);
populatePresets(presetsInputDark);

presetsInput.addEventListener("change", e => {
const preset = presets[e.currentTarget.value];
// Event listener for light presets
presetsInputLight.addEventListener("change", e => {
const presetName = e.currentTarget.value;
if (!presetName || !presets[presetName]) return; // Ignore if default or invalid

for (const i of settings) {
if (preset[i.name]) {
i.value = preset[i.name];
root.style.setProperty(`--${i.name}`, preset[i.name]);
}
const preset = presets[presetName];
if (preset.background) colorInputsLight.background.value = preset.background;
if (preset.foreground) colorInputsLight.foreground.value = preset.foreground;
if (preset.accent) colorInputsLight.accent.value = preset.accent;

// Trigger live preview update (function defined in script.js)
if (window.updateLivePreview) {
window.updateLivePreview();
}
});

// Event listener for dark presets
presetsInputDark.addEventListener("change", e => {
const presetName = e.currentTarget.value;
if (!presetName || !presets[presetName]) return; // Ignore if default or invalid

const preset = presets[presetName];
if (preset.background) colorInputsDark.background.value = preset.background;
if (preset.foreground) colorInputsDark.foreground.value = preset.foreground;
if (preset.accent) colorInputsDark.accent.value = preset.accent;

// Trigger live preview update (function defined in script.js)
if (window.updateLivePreview) {
window.updateLivePreview();
}
});
62 changes: 54 additions & 8 deletions script.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,64 @@ import "./modules/presets.js";
// Init ------------------------------------------------------------------------
const root = document.querySelector(":root");

const settings = document.querySelectorAll("#settings input");
for (const i of settings) {
if (defaultValues[i.name]) {
i.value = defaultValues[i.name];
}
// Get references to all relevant inputs once
const colorInputsLight = {
background: document.querySelector('input[name="background-light"]'),
foreground: document.querySelector('input[name="foreground-light"]'),
accent: document.querySelector('input[name="accent-light"]'),
};
const colorInputsDark = {
background: document.querySelector('input[name="background-dark"]'),
foreground: document.querySelector('input[name="foreground-dark"]'),
accent: document.querySelector('input[name="accent-dark"]'),
};
const previewThemeRadios = document.querySelectorAll('input[name="preview-theme"]');
const headingStyleSelect = document.querySelector('select[name="headingStyle"]');
const fontFamilySelect = document.querySelector('select[name="fontFamily"]');

// Set initial default values for color inputs
if (defaultValues["background"]) colorInputsLight.background.value = defaultValues["background"];
if (defaultValues["foreground"]) colorInputsLight.foreground.value = defaultValues["foreground"];
if (defaultValues["accent"]) colorInputsLight.accent.value = defaultValues["accent"];
// Optionally set defaults for dark theme as well, or leave them empty/black/white
// For now, let's mirror the light theme defaults initially
if (defaultValues["background"]) colorInputsDark.background.value = defaultValues["background"];
if (defaultValues["foreground"]) colorInputsDark.foreground.value = defaultValues["foreground"];
if (defaultValues["accent"]) colorInputsDark.accent.value = defaultValues["accent"];

// Set other defaults if they exist in defaultValues
if (headingStyleSelect && defaultValues[headingStyleSelect.name]) {
headingStyleSelect.value = defaultValues[headingStyleSelect.name];
}
if (fontFamilySelect && defaultValues[fontFamilySelect.name]) {
fontFamilySelect.value = defaultValues[fontFamilySelect.name];
}

// -----------------------------------------------------------------------------

function setVariable(variable, value) {
root.style.setProperty(variable, value);
// Live Preview Update Function
function updateLivePreview() {
const selectedTheme = document.querySelector('input[name="preview-theme"]:checked').value;
let sourceInputs;

if (selectedTheme === 'dark') {
sourceInputs = colorInputsDark;
} else {
sourceInputs = colorInputsLight;
}

root.style.setProperty('--background', sourceInputs.background.value);
root.style.setProperty('--foreground', sourceInputs.foreground.value);
root.style.setProperty('--accent', sourceInputs.accent.value);

// Note: Other variables like --radius, --font-size, --line-height are not
// dynamically updated in this preview but will be included in the download.
// If previewing them is needed, this function would need expansion.
}
window.setVariable = setVariable;
window.updateLivePreview = updateLivePreview; // Make it globally accessible

// Initial preview update on load
updateLivePreview();

// Submit Download
const form = document.querySelector("#settings");
Expand Down