Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F1374866
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/builders/colors-builder.ts b/src/builders/colors-builder.ts
index 6f89259..5748a73 100644
--- a/src/builders/colors-builder.ts
+++ b/src/builders/colors-builder.ts
@@ -1,461 +1,466 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { build } from "esbuild";
import tempfile from "tempfile";
import { promises as fs } from "fs";
import { resolve } from "path";
import { colord } from "colord";
import { formatWithPrettier } from "../utils/prettier.js";
import { Builder } from "./builder.js";
import { exec } from "../utils/exec.js";
import { APP_BACK_ALIVE_SIGNAL } from "../notifier.js";
export const COLORS_TS_PATH = "src/back/colors.ts"; // keeping it in src/back instead of just src to make typescript hints work there
export const COLORS_CSS_PATH = "src/colors.css";
export const COLORS_HTML_PATH = "public/dist/colors.html";
export class ColorsBuilder extends Builder {
getName(): string {
return "colors";
}
ownsFile(file_path: string) {
return file_path == COLORS_TS_PATH;
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
async dispose(): Promise<void> {}
async _build() {
const outfile = tempfile({ extension: "mjs" });
await build({
entryPoints: [COLORS_TS_PATH],
bundle: true,
format: "esm",
outfile,
});
await fs.appendFile(
outfile,
/* HTML */ `<script>
console.log(
JSON.stringify(
Object.fromEntries(
Object.entries(colors).map(
([group_name, group]) => [
group_name,
Object.fromEntries(
Object.entries(group).map(
([color_name, color]) => {
const shades = [];
for (let i = 0; i <= 9; i++) {
shades.push(
shade(color, i)
);
}
return [
color_name,
{ color, shades },
];
}
)
),
]
)
)
)
);
</script>`.replaceAll(/<\/?script>/g, "") // <script> helps Prettier format this snippet
);
const output = await exec("node", [outfile]);
const color_groups = JSON.parse(output.stdout) as Record<
string,
Record<string, { color: string; shades: string[] }>
>;
let css_colors = "";
let html_colors = "";
function html_color_box({
color,
shade_no,
name,
group_name,
is_main = false,
}: {
color: string;
shade_no: string;
name: string;
group_name: string;
is_main?: boolean;
}) {
const c = colord(color);
return /* HTML */ `<div
class="color-box ${c.isDark() ? "is-dark" : ""} ${is_main
? "is-main"
: ""} ${c.toHsl().l > 90 ? "is-almost-white" : ""}"
style="background-color: ${color}; order: ${parseInt(shade_no)}"
data-color-name="${name}"
data-color-group-name="${group_name}"
data-color-shade-number="${shade_no}"
>
<span class="hex">${c.toHex()}</span>
</div>`;
}
for (const group_name of Object.keys(color_groups).sort((key) =>
key == "brand" ? -1 : 11
)) {
html_colors += `<h2>${group_name}</h2><div class="group">`;
const colors = color_groups[group_name];
for (const [color_name, color_info] of Object.entries(colors)) {
html_colors += `<h3>${color_name}</h3><div class="shades">`;
const main_color = color_info.color;
css_colors += `--color-${group_name}-${color_name}: ${main_color};`;
const main_l = colord(main_color).toHsl().l;
html_colors += html_color_box({
color: main_color,
name: color_name,
shade_no: Math.round((main_l - (main_l % 10)) / 10)
.toString()
.padStart(2, "0"),
is_main: true,
group_name,
});
color_info.shades.forEach((shaded_color, index) => {
const shade_no = index.toString().padStart(2, "0");
css_colors += `--color-${group_name}-${color_name}-${shade_no}: ${shaded_color};`;
if (
Math.abs(
colord(shaded_color).toHsl().l -
colord(main_color).toHsl().l
) < 10
) {
// this color is already in HTML as the main color, skipping adding it to html to avoid duplication
return;
}
html_colors += html_color_box({
color: shaded_color,
shade_no,
name: color_name,
group_name,
});
});
html_colors += `</div>`;
}
html_colors += "</div>";
}
const css = await formatWithPrettier(
`/* DO NOT EDIT! This file is automatically generated by sealgen */
:root {
${css_colors}
}`,
"css"
);
function makeDemoPair(
fg: string,
bg: string,
text: string,
classname = ""
) {
return /* HTML */ ` <div
class="pair ${classname}"
data-fg="${fg}"
data-bg="${bg}"
style="background-color: ${bg}; color: ${fg}"
>
<span>${text}</span>
</div>`;
}
const html = await formatWithPrettier(
/* HTML */ `<!DOCTYPE html>
<html>
<head>
<title>Color palettes</title>
<style>
${css}
</style>
<style>
* {
font-family: sans-serif;
}
.shades {
display: flex;
}
.color-box {
width: 100px;
height: 100px;
transition: transform 50ms;
transition-timing-function: ease-in-out;
transform: scale(1);
cursor: pointer;
&:hover {
transform: scale(1.1);
z-index: 2;
.hex {
visibility: visible;
}
}
&.is-main {
height: 116px;
width: 116px;
margin-top: -8px;
box-shadow: 0px 0px 6px 2px white;
z-index: 1;
&:before {
font-weight: bold;
}
}
&.is-almost-white.is-main {
box-shadow: 0px 0px 6px 1px #00000078;
}
.hex {
padding: 8px;
font-family: Menlo, Consolas, Monaco,
Liberation Mono, Lucida Console,
monospace;
opacity: 0.5;
visibility: hidden;
}
&.is-dark .hex {
color: white;
}
}
.color-box:before {
content: attr(data-color-shade-number);
box-sizing: border-box;
display: block;
color: black;
font-size: 14px;
width: 100%;
padding: 10px;
opacity: 0.3;
}
.color-box.is-dark:before {
color: white;
}
@keyframes float-up {
from {
transform: translateY(0);
opacity: 1;
}
to {
transform: translateY(-20px);
opacity: 0;
}
}
.toast {
background-color: black;
position: absolute;
color: white;
border-radius: 8px;
padding: 8px;
animation: float-up 800ms;
animation-timing-function: ease-out;
z-index: 3;
}
.container {
width: 100vw;
display: flex;
flex-flow: row wrap;
gap: 16px;
@media (max-width: 1400px) {
.demo {
order: 1;
}
.pallete {
order: 2;
}
}
.canvas {
background-color: var(--color-brand-canvas);
display: flex;
flex-flow: column;
border: 1px dashed gray;
color: gray;
gap: 8px;
padding: 8px;
& > * {
padding: 8px;
color: black;
}
.pair.single span {
opacity: 0.5;
}
.pair.link span {
text-decoration: underline;
}
}
}
</style>
</head>
<body>
<div class="container">
<div class="pallete">${html_colors}</div>
<div class="demo">
<div class="canvas">
<span>canvas</span>
+ ${makeDemoPair(
+ "var(--color-brand-text-fg)",
+ "var(--color-brand-canvas)",
+ "text-fg on canvas"
+ )}
${makeDemoPair(
"var(--color-brand-text-fg)",
"var(--color-brand-text-bg)",
"text-fg on text-bg"
)}
${makeDemoPair(
"var(--color-brand-text-accent)",
"var(--color-brand-text-bg)",
"text-accent on text-bg"
)}
${makeDemoPair(
"var(--color-brand-link-fg)",
"var(--color-brand-text-bg)",
"link-fg on text-bg",
"link"
)}
${makeDemoPair(
"",
"var(--color-brand-accent)",
"accent",
"single"
)}
${makeDemoPair(
"",
"var(--color-brand-accent2)",
"accent2",
"single"
)}
${makeDemoPair(
"var(--color-brand-text-on-accent)",
"var(--color-brand-accent)",
"text-on-accent on accent"
)}
${makeDemoPair(
"var(--color-brand-text-on-accent2)",
"var(--color-brand-accent2)",
"text-on-accent2 on accent2"
)}
${makeDemoPair(
"var(--color-brand-link-on-accent)",
"var(--color-brand-accent)",
"link-on-accent on accent",
"link"
)}
${makeDemoPair(
"var(--color-brand-link-on-accent2)",
"var(--color-brand-accent2)",
"link-on-accent2 on accent2",
"link"
)}
</div>
</div>
</div>
</body>
<script>
function pop_toast() {
const toast = document.createElement("div");
document.body.appendChild(toast);
toast.classList.add("toast");
toast.textContent = "Copied!";
toast.style.setProperty(
"top",
event.clientY +
document.scrollingElement.scrollTop -
40 +
"px"
);
toast.style.setProperty(
"left",
event.clientX + "px"
);
toast.addEventListener("animationend", () => {
toast.remove();
});
}
document.addEventListener("click", (event) => {
const box = event.target.closest(".color-box");
if (box) {
const css_var = box.classList.contains(
"is-main"
)
? \`var(--color-\${box.getAttribute(
"data-color-group-name"
)}-\${box.getAttribute(
"data-color-name"
)})\`
: \`var(--color-\${box.getAttribute(
"data-color-group-name"
)}-\${box.getAttribute(
"data-color-name"
)}-\${box.getAttribute(
"data-color-shade-number"
)})\`;
navigator.clipboard.writeText(css_var);
pop_toast();
}
const pair = event.target.closest(".pair");
if (pair) {
let to_copy = "";
if (pair.classList.contains("single")) {
to_copy = pair.getAttribute("data-bg");
} else {
to_copy = \`background-color: \${pair.getAttribute(
"data-bg"
)};\\ncolor: \${pair.getAttribute(
"data-fg"
)};\`;
}
navigator.clipboard.writeText(to_copy);
pop_toast();
}
});
</script>
<script>
(async function () {
const response = await (
await fetch("/dist/notifier.json")
).json();
const ws = new WebSocket(
\`http://localhost:\${response.port}\`
);
ws.addEventListener("message", (event) => {
console.log(event);
if (event.data == "${APP_BACK_ALIVE_SIGNAL}") {
document.location = document.location; // refresh
}
});
})();
</script>
</html>`,
"html"
);
await Promise.all([
fs.writeFile(resolve(this.project_dir, COLORS_CSS_PATH), css),
fs.writeFile(resolve(this.project_dir, COLORS_HTML_PATH), html),
]);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Feb 25, 16:19 (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
610593
Default Alt Text
(11 KB)
Attached To
Mode
rSGEN sealgen
Attached
Detach File
Event Timeline
Log In to Comment