diff --git a/.eleventy.js b/.eleventy.js index b788742..aa2abe7 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -1,168 +1,190 @@ -import dotenv from "dotenv" -import fs from "fs" -import path from "path" -import { DateTime } from "luxon" -import hljs from "highlight.js" -import markdownIt from "markdown-it" -import anchor from "markdown-it-anchor" -import bracketedSpans from "markdown-it-bracketed-spans" -import attrs from "markdown-it-attrs" -import toc from "markdown-it-table-of-contents" -import mark from "markdown-it-mark" -import rssPlugin from "@11ty/eleventy-plugin-rss" - -dotenv.config() - -export default function (eleventyConfig) { - const isDevelopment = process.env.NODE_ENV === "development" - const markdownItConfig = markdownIt({ - html: true, - linkify: true, - typographer: true, - highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - return hljs.highlight(str, { language: lang }).value - } catch (__) { } - } - return "" - }, - }) - .use(anchor, { - permalink: anchor.permalink.linkAfterHeader({ - style: "visually-hidden", - assistiveText: (title) => `Link to heading “${title}”`, - visuallyHiddenClass: "sr-only", - wrapper: ['
', '
'], - }), - }) - .use(bracketedSpans) - .use(attrs, { - leftDelimiter: "{", - rightDelimiter: "}", - allowedAttributes: [], - }) - .use(toc, { - includeLevel: [1, 2, 3], - containerHeaderHtml: `
Table of Contents
`, - }) - .use(mark) - - eleventyConfig.setLibrary("md", markdownItConfig) - eleventyConfig.addGlobalData("isDevelopment", isDevelopment) - - eleventyConfig.addPlugin(rssPlugin, { - posthtmlRenderOptions: { - closingSingleTag: "default", - }, - }) - - eleventyConfig.addPassthroughCopy({ - "src/assets/*.pdf": "assets", - "src/assets/images": "images", - "src/assets/js/*": "js", - "src/admin/*": "admin", - "settings.json": "settings.json", - "src/robots.txt": "robots.txt" - }) - - eleventyConfig.addFilter("postDate", (dateObj) => { - if (typeof dateObj !== "object") dateObj = new Date(dateObj) - return DateTime.fromJSDate(dateObj).toLocaleString(DateTime.DATE_MED) - }) - - eleventyConfig.addFilter("reverseGroupedPosts", (object) => - Object.entries(object).sort((a, b) => b[0] - a[0]) - ) - - eleventyConfig.addFilter("htmlDateString", (dateObj) => { - let date = new Date(dateObj) - return date.toISOString() - }) - - eleventyConfig.addFilter("postYear", (dateObj) => { - return DateTime.fromJSDate(dateObj).toLocaleString({ year: "numeric" }) - }) - - eleventyConfig.addFilter("sliceRecent", (array) => { - return array.slice(0, 3) - }) - - eleventyConfig.addFilter("clipText", (string, size = 12, separator = "-") => { - return string.split(separator).splice(0, size).join(separator) - }) - - eleventyConfig.addFilter("serializeTitle", (string, yearSplice = 2) => { - const titleParts = string.split("-") - const [year, ...title] = titleParts - const titleInitials = title.map((title) => title.split("")[0]).join("") - const yearSuffix = year.slice(-yearSplice) - - return `${yearSuffix}-${titleInitials}` - }) - - eleventyConfig.addFilter("development", (link) => { - return isDevelopment ? "/" : link - }) - - eleventyConfig.addFilter("breakLine", (string, cutAt = 3, maxSize = 30) => { - const titleWords = string.split(" ") - const titleLength = string.length - const titlePreview = titleWords.slice(0, cutAt).join(" ") - const titleRemaining = titleWords.slice(cutAt).join(" ") - - const hasTitleRemaining = !!titleRemaining - const formattedTitleWithBreak = hasTitleRemaining - ? `${titlePreview}
${titleRemaining}` - : titlePreview - - return titleLength <= maxSize || !hasTitleRemaining - ? string - : formattedTitleWithBreak - }) - - eleventyConfig.addFilter("svg", (fileName, index) => { - const filePath = path.join(process.cwd(), "src/assets/svg", fileName) - try { - let svgContent = fs.readFileSync(filePath, "utf8") - if (index) { - svgContent = svgContent.replace( - "` - } - }) - - eleventyConfig.addFilter("renderMarkdown", (text) => { - return markdownItConfig.render(text) - }) - - eleventyConfig.addFilter("renderMarkdownInline", (text) => { - return markdownItConfig.renderInline(text) - }) - - eleventyConfig.addFilter("featuredDate", (string) => { - let date = new Date(string) - return date.toLocaleDateString("en-us", { - year: "numeric", - month: "short", - day: "numeric", - }) - }) - - return { - templateFormats: ["md", "njk", "html", "liquid"], - markdownTemplateEngine: "liquid", - htmlTemplateEngine: "njk", - dataTemplateEngine: "njk", - dir: { - data: "_data", - input: "src", - output: "public", - }, - } +import fs from "fs" +import path from "path" +import dotenv from "dotenv" +import { DateTime } from "luxon" +import highlight from "highlight.js" +import highlightDiff from "highlightjs-code-diff" +import highlightjsLines from "./lib/highlightjs-lines.js" +import markdownIt from "markdown-it" +import anchor from "markdown-it-anchor" +import bracketedSpans from "markdown-it-bracketed-spans" +import attrs from "markdown-it-attrs" +import toc from "markdown-it-table-of-contents" +import mark from "markdown-it-mark" +import { eleventyImageTransformPlugin } from "@11ty/eleventy-img" +import rssPlugin from "@11ty/eleventy-plugin-rss" +import link from "./src/_data/link.json" with {type: 'json'} +dotenv.config() + +export default function (eleventyConfig) { + const isDevelopment = process.env.NODE_ENV === "development" + const hljs = highlightjsLines(highlightDiff(highlight)) + const markdownItConfig = markdownIt({ + html: true, + linkify: true, + typographer: true, + highlight: function (str, lang) { + const processLang = lang.replace("diff:", "") + if (lang && hljs.getLanguage(processLang)) { + try { + const returnValue = hljs.highlight(str, { language: lang }).value + return returnValue + } catch (__) { } + } + return "" + }, + }) + .use(anchor, { + permalink: anchor.permalink.linkAfterHeader({ + style: "visually-hidden", + assistiveText: (title) => `Link to heading “${title}”`, + visuallyHiddenClass: "sr-only", + wrapper: ['
', '
'], + }), + }) + .use(bracketedSpans) + .use(attrs, { + leftDelimiter: "{", + rightDelimiter: "}", + allowedAttributes: [], + }) + .use(toc, { + includeLevel: [1, 2, 3], + containerHeaderHtml: `
Table of Contents
`, + }) + .use(mark) + + eleventyConfig.setLibrary("md", markdownItConfig) + eleventyConfig.addGlobalData("isDevelopment", isDevelopment) + + eleventyConfig.addPlugin(rssPlugin, { + posthtmlRenderOptions: { + closingSingleTag: "default", + }, + }) + + eleventyConfig.addPlugin(eleventyImageTransformPlugin, { + outputDir: "./images" + }); + + eleventyConfig.addPassthroughCopy({ + "src/assets/*.pdf": "assets", + "src/assets/images": "images", + "src/admin/*": "admin", + "settings.json": "settings.json", + "src/robots.txt": "robots.txt", + "src/*.xsl": "/" + }) + + if (isDevelopment) { + eleventyConfig.addPassthroughCopy({ + "src/assets/js": "js", + }) + } + + eleventyConfig.addFilter("postDate", (dateObj) => { + if (typeof dateObj !== "object") dateObj = new Date(dateObj) + return DateTime.fromJSDate(dateObj).toLocaleString(DateTime.DATE_MED) + }) + + eleventyConfig.addFilter("reverseGroupedPosts", (object) => + Object.entries(object).sort((a, b) => b[0] - a[0]) + ) + + eleventyConfig.addFilter("htmlDateString", (dateObj) => { + let date = new Date(dateObj) + return date.toISOString() + }) + + eleventyConfig.addFilter("convertToValidURL", (url) => { + if (url.startsWith('http')) return url + const validURL = new URL(url.replace("\/assets\/",""), (isDevelopment) ? "http://localhost" : link.website) + return validURL + }) + + eleventyConfig.addFilter("postYear", (dateObj) => { + return DateTime.fromJSDate(dateObj).toLocaleString({ year: "numeric" }) + }) + + eleventyConfig.addFilter("sliceRecent", (array) => { + return array.slice(0, 3) + }) + + eleventyConfig.addFilter("clipText", (string, size = 12, separator = "-") => { + return string.split(separator).splice(0, size).join(separator) + }) + + eleventyConfig.addFilter("serializeTitle", (string, yearSplice = 2) => { + const titleParts = string.split("-") + const [year, ...title] = titleParts + const titleInitials = title.map((title) => title.split("")[0]).join("") + const yearSuffix = year.slice(-yearSplice) + + return `${yearSuffix}-${titleInitials}` + }) + + eleventyConfig.addFilter("development", (link) => { + return isDevelopment ? "/" : link + }) + + eleventyConfig.addFilter("breakLine", (string, cutAt = 3, maxSize = 30) => { + const titleWords = string.split(" ") + const titleLength = string.length + const titlePreview = titleWords.slice(0, cutAt).join(" ") + const titleRemaining = titleWords.slice(cutAt).join(" ") + + const hasTitleRemaining = !!titleRemaining + const formattedTitleWithBreak = hasTitleRemaining + ? `${titlePreview}
${titleRemaining}` + : titlePreview + + return titleLength <= maxSize || !hasTitleRemaining + ? string + : formattedTitleWithBreak + }) + + eleventyConfig.addFilter("svg", (fileName, index) => { + const filePath = path.join(process.cwd(), "src/assets/svg", fileName) + try { + let svgContent = fs.readFileSync(filePath, "utf8") + if (index) { + svgContent = svgContent.replace( + "` + } + }) + + eleventyConfig.addFilter("renderMarkdown", (text) => { + return markdownItConfig.render(text) + }) + + eleventyConfig.addFilter("renderMarkdownInline", (text) => { + return markdownItConfig.renderInline(text) + }) + + eleventyConfig.addFilter("featuredDate", (string) => { + let date = new Date(string) + return date.toLocaleDateString("en-us", { + year: "numeric", + month: "short", + day: "numeric", + }) + }) + + return { + templateFormats: ["md", "njk", "html", "liquid"], + markdownTemplateEngine: "liquid", + htmlTemplateEngine: "njk", + dataTemplateEngine: "njk", + dir: { + data: "_data", + input: "src", + output: "public", + }, + } } \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a5ad5f2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +src/pretty-atom-feed.xsl linguist-vendored \ No newline at end of file diff --git a/.github/workflows/highlightjs-lines.yml b/.github/workflows/highlightjs-lines.yml new file mode 100644 index 0000000..4a28bd1 --- /dev/null +++ b/.github/workflows/highlightjs-lines.yml @@ -0,0 +1,25 @@ +name: Highlightjs-lines Test + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x, 22.x, 24.x] + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + - run: npm install + - run: npm test diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..3eb1314 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 427103d..60b0dd0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,13 @@ /public .vscode -# /src/blog/example/* \ No newline at end of file +# /src/blog/example/* + +.cache + +# Playwright +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d2ba654 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +/node_modules +.env +.vscode +.cache +/**/sitemap.njk +/**/feed.njk diff --git a/.prettierrc b/.prettierrc index f00e0b0..6a63f7b 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,8 @@ { - "trailingComma": "es5", - "tabWidth": 2, - "semi": false, - "singleQuote": true - } \ No newline at end of file + "trailingComma": "es5", + "tabWidth": 2, + "semi": false, + "singleQuote": true, + "singleAttributePerLine": false, + "bracketSameLine": true +} diff --git a/LICENSE b/LICENSE index ce6324d..5fffaf6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Stevarth +Copyright (c) 2025 Stevarth Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.abs-ID.md b/README.abs-ID.md index ad31804..5fa83a1 100644 --- a/README.abs-ID.md +++ b/README.abs-ID.md @@ -10,6 +10,11 @@ en: This documents is not updated to the latest version. please read the english [![Repository Forks](https://img.shields.io/github/forks/EmptyWork/emptywork.github.io?style=for-the-badge)]() [![License](https://img.shields.io/github/license/EmptyWork/emptywork.github.io?style=for-the-badge)](https://github.com/EmptyWork/emptywork.github.io/blob/master/LICENSE) [![Netlify Status](https://img.shields.io/netlify/08d2d578-7470-4e65-8067-93ab5e09f671?style=for-the-badge)](https://app.netlify.com/sites/emptywork/deploys) +![Playwright Test](https://img.shields.io/github/actions/workflow/status/emptywork/emptywork.github.io/playwright.yml?style=for-the-badge&label=Playwright%20Test) + +--- + +![Highlightjs-lines Test](https://img.shields.io/github/actions/workflow/status/emptywork/emptywork.github.io/highlightjs-lines.yml?style=for-the-badge&label=Highlightjs-lines%20Test) [**⚠Laporkan Bug**](https://github.com/EmptyWork/emptywork.github.io/issues/new) — [**📧Minta Fitur**](https://github.com/EmptyWork/emptywork.github.io/issues/new) diff --git a/README.id-ID.md b/README.id-ID.md index 1e883d3..6f67035 100644 --- a/README.id-ID.md +++ b/README.id-ID.md @@ -10,6 +10,11 @@ en: This documents is not updated to the latest version. please read the english [![Repository Forks](https://img.shields.io/github/forks/EmptyWork/emptywork.github.io?style=for-the-badge)]() [![License](https://img.shields.io/github/license/EmptyWork/emptywork.github.io?style=for-the-badge)](https://github.com/EmptyWork/emptywork.github.io/blob/master/LICENSE) [![Netlify Status](https://img.shields.io/netlify/08d2d578-7470-4e65-8067-93ab5e09f671?style=for-the-badge)](https://app.netlify.com/sites/emptywork/deploys) +![Playwright Test](https://img.shields.io/github/actions/workflow/status/emptywork/emptywork.github.io/playwright.yml?style=for-the-badge&label=Playwright%20Test) + +--- + +![Highlightjs-lines Test](https://img.shields.io/github/actions/workflow/status/emptywork/emptywork.github.io/highlightjs-lines.yml?style=for-the-badge&label=Highlightjs-lines%20Test) [**⚠Laporkan Bug**](https://github.com/EmptyWork/emptywork.github.io/issues/new) — [**📧Minta Fitur**](https://github.com/EmptyWork/emptywork.github.io/issues/new) diff --git a/README.md b/README.md index 7c7e07a..936ee69 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ [![Repository Forks](https://img.shields.io/github/forks/EmptyWork/emptywork.github.io?style=for-the-badge)]() [![License](https://img.shields.io/github/license/EmptyWork/emptywork.github.io?style=for-the-badge)](https://github.com/EmptyWork/emptywork.github.io/blob/master/LICENSE) [![Netlify Status](https://img.shields.io/netlify/08d2d578-7470-4e65-8067-93ab5e09f671?style=for-the-badge)](https://app.netlify.com/sites/emptywork/deploys) +![Playwright Test](https://img.shields.io/github/actions/workflow/status/emptywork/emptywork.github.io/playwright.yml?style=for-the-badge&label=Playwright%20Test) + +--- + +![Highlightjs-lines Test](https://img.shields.io/github/actions/workflow/status/emptywork/emptywork.github.io/highlightjs-lines.yml?style=for-the-badge&label=Highlightjs-lines%20Test) [**⚠ Report Bug**](https://github.com/EmptyWork/emptywork.github.io/issues/new) — [**📧 Request Feature**](https://github.com/EmptyWork/emptywork.github.io/issues/new) diff --git a/archives/error.scss b/archives/error.scss deleted file mode 100644 index 9780e4e..0000000 --- a/archives/error.scss +++ /dev/null @@ -1,14 +0,0 @@ -@import './modules/vars'; -@import './modules/mixin'; -@import './modules/global'; - -@import './modules/component/preload'; - -@import './modules/header'; -@import './modules/component/nav'; -@import './modules/component/title'; -@import './modules/main'; -@import './modules/footer'; - -@import './modules/component/contact'; -@import './modules/component/error'; diff --git a/archives/modules/_footer.scss b/archives/modules/_footer.scss deleted file mode 100644 index 0c2cf20..0000000 --- a/archives/modules/_footer.scss +++ /dev/null @@ -1,70 +0,0 @@ -.footer { - background-color: var(--clr-background); - color: var(--clr-foreground); - padding: 0.8rem; - - .brand { - display: flex; - gap: 0.5rem; - width: max-content; - - &-title { - font-size: 1.3rem; - font-weight: 700; - } - - &-logo { - display: flex; - align-items: center; - } - } - - &-top { - padding: 0.8rem; - padding-top: 3rem; - font-size: 2rem; - max-width: 35ch; - line-height: 1.2; - position: relative; - isolation: isolate; - - &::before { - content: '"'; - position: absolute; - font-size: 4em; - left: 0; - top: 0; - color: var(--clr-background-800); - font-weight: 600; - user-select: none; - z-index: -1; - } - } - - &-bottom { - padding: 0.8rem; - text-align: left; - display: flex; - flex-direction: column; - gap: 0.3em; - } - - &-container { - max-width: 64rem; - margin: 0 auto; - } - - .copyright { - font-size: 0.9rem; - margin: 1rem auto; - text-align: left; - border: 0.0625rem solid var(--clr-foreground); - width: 100%; - max-width: max-content; - padding: 0.2rem 2rem; - - @include breakpoint-up('medium') { - flex-direction: row; - } - } -} diff --git a/archives/modules/_global.scss b/archives/modules/_global.scss deleted file mode 100644 index 93455cf..0000000 --- a/archives/modules/_global.scss +++ /dev/null @@ -1,163 +0,0 @@ -html { - scroll-behavior: smooth; - scroll-padding-top: 10rem; - scroll-padding-bottom: 10rem; -} - -html:root { - .themes { - background-color: #ffc107; - color: #7e620e; - } - - [data-moon] { - display: none; - } - - [data-sun] { - display: block; - } - - &[data-theme="dark"] { - .themes { - background-color: var(--clr-accent); - color: var(--clr-accent-dark); - } - - [data-sun] { - display: none; - } - - [data-moon] { - display: block; - } - } -} - -*, -*:is(::after, ::before) { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -body { - display: flex; - flex-direction: column; - min-height: 100vh; - background-color: var(--clr-background-900); - font-family: 'Montserrat', sans-serif; - color: var(--clr-foreground); - line-height: 1.6; - overflow-x: hidden; -} - -a, -a:is(:active, :hover, :focus-visible) { - color: inherit; - text-decoration: none; -} - -a:is(:hover, :focus-visible) { - color: var(--clr-accent); -} - -a[href] { - position: relative; - - .new-tab-warning { - position: absolute; - width: max-content; - background-color: hsl(0, 0%, 13%); - font-weight: normal; - letter-spacing: normal; - color: var(--clr-white) !important; - padding: 0.3rem; - font-size: 0.7rem; - border-radius: 0.3rem; - margin-top: 0.5rem; - top: 100%; - left: 0; - visibility: hidden; - pointer-events: none; - text-transform: capitalize; - transition: all 300ms ease-out; - translate: 0 -0.3rem; - z-index: 90; - user-select: none; - } - - &:focus-visible { - .new-tab-warning { - visibility: visible; - translate: 0 0; - } - } -} - -::selection { - background-color: var(--clr-accent); - color: var(--clr-white); -} - -.t-all { - transition: all 300ms ease-in; -} - -.no-scroll { - @include breakpoint-down('small') { - overflow: hidden !important; - - :is(header, main, footer) { - visibility: hidden; - user-select: none; - } - } -} - -.not-loaded { - overflow: hidden; - - :is(header, main, footer, nav) { - visibility: hidden; - user-select: none; - } -} - -:is(ul, li) { - margin: 0; - padding: 0; -} - -li { - list-style: none; -} - -*:focus-visible { - outline: 0.125rem solid var(--clr-accent-dark); -} - -.highlight { - background-color: var(--clr-black); - padding: 0.2rem 0.6rem; - color: var(--clr-white); - - &:is(:hover, :focus) { - color: var(--clr-white); - } -} - -.header-link__access { - text-decoration: none !important; -} - -.sr-only { - border: 0; - clip: rect(0 0 0 0); - height: 0.0625rem; - margin: -0.0625rem; - overflow: hidden; - padding: 0; - position: absolute; - width: 0.0625rem; -} \ No newline at end of file diff --git a/archives/modules/_header.scss b/archives/modules/_header.scss deleted file mode 100644 index d86013c..0000000 --- a/archives/modules/_header.scss +++ /dev/null @@ -1,21 +0,0 @@ -.header { - &_article { - all: unset; - position: relative; - width: 100vw; - aspect-ratio: 16/9; - height: fit-content; - max-height: 60vh; - overflow: hidden; - margin: 0 auto; - - img { - top: 50%; - translate: 0 -50%; - position: absolute; - aspect-ratio: 16 / 9; - width: 100%; - height: auto; - } - } -} diff --git a/archives/modules/_main.scss b/archives/modules/_main.scss deleted file mode 100644 index 4ed5feb..0000000 --- a/archives/modules/_main.scss +++ /dev/null @@ -1,174 +0,0 @@ -.main { - background-color: var(--clr-background-900); - flex: 1 0 auto; - display: flex; - flex-direction: column; - gap: 6rem; - overflow: hidden; - - &-container { - max-width: 64rem; - margin: 0 auto; - padding: 3rem 2rem; - width: 100%; - } - - .section { - &-more { - display: block; - width: 100%; - padding: 1em; - border: 0.0625em solid var(--clr-foreground); - margin-top: 1em; - border-radius: 0.3em; - - &:is(:hover, :focus) { - background-color: var(--clr-accent); - border-color: var(--clr-accent-light); - color: var(--clr-white); - } - } - - &-header { - display: flex; - flex-direction: row; - align-items: center; - z-index: 3; - gap: 2em; - font-size: 1.2rem; - text-transform: uppercase; - letter-spacing: 0.1em; - - .header-title { - display: flex; - line-height: 1; - gap: 0.6em; - - a { - height: max-content; - } - } - - span { - text-transform: none; - letter-spacing: 0; - } - - @include breakpoint-down('large') { - flex-direction: column; - align-items: flex-start; - gap: 0; - - span { - &::before { - display: none; - } - } - } - - .header-desc { - display: block; - position: relative; - font-size: 0.9rem; - color: var(--clr-foreground-700); - - &::before { - content: ''; - position: absolute; - left: 0; - border: 0.1875em solid var(--clr-foreground); - top: 50%; - translate: 0 -50%; - margin-left: -1em; - border-radius: 50%; - } - } - } - } -} - -.blog { - .main-container { - padding-inline: 0; - - >*:not(ul) { - width: min(min(100vw - 4rem, 60rem), 58rem); - margin-inline: auto; - } - } -} - -.work { - position: relative; - isolation: isolate; - padding-top: 4rem; - - &-svg { - &_bottom { - position: absolute; - bottom: calc(-0.0625em - 6em); - z-index: -1; - color: var(--clr-background); - } - } - - .section-header { - width: min(min(100vw - 4rem, 60rem), 58rem); - margin: auto; - } - - .section-more { - margin: auto; - margin-top: -2em; - display: block; - width: min(min(calc(100vw - 4rem), 60rem), 58rem); - padding: 1em; - background-color: var(--clr-background); - border: 0.0625em solid var(--clr-background-900); - border-radius: 0.3em; - - &:is(:hover, :focus) { - background-color: var(--clr-accent); - border-color: var(--clr-accent-light); - color: var(--clr-white); - } - } - - @include breakpoint-down('medium') { - .main-container { - padding-inline: 0; - } - } -} - -.contact { - .form-input { - display: flex; - flex-direction: column; - - &__captcha { - margin-top: 1rem; - margin-bottom: 1rem; - } - - label { - margin: 2em 0 0.5em 0; - } - - input, - textarea { - width: 100%; - padding: 1rem; - border: none; - background-color: var(--clr-background); - color: var(--clr-foreground-900); - resize: none; - } - - input[type='submit'] { - border: none; - color: var(--clr-white); - background-color: var(--clr-accent); - } - } -} \ No newline at end of file diff --git a/archives/modules/_mixin.scss b/archives/modules/_mixin.scss deleted file mode 100644 index c081666..0000000 --- a/archives/modules/_mixin.scss +++ /dev/null @@ -1,23 +0,0 @@ -$breakpoints-up: ( - 'medium': '40em', - 'large': '64em', - 'xlarge': '87.5em', -); - -$breakpoints-down: ( - 'small': '39.9375em', - 'medium': '63.9375em', - 'large': '87.4375em', -); - -@mixin breakpoint-up($size) { - @media screen and (min-width: map-get($breakpoints-up, $size)) { - @content; - } -} - -@mixin breakpoint-down($size) { - @media screen and (max-width: map-get($breakpoints-down, $size)) { - @content; - } -} diff --git a/archives/modules/_vars.scss b/archives/modules/_vars.scss deleted file mode 100644 index d5efc55..0000000 --- a/archives/modules/_vars.scss +++ /dev/null @@ -1,72 +0,0 @@ -html:root { - --_h: 270; - --_hs: var(--_h) 20%; - --clr-accent: hsl(var(--_h), 50%, 40%); - --clr-accent-light: hsl(var(--_h), 84%, 72%); - --clr-accent-dark: hsl(var(--_h), 71%, 22%); - --clr-complementary: hsl(90, 50%, 40%); - - --clr-href: hsl(var(--_h), 80%, 50%); - --clr-href-visited: hsl(var(--_h), 70%, 22%); - - --clr-background: hsl(var(--_hs) 95%); - --clr-background-900: hsl(var(--_hs) 88%); - --clr-background-800: hsl(var(--_hs) 75%); - --clr-background-700: hsl(var(--_hs) 63%); - --clr-foreground: hsl(var(--_hs) 10%); - --clr-foreground-900: hsl(var(--_hs) 13%); - --clr-foreground-800: hsl(var(--_hs) 25%); - --clr-foreground-700: hsl(var(--_hs) 38%); - - --clr-disabled-text: hsl(var(--_hs) 50%); - --clr-white: hsl(var(--_hs) 100%); - --clr-black: hsl(var(--_hs) 0%); - --clr-black-light: hsl(var(--_hs) 13%); - - --clr-red: hsl(357, 70%, 41%); - --clr-red-dark: hsl(357, 70%, 30%); - - --bx-shadow-two-side: 0 0.3125rem 0.625rem hsla(var(--_hs) 0%, 0.2), - -0.3125rem -0.3125rem 0.625rem hsla(var(--_hs) 100%, 0.8); - --table-header: hsl(270, 80%, 30%); - - &[data-theme=sunglow] { - --clr-accent: hsl(40, 56%, 40%); - --clr-accent-light: hsl(40, 56%, 72%); - --clr-accent-dark: hsl(40, 56%, 22%); - - --clr-href: hsl(40, 56%, 72%); - --clr-href-visited: hsl(40, 56%, 22%); - - --clr-background: hsl(40, 56%, 95%); - --clr-background-900: hsl(40, 56%, 88%); - --clr-background-800: hsl(40, 56%, 75%); - --clr-background-700: hsl(40, 56%, 63%); - --clr-foreground: hsl(40, 56%, 10%); - --clr-foreground-900: hsl(40, 56%, 13%); - --clr-foreground-800: hsl(40, 56%, 25%); - --clr-foreground-700: hsl(40, 56%, 38%); - - &[data-theme=dark] { - --clr-href: hsl(40, 56%, 72%); - --clr-href-visited: hsl(40, 56%, 40%); - } - } - - &[data-theme=dark] { - --clr-href: hsl(var(--_h), 84%, 72%); - --clr-href-visited: hsl(var(--_h), 50%, 40%); - --clr-background: hsl(var(--_hs) 10%); - --clr-background-900: hsl(var(--_hs) 13%); - --clr-background-800: hsl(var(--_hs) 25%); - --clr-background-700: hsl(var(--_hs) 38%); - --clr-foreground: hsl(var(--_hs) 95%); - --clr-foreground-900: hsl(var(--_hs) 88%); - --clr-foreground-800: hsl(var(--_hs) 75%); - --clr-foreground-700: hsl(var(--_hs) 63%); - - --bx-shadow-two-side: 0 0.3125rem 0.625rem rgb(0 0 0 / 20%), - -0.3125rem -0.3125rem 0.625rem rgb(58 58 58 / 63%); - --table-header: var(--clr-accent-light); - } -} \ No newline at end of file diff --git a/archives/modules/component/_aboutme.scss b/archives/modules/component/_aboutme.scss deleted file mode 100644 index 169e393..0000000 --- a/archives/modules/component/_aboutme.scss +++ /dev/null @@ -1,305 +0,0 @@ -.aboutme { - background-color: var(--clr-accent); - color: var(--clr-white); - - .adds { - font-size: 0.9rem; - padding: 1rem; - - .new-line { - margin-top: 0.5em; - } - } - - a { - text-decoration: underline; - text-underline-offset: 0.4rem; - } - - a:hover, - a:focus-visible { - color: #d0d0d0; - } - - ::selection { - background-color: #333333; - } - - .extra { - text-transform: uppercase; - letter-spacing: 0.1rem; - } - - .main-container { - display: flex; - flex-direction: column; - } - - .flex { - display: flex; - flex-direction: row; - border-radius: 0.3rem; - margin-top: 1rem; - - &-header { - display: flex; - padding: 0.5rem 1rem; - width: 100%; - justify-content: space-between; - box-shadow: 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - flex: 0 0 auto; - - span[aria-hidden=true] { - display: block; - margin-top: 0.3rem; - } - - div { - display: flex; - align-items: center; - font-weight: 600; - gap: 0.2rem; - - .dot { - height: 0.9375rem; - width: 0.9375rem; - background-color: hsl(0, 100%, 43%); - border-radius: 50%; - - &:first-of-type { - background-color: green; - } - - &:nth-of-type(2) { - background-color: yellow; - } - } - } - } - - .lists { - display: flex; - justify-content: space-around; - width: 100%; - } - - &.tools { - background-color: hsl(0, 0%, 13%); - box-shadow: 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - margin-bottom: -9rem; - z-index: 2; - - h2 { - padding: 1rem; - } - - .lists { - background-color: hsl(0, 0%, 11%); - border-radius: 0.3rem; - padding: 2rem; - } - } - - &.languages { - filter: brightness(110%); - box-shadow: inset 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - background-color: var(--clr-accent-dark); - padding: 2rem; - - .header-title { - gap: 1em; - display: flex; - } - - h2 { - padding: 1rem 0; - } - - ul { - margin-top: 0.2rem; - } - - .lists { - overflow: unset; - height: fit-content; - } - } - - &:not(:first-of-type) { - margin-top: 5rem; - justify-content: space-around; - flex-direction: column; - - ul { - font-size: 1.1rem; - font-weight: 600; - - li { - font-weight: 500; - font-size: 1rem; - margin-top: 0.5rem; - - &:first-of-type { - font-weight: 600; - } - - &:nth-child(2) { - margin-top: 0.5rem; - } - } - } - - @include breakpoint-down('medium') { - align-items: flex-start; - - .lists { - flex-direction: column; - } - - ul>li { - display: inline-block; - margin-top: 0; - } - } - } - - @include breakpoint-down('medium') { - flex-direction: column; - align-items: center; - } - } - - .data { - position: relative; - min-width: 25rem; - height: 20rem; - - @include breakpoint-down('medium') { - width: 100%; - max-width: 25rem; - min-width: unset; - } - - &:hover { - .note { - scale: 1.1; - } - } - - &::before { - content: ''; - position: absolute; - width: 100%; - height: 100%; - background-color: hsl(0, 100%, 43%); - left: -0.7rem; - border-radius: 40% 20% / 10%; - } - } - - .explain { - display: flex; - align-items: center; - margin-top: 1rem; - padding: 1rem; - font-size: 1.2rem; - - @include breakpoint-up('large') { - padding-left: 3rem; - } - } - - .note { - rotate: -3deg; - } -} - -.note { - color: var(--clr-white); - display: flex; - flex: 0 0 auto; - position: relative; - width: min(25rem, 100%); - flex-direction: column; - background-color: hsl(0, 0%, 13%); - border-radius: 0.6rem; - box-shadow: 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - height: max(20rem, 100%); - transition: all 300ms ease-in-out; - - article { - padding: 1rem; - z-index: 80; - } - - &>svg { - position: absolute; - right: 0.1rem; - bottom: 0.1rem; - color: hsl(0, 0%, 11%); - } - - article { - padding: 1rem; - height: 100%; - overflow-y: auto; - - scrollbar-width: none; - - &::-webkit-scrollbar { - width: 0; - } - - li { - padding-left: 0.6rem; - } - } - - &-heading { - font-weight: 600; - display: block; - text-transform: uppercase; - letter-spacing: 0.1em; - line-height: 1.2; - } - - &-subheading { - font-style: italic; - } - - li { - font-size: 0.85em; - } - - &-header { - display: flex; - height: 2.5rem; - padding: 0 1rem; - width: 100%; - justify-content: space-between; - box-shadow: 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - flex: 0 0 auto; - - div { - display: flex; - align-items: center; - font-weight: 600; - gap: 0.2rem; - - .dot { - height: 0.9375rem; - width: 0.9375rem; - background-color: hsl(0, 100%, 43%); - border-radius: 50%; - - &:first-of-type { - background-color: green; - } - - &:nth-of-type(2) { - background-color: yellow; - } - } - } - } -} \ No newline at end of file diff --git a/archives/modules/component/_animation.scss b/archives/modules/component/_animation.scss deleted file mode 100644 index 5b19ab2..0000000 --- a/archives/modules/component/_animation.scss +++ /dev/null @@ -1,59 +0,0 @@ -[data-animation] { - opacity: 0; - transition: translate var(--_translate, 1s) ease-in-out, scale var(--_scale, 1s) ease-in-out, rotate var(--_rotate, 1s) ease-in-out, opacity var(--_opacity, 1s) ease-in-out; -} - -[data-animation='fade-in'] { - translate: 0 4em; -} - -[data-animation='right-show'] { - translate: -5em 0; -} - -[data-animation='top-show'] { - translate: 0 -5em; -} - -[data-animation='bottom-show'] { - translate: 0 5em; -} - -[data-animation='left-show'] { - translate: 5em 0; -} - -[data-animation='prototype-card'] { - translate: -5em 0; - - &:nth-of-type(odd) { - translate: 5em 0; - } -} - -.has-been-animated { - opacity: unset; - scale: unset; - rotate: unset; - translate: unset !important; -} - -@media (prefers-reduced-motion) { - .prototype-reader, - .data { - transform: unset !important; - } - - *, - *::after, - *::before { - animation: unset !important; - rotate: 0deg !important; - translate: 0em 0em !important; - } - - [data-animation] { - translate: unset !important; - opacity: 1 !important; - } -} \ No newline at end of file diff --git a/archives/modules/component/_article.scss b/archives/modules/component/_article.scss deleted file mode 100644 index 6db37d7..0000000 --- a/archives/modules/component/_article.scss +++ /dev/null @@ -1,432 +0,0 @@ -.article { - --_article-color: hsla(270, 50%, 40%, 0.5); - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; - color: var(--clr-foreground-900); - background-color: var(--clr-background-900); - line-height: 2; - margin: auto; - - main { - position: relative; - - .table-of-contents { - position: absolute; - top: 0; - right: 0; - - @include breakpoint-down('medium') { - position: unset; - } - } - } - - - a { - font-weight: 600; - color: var(--clr-href); - - &:not(.header-anchor, .breadcrumb) { - text-decoration: 0.0625em underline var(--clr-href); - text-underline-offset: 0.35em; - } - - &:hover { - color: var(--clr-href-visited); - } - - &:focus-visible { - outline: thin dotted; - } - - &:hover, - &:active { - outline: 0; - } - } - - span { - - a:hover, - a:visited, - a:active, - a:focus-visible { - color: var(--clr-href); - } - } - - ::selection { - background: var(--clr-accent-dark); - color: var(--clr-white); - } - - a::selection { - background: var(--clr-accent-dark); - color: var(--clr-white); - } - - :is(p:not(:first-of-type), table, blockquote) { - max-width: 60ch; - } - - p { - margin: 1rem 0; - } - - img { - max-width: 100%; - } - - h1, - h2, - h3, - h4, - h5, - h6 { - font-weight: 600; - color: var(--clr-foreground-900); - padding: 1rem 0; - line-height: 1.1em; - } - - h4, - h5, - h6 { - font-weight: bold; - } - - h1 { - font-size: 2.5rem; - } - - h2 { - font-size: 2rem; - } - - h3 { - font-size: 1.5rem; - } - - h4 { - font-size: 1.2rem; - } - - h5 { - font-size: 1rem; - } - - h6 { - font-size: 0.9rem; - } - - blockquote { - color: var(--clr-foreground-900); - margin: 0; - padding-left: 2em; - border-left: 0.5em var(--clr-accent) solid; - } - - hr { - display: block; - height: 0.15em; - border: 0; - border-top: 0.0625em solid var(--clr-accent); - border-bottom: 0.0625em solid var(--clr-accent-light); - margin: 1em 0; - padding: 0; - } - - pre, - code, - kbd, - samp { - padding: 0 0.3em; - font-family: monospace, "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", "Lucida Sans", Arial, sans-serif; - // font-size: 0.8rem; - // line-height: 1.4; - } - - pre { - border: 0.1rem solid var(--_article-color); - // background-color: var(--_article-color); - border-radius: 0.5rem; - padding: 1rem; - white-space: pre; - white-space: pre-wrap; - word-wrap: break-word; - - code { - box-shadow: none; - background-color: transparent; - } - } - - b, - strong { - font-weight: bold; - color: var(--clr-foreground-900); - } - - dfn { - font-style: italic; - } - - ins { - background: #ff9; - color: #000; - text-decoration: none; - } - - mark { - background-color: var(--clr-black); - color: var(--clr-white); - - &:is(:hover, :focus) { - color: var(--clr-white); - } - } - - sub, - sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; - - >a { - text-decoration: none !important; - } - } - - sup { - top: -0.5rem; - } - - sub { - bottom: -0.25rem; - } - - ul, - ol { - margin: 1em 0; - padding: 0 0 0 2rem; - } - - ul li { - list-style-type: disc; - } - - ol li { - list-style-type: decimal; - } - - li p:last-child { - margin: 0; - } - - dd { - margin: 0 0 0 2rem; - } - - img { - border: 0; - vertical-align: middle; - } - - table { - border-collapse: collapse; - overflow-x: scroll; - border-spacing: 0; - width: 100%; - background-color: var(--clr-background); - border-radius: 0.5rem; - overflow: hidden; - - td, - th { - padding: 0.4rem 0.8rem; - } - - tr { - :is(td, th):first-child { - text-align: center; - } - } - - th { - background-color: var(--_article-color); - color: var(--table-header); - // border-bottom: 0.25rem solid var(--clr-accent); - } - - td { - vertical-align: top; - } - - &.properties { - th { - border-bottom: 0.0625rem solid var(--clr-background-800); - border-left: 0.25rem solid var(--clr-accent); - } - } - } - - .table-of-contents { - .toc-container-header { - font-weight: 700; - } - - ul { - margin: 0; - - li { - margin-top: 0.15em; - list-style: circle; - } - } - } - - @media print { - * { - background: transparent !important; - color: var(--clr-accent-dark) !important; - filter: none !important; - -ms-filter: none !important; - } - - body { - font-size: 12pt; - max-width: 100%; - } - - a, - a:visited { - text-decoration: underline; - } - - hr { - height: 0.0625em; - border: 0; - border-bottom: 0.0625em solid var(--clr-accent-dark); - } - - a[href]:after { - content: ' (' attr(href) ')'; - } - - abbr[title]:after { - content: ' (' attr(title) ')'; - } - - .ir a:after, - a[href^='javascript:']:after, - a[href^='#']:after { - content: ''; - } - - pre, - blockquote { - border: 0.0625em solid #999; - padding-right: 1em; - page-break-inside: avoid; - } - - tr, - img { - page-break-inside: avoid; - } - - img { - max-width: 100% !important; - } - - @page :left { - margin: 15mm 20mm 15mm 10mm; - } - - @page :right { - margin: 15mm 10mm 15mm 20mm; - } - - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - - h2, - h3 { - page-break-after: avoid; - } - } - - &-flex_list { - flex-wrap: wrap; - gap: 0.5em; - display: flex; - margin: unset !important; - padding: unset !important; - } - - &-detail { - &_list { - flex-wrap: wrap; - gap: 0.5em; - display: flex; - margin: unset !important; - padding: unset !important; - } - - &_item { - font-size: 0.9rem; - gap: 0.5em; - border-radius: 0.3em; - padding: 0.4em; - display: flex; - color: var(--clr-foreground); - border: 0.0625em solid var(--clr-foreground); - align-items: center; - - &:first-of-type { - border: none; - color: var(--clr-white); - box-shadow: 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - background-color: var(--clr-accent-light); - background-image: linear-gradient(45deg, - var(--clr-accent-dark), - var(--clr-accent)); - } - - &_svg { - width: 1.2rem; - } - } - } - - &-summary { - font-style: italic; - font-size: 1.2rem; - color: var(--clr-foreground-700); - } - - .more { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(min(20em, 60ch), 29.5rem)); - margin-top: 1em; - gap: 1em; - - div { - padding: 1rem; - background: var(--clr-background); - - a { - display: block; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - } - - } -} \ No newline at end of file diff --git a/archives/modules/component/_card.scss b/archives/modules/component/_card.scss deleted file mode 100644 index dde106a..0000000 --- a/archives/modules/component/_card.scss +++ /dev/null @@ -1,226 +0,0 @@ -.card { - background-color: #333333; - background-image: linear-gradient(transparent, var(--clr-accent-dark)); - background-position: center; - background-size: cover; - background-clip: border-box; - box-shadow: 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - border-radius: 0.3rem; - padding: 10rem 0 0; - width: 28ch; - position: relative; - isolation: isolate; - flex: 0 0 auto; - scroll-snap-align: center; - color: var(--clr-white); - - &:hover, - &:has(:focus) { - scale: 1.1; - z-index: 10; - } - - &-image { - position: absolute; - display: flex; - align-items: center; - justify-content: center; - z-index: -1; - left: 0; - right: 0; - top: 0; - bottom: 0; - height: 100%; - width: 100%; - border-radius: 0.3rem; - overflow: hidden; - - &::after { - content: ''; - position: absolute; - left: 0; - right: 0; - bottom: 0; - top: 0; - background-image: linear-gradient(transparent, var(--clr-accent-dark)); - } - } - - &-background { - height: inherit; - width: fit-content; - } - - &-empty { - width: calc(100% - 4rem); - padding: 2rem; - box-shadow: var(--bx-shadow-two-side); - background: var(--clr-background-900); - color: var(--clr-foreground-700); - - &:hover, - &:focus-visible { - scale: 1; - } - } - - &s { - display: flex; - flex-wrap: wrap; - gap: 1rem; - margin-top: 2rem; - scrollbar-width: none; - - &.nocentered { - justify-content: flex-start; - } - - &-more { - width: 100%; - } - - &.cards-horizontal { - padding: 2rem 2rem 4.5rem 2rem; - scroll-snap-type: x proximity; - overflow-y: hidden; - - - &::-webkit-scrollbar { - width: 0; - height: 0; - } - } - - @include breakpoint-down('medium') { - &.cards-horizontal { - flex-wrap: nowrap; - overflow-x: auto; - } - } - - @include breakpoint-up('medium') { - &.cards-horizontal { - flex-wrap: nowrap; - overflow-x: auto; - - } - - &-more { - justify-content: space-evenly; - } - } - } - - &-body { - background-image: linear-gradient(hsla(0, 0%, 0%, 0), - hsla(0, 0%, 0%, 0.4) 10%, - hsla(0, 0%, 0%, 1)); - height: 100%; - display: flex; - flex-direction: column; - border-radius: 0.3rem; - - header, - .description-area { - flex: 0 0 auto; - padding: 0 1.5rem; - } - - header { - h3 { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - } - - .description-area { - height: 3.2rem; - overflow: hidden; - } - - h2 { - font-size: 1.2rem; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - - .card-time { - display: block; - color: var(--clr-accent-light); - font-weight: 600; - margin-bottom: 1rem; - margin-top: 0.3rem; - font-size: 0.8rem; - padding: 0.2rem 1rem 0.2rem 0; - width: fit-content; - border-radius: 0.3em; - background-color: transparent; - background-image: linear-gradient(90deg, - hsl(270deg 71% 22% / 0%), - var(--clr-accent)); - } - - .more-links { - display: flex; - flex: 1 0 auto; - align-items: flex-end; - width: 100%; - justify-content: space-between; - - .disabled { - display: block; - margin-top: 1rem; - padding: 1rem 1.25rem; - color: var(--clr-disabled-text); - cursor: not-allowed; - user-select: none; - } - - a { - display: flex; - margin-top: 1rem; - padding: 1rem 1.5rem; - user-select: none; - position: relative; - z-index: 10; - gap: 0.3rem; - - svg { - flex: 0 0 auto; - height: 1.5rem; - } - - &:hover, - &:focus-visible { - color: var(--clr-white); - - &::before { - width: 100%; - } - } - - &:focus { - .new-tab-warning { - box-shadow: 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - visibility: visible; - } - } - - &::before { - content: ''; - position: absolute; - width: 0%; - left: 0; - margin-top: -1rem; - background-color: var(--clr-accent); - height: 100%; - transition: all 150ms ease-in; - border-radius: 0.3rem; - z-index: -1; - } - } - } - } -} \ No newline at end of file diff --git a/archives/modules/component/_clients.scss b/archives/modules/component/_clients.scss deleted file mode 100644 index ca00cd8..0000000 --- a/archives/modules/component/_clients.scss +++ /dev/null @@ -1,17 +0,0 @@ -.clients { - background-color: var(--clr-background); - margin-bottom: -6em; - - &-list { - padding: 2em; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: center; - gap: 2em; - - svg { - max-height: 5em; - } - } -} diff --git a/archives/modules/component/_contact.scss b/archives/modules/component/_contact.scss deleted file mode 100644 index 17389b8..0000000 --- a/archives/modules/component/_contact.scss +++ /dev/null @@ -1,182 +0,0 @@ -.contact { - .form-input { - display: flex; - flex-direction: column; - - &__captcha { - margin-top: 1rem; - margin-bottom: 1rem; - } - - label { - margin: 2em 0 0.5em 0; - } - - input, - textarea { - width: 100%; - padding: 1rem; - border: none; - background-color: var(--clr-background); - color: var(--clr-foreground-900); - resize: none; - } - - input[type='submit'] { - border: none; - color: var(--clr-white); - background-color: var(--clr-accent); - } - } - - &-container { - --_main-color: var(--clr-background); - --_transparent-limit: 30%; - - display: grid; - gap: 0.5rem; - - .contact-summary { - background-color: var(--_main-color); - padding: 2rem; - // border: 0.0625rem solid var(--clr-background-800); - border-radius: 0.5em; - margin-top: 2em; - - sup { - font-size: 0.6em; - } - - a { - color: var(--clr-accent-light); - text-decoration: underline; - text-underline-offset: 0.2em; - font-weight: 500; - transition: text-underline-offset 100ms ease-in-out; - - &:is(:hover, :focus) { - text-underline-offset: 0.3em; - } - } - } - - .social { - &-container { - --_size: 20em; - --_minGridSize: min(var(--_size), calc(100% - 4rem + 0.5rem)); - --_minMax: minmax(var(--_minGridSize), 1fr); - --_gridSize: repeat(auto-fit, var(--_minMax)); - display: grid; - grid-template-columns: var(--_gridSize); - gap: 0.5rem; - - &__small { - --_size: 18em; - - .social-item { - aspect-ratio: unset; - padding: 1em; - - &-text { - - h2 { - font-size: 0.9em; - } - } - } - } - - li:nth-of-type(odd) { - .social-item { - grid-template-areas: "text text"; - } - } - - } - - &-item { - --_disabledColor: var(--clr-background-800); - aspect-ratio: 3/1; - border: 0.0625rem solid var(--_disabledColor); - border-radius: 0.5em; - overflow: hidden; - color: var(--_disabledColor); - cursor: not-allowed; - - &[href] { - border: 0.0625rem solid var(--_main-color); - cursor: pointer; - color: var(--clr-foreground); - } - - user-select: none; - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-template-areas: "text text"; - align-items: center; - justify-content: space-around; - position: relative; - padding: 2em; - - &-text { - font-variant: all-small-caps; - font-weight: 700; - font-size: 1.2rem; - grid-area: text; - text-align: center; - z-index: 99; - - h2 { - font-size: inherit; - line-height: 1; - } - - span { - font-size: 0.8rem; - font-style: italic; - color: var(--clr-foreground-700); - font-weight: 500; - font-variant: normal; - text-transform: lowercase; - } - } - - svg { - position: absolute; - color: var(--clr-background-800); - z-index: 0; - bottom: -80%; - left: -20%; - height: 160%; - } - - * { - z-index: 4; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - - &[href]::after { - content: ""; - position: absolute; - background-image: linear-gradient(-45deg, var(--clr-accent) 2%, transparent var(--_transparent-limit), var(--_main-color)); - width: 300%; - aspect-ratio: 1/1; - z-index: 1; - transition: width 250ms ease-in-out; - } - - &[href]:is(:hover, :focus) { - color: inherit; - - outline-offset: 0.1em; - - &::after { - width: 100%; - } - } - } - } - } -} \ No newline at end of file diff --git a/archives/modules/component/_error.scss b/archives/modules/component/_error.scss deleted file mode 100644 index fd23b3d..0000000 --- a/archives/modules/component/_error.scss +++ /dev/null @@ -1,120 +0,0 @@ -.error { - &-container { - @extend .contact-container; - } - - &-summary { - @extend .contact-summary; - - h2 { - line-height: 1; - font-size: 1.8rem; - font-variant: all-small-caps; - margin-bottom: 1rem; - } - } - - &-decorative { - aspect-ratio: 10/1; - width: 100%; - margin-bottom: 1em; - border-radius: 0.7em; - background-image: linear-gradient(-45deg, var(--clr-accent) 2%, transparent var(--_transparent-limit), var(--clr-accent)); - position: relative; - box-shadow: inset 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.5); - - .decorative { - user-select: none; - position: absolute; - filter: drop-shadow(0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.5)); - - &-left { - position: absolute; - font-size: 9rem; - font-variant: all-small-caps; - font-weight: 900; - height: 100%; - - animation: bounce-up-down 1.5s infinite forwards; - - @include breakpoint-down('small') { - font-size: 7rem; - } - } - - &-exclamation { - top: 50%; - translate: 0% -50%; - rotate: -20deg; - } - - &-question { - top: 0%; - left: 0.2em; - translate: 0% -50%; - rotate: 15deg; - } - } - - svg { - position: absolute; - aspect-ratio: 1/1; - height: 120%; - width: auto; - rotate: -20deg; - top: 50%; - right: 0; - translate: 0% -50%; - animation: svg-move 4s infinite forwards; - - filter: drop-shadow(0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.5)); - } - - @include breakpoint-down('small') { - aspect-ratio: 4/1; - } - - @keyframes svg-move { - 40% { - rotate: 30deg; - } - - 50% { - rotate: 20deg; - } - - 90% { - rotate: -30deg; - } - - 100% { - rotate: -20deg; - } - } - - @keyframes bounce-up-down { - 50% { - margin-top: -0.6rem; - rotate: 2deg; - } - - 100% { - margin-top: 0; - rotate: 0; - } - } - } - - &-button { - @extend .social-container; - @extend .social-container__small; - - li:first-of-type .error-link:first-of-type:after { - background-image: linear-gradient(-45deg, var(--clr-accent) 2%, transparent var(--_transparent-limit), var(--clr-accent)); - } - } - - &-link { - @extend .social-item; - } -} \ No newline at end of file diff --git a/archives/modules/component/_featured.scss b/archives/modules/component/_featured.scss deleted file mode 100644 index cde7a6a..0000000 --- a/archives/modules/component/_featured.scss +++ /dev/null @@ -1,204 +0,0 @@ -.featured { - --clr-variant: var(--clr-accent); - --clr-variant-dark: var(--clr-accent-dark); - - &.red { - --clr-variant: var(--clr-red); - --clr-variant-dark: var(--clr-red-dark); - } - - &-container { - display: flex; - flex-direction: column; - position: relative; - justify-content: space-between; - overflow: hidden; - margin: 2em auto; - margin-top: 5em; - border-radius: 0.3em; - padding: 0; - background-color: transparent; - min-height: 20rem; - margin-bottom: -5rem; - // border: 0.0625em solid var(--clr-background); - // box-shadow: 0 0.3125rem 0.625rem rgb(0 0 0 / 40%) - box-shadow: 0 0.625rem 1.25rem hsla(0, 0%, 0%, 0.4); - - @include breakpoint-down('medium') { - width: calc(100% - 2em); - } - } - - &-right { - display: flex; - padding: 2em; - background-color: var(--clr-variant); - flex: 1 auto; - position: relative; - justify-content: center; - align-items: center; - - @include breakpoint-down('small') { - min-height: 10em; - } - - img { - all: unset; - overflow: clip !important; - width: min(100%, 26.1875rem); - aspect-ratio: 1/1; - } - - #svg { - position: absolute; - bottom: 0; - left: 0; - } - } - - &-left { - color: var(--clr-white); - padding: 2em; - width: min(90ch, 100%); - background-color: var(--clr-variant-dark); - - >.featured-header { - display: flex; - flex-direction: column; - margin-bottom: 1em; - gap: 0.5em; - align-items: flex-start; - } - } - - &-header { - &_project-name { - border-radius: 0.3em; - background-color: var(--clr-variant); - background-image: linear-gradient(90deg, - var(--clr-variant-dark), - var(--clr-variant)); - padding: 1em; - width: 100%; - - p:last-of-type { - font-size: 0.8em; - } - } - } - - &-main { - &>* { - max-width: 45ch; - } - - &_tag { - border-radius: 0.3em; - padding: 0.5em 1.5em; - color: var(--clr-white); - border: 0.0625em solid var(--clr-white); - user-select: none; - pointer-events: none; - width: fit-content; - font-size: 0.7rem; - - &-area { - display: flex; - margin-top: 2em; - gap: 0.5em; - flex-wrap: wrap; - } - } - } - - &-body { - display: flex; - position: relative; - flex: 1 1 auto; - - @include breakpoint-down('small') { - flex-direction: column-reverse; - } - } - - &-footer { - padding: 1em 2em; - display: flex; - justify-content: flex-end; - background-color: var(--clr-black-light); - gap: 0.4rem; - flex-wrap: wrap; - - p { - border-radius: 0.3em; - padding: 0.5em 1.5em; - color: var(--clr-white); - border: 0.0625em solid var(--clr-white); - user-select: none; - pointer-events: none; - width: fit-content; - font-size: 0.9rem; - - &:first-of-type { - border: none; - color: var(--clr-white); - background: var(--clr-variant-dark); - background-image: linear-gradient(-90deg, - var(--clr-variant), - var(--clr-variant-dark)); - } - - @include breakpoint-down('small') { - width: 100%; - } - } - } - - &-background { - position: absolute; - left: 0; - bottom: 0; - right: 0; - width: 100%; - user-select: none; - pointer-events: none; - } - - &-external { - &_link { - display: flex; - align-items: center; - gap: 0.5em; - padding: 0.5em; - margin-top: 1em; - border-radius: 0.3em; - font-size: 1.2rem; - color: var(--clr-white); - border: none; - background: var(--clr-black-light); - - svg { - width: 1.2em; - height: 1.2em; - } - - &:is(:hover, :focus) { - color: var(--clr-white); - outline: 0.0625em solid var(--clr-background); - } - } - } - - &-tag { - border-radius: 0.3em; - position: absolute; - right: 1em; - top: 1em; - padding: 0.2em 0.5em; - z-index: 10; - font-weight: 500; - background-color: var(--clr-black-light); - color: var(--clr-white); - // box-shadow: 0 0.625rem 1.25rem hsla(270, 71%, 22%, 0.2); - } -} \ No newline at end of file diff --git a/archives/modules/component/_hero.scss b/archives/modules/component/_hero.scss deleted file mode 100644 index 21821fe..0000000 --- a/archives/modules/component/_hero.scss +++ /dev/null @@ -1,88 +0,0 @@ -.hero { - overflow: hidden; - display: flex; - isolation: isolate; - align-items: center; - justify-content: center; - flex-direction: column; - background-color: var(--clr-background); - height: 80dvh; - z-index: 3; - position: relative; - - @include breakpoint-down('medium') { - height: 100%; - } - - &-decoration { - --width: 95vmin; - --increase: 1.2; - --duration: 12s; - --delay: calc((var(--duration) * 0.1)); - position: absolute; - right: calc(var(--width) / -2); - bottom: calc(var(--width) / -2); - z-index: -1; - width: var(--width); - aspect-ratio: 1/1; - border-radius: 50%; - background-color: var(--clr-accent); - animation: twiching var(--duration) var(--delay) infinite; - } - - @keyframes twiching { - 50% { - width: calc(var(--width) * var(--increase)); - } - } - - &-bigger { - --width: 120vmin; - --increase: 2; - --duration: 10s; - --delay: calc((var(--duration) * 0.2)); - background-color: transparent; - border: max(0.5vmin, 0.125rem) solid var(--clr-accent); - } - - .decoration { - position: absolute; - width: 100%; - height: 100%; - user-select: none; - pointer-events: none; - - .relative { - position: relative; - } - } - - &-svg { - &_bottom { - z-index: -1; - position: absolute; - bottom: 0; - margin-bottom: -.625rem; - left: 0; - width: 100%; - overflow: hidden; - line-height: 0; - transform: rotate(180deg); - - svg { - position: relative; - display: block; - width: calc(100% + 0.08125rem); - height: 9.375rem; - - @include breakpoint-down('small') { - height: 3.125rem; - } - } - - .shape-fill { - fill: var(--clr-background-900); - } - } - } -} \ No newline at end of file diff --git a/archives/modules/component/_nav.scss b/archives/modules/component/_nav.scss deleted file mode 100644 index f8ff033..0000000 --- a/archives/modules/component/_nav.scss +++ /dev/null @@ -1,191 +0,0 @@ -.nav { - background-color: var(--clr-background); - flex-grow: 0; - flex-shrink: 0; - position: sticky; - top: 0; - padding: 0.8rem; - z-index: 20; - - &-container { - width: 100%; - max-width: 64rem; - margin: 0 auto; - gap: 1rem; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - - .menubar { - font-weight: 600; - flex: 1; - justify-content: flex-end; - } - } - - &-skipto { - background-color: var(--clr-accent); - color: var(--clr-white); - display: flex; - padding: 0 0.3rem; - align-self: center; - scale: 0 1; - order: 2; - transform-origin: left; - transition: scale 200ms ease; - - &:focus { - color: var(--clr-white); - scale: 1; - } - } - - .leftside { - display: flex; - gap: 1rem; - } - - .rightside { - display: flex; - gap: 0.2rem; - } - - .brand { - display: flex; - gap: 0.5rem; - flex: 1 0 auto; - order: 1; - - &-title { - font-size: 1.3rem; - font-weight: 700; - } - - &-logo { - display: flex; - align-items: center; - } - } - - .themes { - transition: background-color 200ms ease-in; - border-radius: 50%; - } - - .themes, - &-mobile { - cursor: pointer; - background: none; - border: none; - display: block; - height: 2rem; - width: 2rem; - padding: 0.4rem; - color: inherit; - - svg { - width: 100%; - height: 100%; - } - - &__section { - margin-top: 3.605625rem; - right: 0; - position: fixed; - top: 0; - left: 0; - height: calc(100% - 3.605625rem); - background-color: var(--clr-background); - color: var(--clr-foreground); - z-index: 20; - display: flex; - flex-direction: column; - align-items: center; - gap: 2rem; - justify-content: center; - - &.not-showing { - height: 0%; - overflow-y: hidden; - visibility: hidden; - } - - @include breakpoint-up('medium') { - height: 0%; - overflow-y: hidden; - visibility: hidden; - } - - li { - width: 100%; - list-style: none; - - a { - display: block; - text-transform: uppercase; - height: 5em; - - div { - position: absolute; - left: 50%; - top: 50%; - translate: -50% -50%; - } - - &:is(:hover, :focus) { - font-weight: 700; - } - } - } - } - } - - &-mobile { - @include breakpoint-up('medium') { - display: none; - } - } - - ul { - display: none; - gap: 1rem; - align-items: center; - - @include breakpoint-up('medium') { - display: flex; - } - - li { - list-style: none; - text-transform: uppercase; - letter-spacing: 0rem; - - a { - position: relative; - z-index: 1; - transition: color 300ms 150ms ease-in; - - &:hover, - &:focus-visible { - color: var(--clr-white); - z-index: 1; - - &::before { - width: 100%; - } - } - - &::before { - content: ''; - position: absolute; - width: 0%; - background-color: var(--clr-accent); - height: 110%; - transition: all 150ms ease-in; - z-index: -1; - } - } - } - } -} \ No newline at end of file diff --git a/archives/modules/component/_partof.scss b/archives/modules/component/_partof.scss deleted file mode 100644 index 6cc1e54..0000000 --- a/archives/modules/component/_partof.scss +++ /dev/null @@ -1,168 +0,0 @@ -.part-of { - background-color: var(--clr-background); - width: 100%; - margin-bottom: -6em; - padding-bottom: 4em; - position: relative; - isolation: isolate; - - .description { - font-size: 0.9rem; - } - - &-more { - display: block; - width: 100%; - padding: 1em; - background-color: var(--clr-background); - border: 0.0625em solid var(--clr-background-900); - margin-top: 1em; - border-radius: 0.3em; - - &:is(:hover, :focus) { - background-color: var(--clr-accent); - border-color: var(--clr-accent-light); - color: var(--clr-white); - } - } - - &-container { - max-width: 64rem; - margin: 0 auto; - padding: 3em 2em 2em 2em; - width: 100%; - - h2 { - text-transform: uppercase; - letter-spacing: 0.1em; - - @include breakpoint-down('medium') { - margin-bottom: 0.5em; - } - } - - .organization { - &-container { - margin-top: 1em; - display: grid; - gap: 1em; - grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr)); - - svg { - max-height: 8em; - - @include breakpoint-down('medium') { - max-width: 100%; - margin-top: 2em; - } - } - } - - &-item { - border: 0.0625em solid var(--clr-background-900); - border-radius: 0.3em; - padding: 1em; - position: relative; - background: var(--clr-background); - - &.full { - grid-column: 1 / -1; - display: flex; - align-items: center; - gap: 1em; - - svg { - flex: 0 0 auto; - } - - .organization-content { - flex: 1 auto; - margin-top: 2em; - } - - @include breakpoint-down('medium') { - flex-direction: column; - } - } - } - - &-desc { - margin-bottom: 1em; - } - - &-position { - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 0.5em; - - span { - border-radius: 0.3em; - padding: 0.5em 1.5em; - color: var(--clr-foreground); - border: 0.0625em solid var(--clr-foreground); - align-items: center; - gap: 0.4rem; - user-select: none; - pointer-events: none; - - &:first-of-type { - border: none; - color: var(--clr-white); - background: var(--clr-accent-dark); - background-image: linear-gradient( - -90deg, - var(--clr-accent), - var(--clr-accent-dark) - ); - background-size: 400%; - animation: positionanimate 1s infinite alternate; - } - } - } - - &-join { - display: flex; - flex-direction: row; - color: var(--clr-foreground); - border: 0.0625em solid var(--clr-foreground); - border-radius: 0.3em; - padding: 0.5em 1.5em; - align-items: center; - width: fit-content; - position: absolute; - right: 1em; - font-size: 0.8rem; - top: 1em; - user-select: none; - pointer-events: none; - } - } - } - - &-svg { - &_bottom { - position: absolute; - bottom: -0.0625em; - z-index: -1; - color: var(--clr-accent); - - @include breakpoint-up('medium') { - bottom: -4.0625em; - } - - @include breakpoint-up('large') { - bottom: -8.0625em; - } - } - } -} - -@keyframes positionanimate { - 0% { - background-position: left; - } - 100% { - background-position: right; - } -} diff --git a/archives/modules/component/_post.scss b/archives/modules/component/_post.scss deleted file mode 100644 index 5c8d496..0000000 --- a/archives/modules/component/_post.scss +++ /dev/null @@ -1,79 +0,0 @@ -.post { - &s { - margin-top: 2.5rem; - - &-yeargroup { - margin-bottom: 0.5em; - } - - &-detail { - margin-top: 1rem; - margin-left: 2rem; - background-color: var(--clr-background); - color: var(--clr-foreground-700); - border: 0.0625em solid var(--clr-background-900); - padding: 1rem; - border-radius: 0.3rem; - - @include breakpoint-down('small') { - margin-inline: 0; - } - - &_header { - font-size: 1.6rem; - line-height: 1.1; - margin-bottom: 0.5em; - display: block; - font-weight: 600; - color: var(--clr-foreground); - } - - &_accent { - color: var(--clr-white); - margin-bottom: 1em; - width: fit-content; - flex-wrap: wrap; - gap: 0.5em; - display: flex; - - &-item { - font-size: 0.9rem; - gap: 0.5em; - border-radius: 0.3em; - padding: 0.4em; - display: flex; - color: var(--clr-foreground); - border: 0.0625em solid var(--clr-foreground); - align-items: center; - - &_filled { - border: none; - color: var(--clr-white); - box-shadow: 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - background-color: var(--clr-accent-light); - background-image: linear-gradient(45deg, - var(--clr-accent-dark), - var(--clr-accent)); - } - } - } - - &_svg { - width: 1.5em; - height: fit-content; - } - } - - &-list { - margin: 0; - } - - &:not(:first-of-type) { - margin-top: 1.5rem; - } - - &:is(:last-of-type) { - margin-bottom: 1.5rem; - } - } -} \ No newline at end of file diff --git a/archives/modules/component/_preload.scss b/archives/modules/component/_preload.scss deleted file mode 100644 index 485b280..0000000 --- a/archives/modules/component/_preload.scss +++ /dev/null @@ -1,55 +0,0 @@ -.preload { - position: fixed; - display: flex; - align-items: center; - justify-content: center; - background-color: var(--clr-background); - top: 0; - left: 0; - right: 0; - bottom: 0; - height: 100dvh; - z-index: 999; - flex-direction: column; - translate: 0 0; - opacity: 1; - transition: transform 1s ease-in-out; - - svg { - rotate: 0deg; - max-width: 10em; - animation: preload 2s infinite; - } - - &-text { - margin-top: 1em; - max-width: 30ch; - text-align: center; - } - - &.hidden { - translate: 0 -100vh; - } - - &.disabled { - display: none; - } -} - -@keyframes preload { - 25% { - rotate: 15deg; - } - - 50% { - rotate: 0deg; - } - - 75% { - rotate: -15deg; - } - - 100% { - rotate: 0deg; - } -} diff --git a/archives/modules/component/_prototype.scss b/archives/modules/component/_prototype.scss deleted file mode 100644 index 777d4e3..0000000 --- a/archives/modules/component/_prototype.scss +++ /dev/null @@ -1,259 +0,0 @@ -.prototype { - margin-top: 4rem; - position: relative; - isolation: isolate; - - &s { - display: flex; - flex-direction: column; - align-items: center; - flex-wrap: wrap; - justify-content: center; - gap: 7rem; - - @include breakpoint-up('large') { - margin: 3rem; - } - - @include breakpoint-down('medium') { - flex-direction: row; - } - } - - &-header { - margin-bottom: 1rem; - } - - &-card { - align-items: center; - display: flex; - border-radius: 0.3rem; - width: 100%; - - &:first-of-type { - @include breakpoint-down('medium') { - margin-top: 2em; - } - } - - @include breakpoint-down('medium') { - flex-direction: column; - } - - &:nth-of-type(odd) { - .prototype-header { - order: 2; - - &::before { - background-color: var(--clr-accent-dark); - rotate: -6deg; - } - - @include breakpoint-down('medium') { - order: 0; - } - } - - .prototype-description { - .text { - padding: 1rem; - padding-right: 2rem; - } - - .links { - justify-content: flex-start; - padding-left: 1rem; - } - } - } - } - - &-description { - flex: 1 1 auto; - z-index: 5; - - ul { - li { - margin-top: 0.3rem; - } - } - - .text { - padding: 1rem; - padding-left: 2rem; - - .language { - display: inline-block; - padding: 0 0.1rem; - background-color: var(--clr-accent-dark); - color: var(--clr-white); - border-radius: 0.3rem; - user-select: none; - font-size: 0.8rem; - } - - @include breakpoint-down('medium') { - padding-left: 0; - } - - ul { - margin-top: 0.8rem; - border-radius: 0.3rem; - background-color: hsl(0, 0%, 10%); - padding: 1rem; - box-shadow: inset 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - } - } - - .links { - display: flex; - align-items: flex-end; - justify-content: flex-end; - padding-right: 1rem; - - a { - background-color: var(--clr-accent); - color: var(--clr-white); - padding: 0.5rem 1rem; - border-radius: 0.3rem; - transition: all 300ms ease-out; - display: flex; - - svg { - flex: 0 0 auto; - height: 1.5rem; - } - - &:hover { - filter: brightness(120%); - } - } - } - - &_header { - display: flex; - margin-bottom: 1em; - gap: 0.5em; - font-weight: 700; - align-items: center; - line-height: 1.2; - } - } - - &-more { - display: flex; - flex-wrap: wrap; - gap: 0.4rem; - - i { - display: inline-block; - font-style: normal; - padding: 0.2rem 0.7rem; - background-color: var(--clr-background-900); - border-radius: 0.3rem; - - &.error { - background-color: hsl(0, 100%, 43%); - color: var(--clr-white); - } - - &.active { - background-color: #067e06; - color: var(--clr-white); - } - } - - .status { - background-image: linear-gradient( - -90deg, - var(--clr-accent), - var(--clr-accent-dark) - ); - padding: 0 0.7rem; - display: flex; - align-items: center; - border-radius: 0.3rem; - color: white; - margin-right: 0.3rem; - } - } - - &-header { - flex: 0 0 auto; - position: relative; - width: 25rem; - height: 20rem; - color: var(--clr-white); - - @include breakpoint-down('medium') { - width: 100%; - max-width: 25rem; - } - - &:hover { - .note { - scale: 1.1; - } - } - - &::before { - content: ''; - position: absolute; - width: 100%; - height: 100%; - rotate: 6deg; - background: var(--clr-accent); - left: -0.7rem; - border-radius: 40% 20% / 10%; - } - } - - .note { - article { - padding: 1rem; - height: 100%; - overflow-y: auto; - scrollbar-width: none; - position: relative; - - &::-webkit-scrollbar { - width: 0; - } - } - - pre { - overflow: scroll; - &::-webkit-scrollbar { - width: 0; - } - } - } - - &-svg { - &_top { - position: absolute; - top: -16.0625em; - z-index: -1; - color: var(--clr-accent); - - @include breakpoint-up('large') { - top: -20.0625em; - } - - @include breakpoint-down('small') { - top: -10em; - } - - @include breakpoint-up('xlarge') { - top: -25em; - } - } - - &_bottom { - position: absolute; - bottom: -0.0625em; - z-index: -1; - color: var(--clr-background); - } - } -} diff --git a/archives/modules/component/_title.scss b/archives/modules/component/_title.scss deleted file mode 100644 index 2ab2c8d..0000000 --- a/archives/modules/component/_title.scss +++ /dev/null @@ -1,338 +0,0 @@ -.title { - &-container { - display: flex; - gap: 2rem; - max-width: 70rem; - padding: 3rem 2rem; - align-items: flex-start; - z-index: 0; - - @include breakpoint-up('large') { - // background-color: var(--clr-background); - border-radius: 0.3em; - } - - .title { - &-left { - flex: 1 0 auto; - max-width: 45ch; - } - - &-right { - position: relative; - width: 25rem; - height: 20rem; - - @include breakpoint-down('medium') { - width: 100%; - } - - &:hover { - .note.has-been-animated { - rotate: 0deg; - scale: 1.1; - } - } - - .note.has-been-animated { - rotate: 3deg; - --_scale: 300ms; - --_rotate: 700ms; - } - - .note { - rotate: -10deg; - } - - &::before { - rotate: 9deg; - content: ''; - position: absolute; - width: 100%; - height: 100%; - background-color: var(--clr-complementary); - left: -0.7rem; - border-radius: 40% 20% / 90%; - animation: rolling 3s infinite ease-in; - } - - @keyframes rolling { - 50% { - rotate: -9deg; - scale: 1.01; - } - } - } - } - - @include breakpoint-down('medium') { - flex-direction: column-reverse; - } - } - - &-side { - &_top { - display: flex; - flex: 0 0 auto; - flex-direction: column; - gap: 0.4rem; - width: 100%; - } - - &_bottom { - width: 100%; - font-size: 1.2rem; - - h2 { - margin-bottom: 0.5rem; - text-transform: uppercase; - letter-spacing: 0.1rem; - - @include breakpoint-down('small') { - font-size: 1.2rem; - } - } - - .status-area { - display: flex; - align-items: center; - justify-content: flex-start; - gap: 0.5em; - font-size: 1rem; - margin-bottom: 0.5em; - - >.status-icon { - width: 0.75em; - height: 0.75em; - background-color: var(--clr-accent); - border-radius: 50%; - } - - .status-hoverable { - position: relative; - cursor: pointer; - - &:is(:hover, :focus), - &:has(:is(:hover, :focus)) { - .status-card { - visibility: visible; - user-select: text; - pointer-events: unset; - translate: -50% 0%; - } - } - - .status-card { - visibility: hidden; - user-select: none; - pointer-events: none; - background-color: var(--clr-background); - box-shadow: 0 0.625rem 1.25rem hsla(0, 0%, 0%, 0.4); - // box-shadow: 0 0.625rem 1.25rem hsla(270, 71%, 22%, 0.7); - padding: 1em; - left: 50%; - border-radius: 0.2em; - width: 25ch; - position: absolute; - z-index: 70; - margin-top: 0.2em; - translate: -50% 20%; - transition: all 200ms ease-in-out; - - &-header { - font-size: 0.9em; - letter-spacing: normal; - margin-bottom: unset; - } - - &-body { - margin-top: 1em; - display: flex; - gap: 1em; - - .status-card-left { - display: flex; - align-items: center; - justify-content: center; - background-color: var(--clr-foreground-900); - border-radius: 0.4em; - width: 6ch; - height: 6ch; - flex: 0 0 auto; - - svg { - width: 75%; - height: 75%; - } - } - - .status-card-right { - font-size: 0.7em; - font-weight: normal; - } - } - - &-button { - text-align: center; - font-weight: normal; - display: block; - width: 100%; - margin-top: 1em; - font-size: 0.9em; - background-color: var(--clr-accent); - padding: 0.4em; - border-radius: 0.2em; - color: var(--clr-white); - - &:is(:hover, :focus) { - background-color: var(--clr-accent-dark); - color: var(--clr-white); - } - } - - &:is(:hover, :focus) { - visibility: visible; - user-select: text; - pointer-events: unset; - translate: -50% 0%; - } - } - } - } - } - } - - &-link { - display: block; - width: max-content; - } - - &-activity { - display: flex; - flex-direction: row; - align-items: center; - margin-bottom: -0.4rem; - - &__details { - margin-left: 0.4rem; - font-weight: 300; - user-select: none; - color: var(--clr-foreground-900); - - #month { - font-weight: 600; - } - - - #activity { - display: inline-block; - position: relative; - font-weight: 400; - z-index: 0; - - &::before { - content: ''; - position: absolute; - width: 70%; - height: 0.2rem; - bottom: 0; - border-bottom: 0.1rem dashed var(--clr-accent); - } - } - } - - #year, - svg, - #month { - color: var(--clr-foreground-900) - } - - svg { - width: 1.2rem; - } - } - - &-text { - font-size: 2.4rem; - font-weight: 700; - margin-left: -0.1rem; - transition: font-size 300ms ease-in; - } - - &-action { - display: flex; - flex-direction: row; - margin-top: 1.2rem; - color: var(--clr-white); - background: var(--clr-accent-dark); - background-image: linear-gradient(-90deg, - var(--clr-accent), - var(--clr-accent-dark)); - border-radius: 0.3rem; - padding: 0.5rem 1.5rem; - align-items: center; - gap: 0.4rem; - cursor: pointer; - font-size: 1.3rem; - transition: all 300ms ease-out; - - &:hover, - &:focus-visible { - color: hsl(0, 0%, 80%); - background-color: var(--clr-accent); - filter: brightness(140%); - box-shadow: 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - scale: 1.04; - } - - svg { - height: 1.3rem; - width: 1.3rem; - } - - &[disabled] { - cursor: not-allowed; - - &:hover { - scale: 0.96; - box-shadow: inset 0 0.3125rem 0.625rem hsla(0, 0%, 0%, 0.4); - filter: grayscale(100); - } - } - - &.secondary { - background-color: var(--clr-background-800); - color: var(--clr-foreground-800); - background-image: none; - box-shadow: none; - - &:hover, - &:focus-visible { - filter: brightness(100%); - scale: 0.98; - } - } - - &__group { - display: flex; - gap: 0.6rem; - } - } - - &-submenu { - display: flex; - position: relative; - margin-top: -0.4rem; - margin-left: 0.4rem; - align-items: center; - gap: 0.3rem; - color: var(--clr-foreground-800); - user-select: none; - z-index: 0; - - svg { - height: 1rem; - rotate: 180deg; - } - } -} \ No newline at end of file diff --git a/archives/style.scss b/archives/style.scss deleted file mode 100644 index 5dec5b8..0000000 --- a/archives/style.scss +++ /dev/null @@ -1,25 +0,0 @@ -@import './modules/vars'; -@import './modules/mixin'; -@import './modules/global'; - -@import './modules/header'; -@import './modules/component/nav'; -@import './modules/component/title'; -@import './modules/main'; -@import './modules/footer'; - -@import './modules/component/prototype'; -@import './modules/component/card'; -@import './modules/component/partof'; -@import './modules/component/preload'; - -@import './modules/component/aboutme'; -@import './modules/component/hero'; -@import './modules/component/featured'; -@import './modules/component/post'; -@import './modules/component/article'; -@import './modules/component/clients'; -@import './modules/component/animation'; -@import './modules/component/contact'; - -@import './modules/component/error'; \ No newline at end of file diff --git a/bin/ewTools.class.js b/bin/ewTools.class.js new file mode 100644 index 0000000..e89e095 --- /dev/null +++ b/bin/ewTools.class.js @@ -0,0 +1,228 @@ +import fs from "fs" +import path from "path" +import dotenv from "dotenv" +import chalk from "chalk" + +class Configuration { + static load() { + dotenv.config() + } + + static get(key) { + Configuration.load() + return process.env[key] + } +} + +class Logger { + static isDevelopment = Configuration.get("TOOLS_ENV") === "development" + static debugModeSuffix = chalk.gray(`[debug]`) + static Types = Object.freeze({ + INFO: "Info", + WARN: "Warning", + ERROR: "Error", + EMPTY: "" + }) + + static log(text, type = Logger.Types.EMPTY) { + const typeFormatted = type ? `${type}:` : "" + if (this.isDevelopment) console.log(`${Ewtools.programSuffix}${this.debugModeSuffix} ${typeFormatted}`, text) + } +} + +class FlagsProcessor { + static formatFlagsString(args) { + if (!Array.isArray(args) || args.length === 0) return "" + let isPreviousDump = false + + return args.map((token, index) => { + if (index === 0) return token + if (token.startsWith("-") || isPreviousDump) { + if (token.includes("dump")) { isPreviousDump = true } else { isPreviousDump = false } + return "+" + token + } + + return ";" + token + }).join("") + } + + static parseFlags(flags) { + const paramWithValue = flags.split("+") + const flagsObject = {} + paramWithValue.forEach(token => { + let [attr, value] = token.split(";") + if (!value) value = true + if (attr.startsWith("--")) { + flagsObject[attr.slice(2)] = value + } else { + flagsObject[attr.slice(1)] = value + } + }) + return flagsObject + } +} + +class FileHandler { + static ActionTypes = Object.freeze({ MAKE: "make", DELETE: "delete", REMOVE: "remove" }) + static EntityTypes = Object.freeze({ BLOG: "blog", PROJECT: "project", PROTOTYPE: "prototype" }) + + static generateFilePath(title, type, flags) { + let date = flags.d || new Date().toISOString() + const [year, month, day] = date.split("T")[0].split("-") + + if (!(year && month && day)) { + console.log("Invalid date format. Please provide -d yyyy-mm-dd") + process.exit(1) + } + + const formattedDate = type === this.EntityTypes.PROTOTYPE ? year : `${year}-${month}-${day}` + const filename = `${formattedDate}-${title.replace(/\s+/g, "-").toLowerCase()}.md`.replace(/[<>:"\/\\|?*]+/g, "") + const filepath = path.join(process.cwd(), `src/articles/${type}`, filename) + + return { filename, filepath } + } + + static handleError(error, filename, { type, action = this.ActionTypes.MAKE }) { + if (error) return console.error(`${Ewtools.programSuffix} Error creating file: ${error.message}`) + console.log(`${Ewtools.programSuffix} File "${type}/${filename}" ${action === this.ActionTypes.MAKE ? "created" : "removed"} successfully.`) + } + + static generateFile(title, entityType, flags, getContentFunction) { + const { filename, filepath } = this.generateFilePath(title, entityType, flags) + const content = getContentFunction(title, flags) + fs.writeFile(filepath, content, (error) => this.handleError(error, filename, { type: entityType })) + } + + static removeFile(title, entityType, flags) { + const { filename, filepath } = this.generateFilePath(title, entityType, flags) + fs.rm(filepath, (error) => this.handleError(error, filename, { type: entityType, action: this.ActionTypes.DELETE })) + } + + static getAllActionTypes() { + return Object.values(this.ActionTypes).map((action, index, arr) => (index === arr.length - 1 ? ` and \`${action}\`` : ` \`${action}\`,`)).join("") + } + + static getAllEntityTypes() { + return Object.values(this.EntityTypes).map((entity, index, arr) => (index === arr.length - 1 ? ` and \`${entity}\`` : ` \`${entity}\`,`)).join("") + } +} + +class ContentGenerator { + static tagsHandler(tags) { + return typeof tags === "string" ? tags.split(",").map(tag => `\n - ${tag}`).join("") : "" + } + + static getDefaultBlogContent(title, flags = {}) { + return `--- +title: ${title} +description: +author: +draft: true +date: ${new Date().toISOString()} +tags: + - post${ContentGenerator.tagsHandler(flags.t)} +---\n` + } + + static getDefaultProjectContent(title, flags = {}) { + return `--- +title: ${title} +description: +date: +endDate: +image: +linkDemo: +linkCode: +tags: + - project${ContentGenerator.tagsHandler(flags.t)} +---\n` + } + + static getDefaultPrototypeContent(title, flags = {}) { + return `--- +title: ${title} +status: 1 +description: +date: +linkDemo: +language: +code: |- +tags: + - prototype${ContentGenerator.tagsHandler(flags.t)} +---\n` + } +} + +class Ewtools { + static programSuffix = `${chalk.gray(`[`)}${chalk.green(`EwT`)}${chalk.gray(`/bin]`)}` + + constructor(args) { + this.args = args + this.command = args[0] + this.title = args[1] + this.restArgs = args.slice(2) + this.flags = FlagsProcessor.parseFlags(FlagsProcessor.formatFlagsString(this.restArgs)) + this.action = this.command.split(":")[0] + this.type = this.command.split(":")[1] + } + + validateArgs() { + if (this.args.length < 2) { + console.log(`${Ewtools.programSuffix} Usage: ${chalk.green(`./ewtools.bat make:blog "title"`)}`) + process.exit(1) + } + } + + execute() { + this.validateArgs() + Logger.log(this.args, Logger.Types.INFO) + Logger.log(this.flags, Logger.Types.INFO) + Logger.log(FlagsProcessor.formatFlagsString(this.restArgs), Logger.Types.INFO) + + if (!this.flags.dump && !this.flags.dp) switch (this.action) { + case FileHandler.ActionTypes.MAKE: + this.handleMake() + break + + case FileHandler.ActionTypes.REMOVE: + case FileHandler.ActionTypes.DELETE: + this.handleDelete() + break + + default: + console.log(`${Ewtools.programSuffix} Invalid Action. Valid Actions: ${chalk.green(FileHandler.getAllActionTypes())}`) + } + } + + handleMake() { + switch (this.type) { + case FileHandler.EntityTypes.BLOG: + FileHandler.generateFile(this.title, this.type, this.flags, ContentGenerator.getDefaultBlogContent) + break + + case FileHandler.EntityTypes.PROJECT: + FileHandler.generateFile(this.title, this.type, this.flags, ContentGenerator.getDefaultProjectContent) + break + + case FileHandler.EntityTypes.PROTOTYPE: + FileHandler.generateFile(this.title, this.type, this.flags, ContentGenerator.getDefaultPrototypeContent) + break + + default: + console.log(`${Ewtools.programSuffix} Invalid Type, Valid Types: ${chalk.green(FileHandler.getAllEntityTypes())}`) + } + } + + handleDelete() { + if (this.type === "" || this.type === undefined) return console.log(`${Ewtools.programSuffix} Invalid Type, Valid Types: ${chalk.green(FileHandler.getAllEntityTypes())}`) + FileHandler.removeFile(this.title, this.type, this.flags) + } +} + +const args = process.argv.slice(2) + +if (args.length <= 0) { + console.log(`${Ewtools.programSuffix} Usage: ${chalk.green(`./ewtools.bat make:blog "title"`)}`) +} else { + new Ewtools(args).execute() +} \ No newline at end of file diff --git a/bin/ewTools.js b/bin/ewTools.js new file mode 100644 index 0000000..4cf7ccb --- /dev/null +++ b/bin/ewTools.js @@ -0,0 +1,199 @@ +import fs from "fs" +import path from "path" +import dotenv from "dotenv" +import chalk from "chalk" + +dotenv.config() +const programSuffix = `${chalk.gray(`[`)}${chalk.green(`EwT`)}${chalk.gray(`/bin]`)}` +const debugModeSuffix = chalk.gray(`[debug]`) +const isDevelopment = process.env.TOOLS_ENV === "development" +const LoggerType = Object.freeze({ + INFO: "Info", + WARN: "Warning", + ERROR: "Error", + EMPTY: "" +}) + +const getAllActionTypes = () => Object.values(ActionTypes).map((action, index, arr) => (index === arr.length - 1 ? ` and \`${action}\`` : ` \`${action}\`,`)).join("") + +const getAllEntityTypes = () => Object.values(EntityTypes).map((entity, index, arr) => (index === arr.length - 1 ? ` and \`${entity}\`` : ` \`${entity}\`,`)).join("") + +const Logger = (text, type = LoggerType.EMPTY) => { + const typeFormatted = (type === LoggerType.EMPTY) ? `${type}` : `${type}:` + if (isDevelopment) console.log(`${programSuffix}${debugModeSuffix} ${typeFormatted}`, text) +} + +const formatFlagsString = (args) => { + if (!Array.isArray(args) || args.length === 0) return ""; + let isPreviousDump = false + + return args.map((token, index) => { + if (index === 0) return token + if (token.startsWith("-") || isPreviousDump) { + if (token.includes("dump")) { isPreviousDump = true } else { isPreviousDump = false } + return "+" + token + } + + return ";" + token + }).join("") +} + +const flagsHandler = (flags) => { + const paramWithValue = flags.split("+") + const flagsObject = {} + paramWithValue.forEach(token => { + let [attr, value] = token.split(";") + if (!value) value = true + if (attr.startsWith("--")) { + flagsObject[attr.slice(2)] = value + } else { + flagsObject[attr.slice(1)] = value + } + }) + return flagsObject +} + +const args = process.argv.slice(2) +if (args.length <= 0) { + console.log(`${programSuffix} Usage: ${chalk.green(`./ewtools.bat make:blog "title"`)}`) + process.exit() +} +const [command, title, ...restArgs] = args +const [action, type] = command.split(':') +const formattedFlags = formatFlagsString(restArgs) +const flagsObject = flagsHandler(formattedFlags) +Logger(args, LoggerType.INFO) +Logger(flagsObject, LoggerType.INFO) +Logger(formattedFlags, LoggerType.INFO) + +const ActionTypes = Object.freeze({ + MAKE: "make", + DELETE: "delete", + REMOVE: "remove" +}) + +const EntityTypes = Object.freeze({ + BLOG: "blog", + PROJECT: "project", + PROTOTYPE: "prototype" +}) + +if (args.length < 2) { + console.log(`${programSuffix} Usage: ${chalk.green(`./ewtools.bat make:blog "title"`)}`) + process.exit(1) +} + +const handleError = (error, filename, { type, action = ActionTypes.MAKE } = {}) => { + if (error) return console.error(`${programSuffix} Error creating file: ${error.message}`) + const currentAction = (action === ActionTypes.MAKE) ? "created" : "removed" + console.log(`${programSuffix} File "${type}/${filename}" ${currentAction} successfully.`) +} + +const generateFile = (title, entityType, getContent) => { + const { filename, filepath } = generateFilePath(title, entityType) + const content = getContent(title, flagsObject) + fs.writeFile(filepath, content, error => handleError(error, filename, { type: entityType })) +} + +const generateBlogFile = (title) => generateFile(title, EntityTypes.BLOG, getDefaultBlogContent) + +const generateProjectFile = (title) => generateFile(title, EntityTypes.PROJECT, getDefaultProjectContent) + +const generatePrototypeFile = (title) => generateFile(title, EntityTypes.PROTOTYPE, getDefaultPrototypeContent) + +const removeFile = (title, entityType) => { + const { filename, filepath } = generateFilePath(title, entityType) + fs.rm(filepath, (error) => handleError(error, filename, { action: ActionTypes.DELETE, type: entityType })) +} + +const generateFilePath = (title, type) => { + let date = flagsObject.d || new Date().toISOString() + const [year, month, day] = date.split("T")[0].split("-") + if (!(year && month && day)) { + console.log(`${programSuffix} Invalid date format. Please provide -d yyyy-mm-dd`) + process.exit(1) + } + const formattedDate = type === EntityTypes.PROTOTYPE ? year : `${year}-${month}-${day}` + const filename = `${formattedDate}-${title.replace(/\s+/g, "-").toLowerCase()}.md`.replace(/[<>:"\/\\|?*]+/g, "") + const filepath = path.join(process.cwd(), `src/articles/${type}`, filename) + + return { filename, filepath } +} + +const tagsHandler = (tags) => typeof tags === "string" ? tags.split(",").map(tag => `\n - ${tag}`).join("") : "" + +const getDefaultBlogContent = (title, flags = {}) => `--- +title: ${title} +description: +author: +draft: true +date: ${new Date().toISOString()} +tags: + - post${tagsHandler(flags.t)} +---\n` +const getDefaultProjectContent = (title, flags = {}) => `--- +title: ${title} +description: +date: +endDate: +image: +linkDemo: +linkCode: +tags: + - project${tagsHandler(flags.t)} +---\n` +const getDefaultPrototypeContent = (title, flags = {}) => `--- +title: ${title} +status: 1 +description: +date: +linkDemo: +language: +code: |- +tags: + - prototype${tagsHandler(flags.t)} +---\n` + +const handleMake = (type, title) => { + switch (type) { + case EntityTypes.BLOG: + generateBlogFile(title) + break + case EntityTypes.PROJECT: + generateProjectFile(title) + break + case EntityTypes.PROTOTYPE: + generatePrototypeFile(title) + break + default: + console.log(`${programSuffix} Invalid Type, Valid Types: ${getAllEntityTypes()}`) + } +} + +const handleDelete = (type, title) => { + switch (type) { + case EntityTypes.BLOG: + removeFile(title, EntityTypes.BLOG) + break + case EntityTypes.PROJECT: + removeFile(title, EntityTypes.PROJECT) + break + case EntityTypes.PROTOTYPE: + removeFile(title, EntityTypes.PROTOTYPE) + break + default: + console.log(`${programSuffix} Invalid Type, Valid Types: ${getAllEntityTypes()}`) + } +} + +if (!flagsObject.dump && !flagsObject.dp) switch (action) { + case ActionTypes.MAKE: + handleMake(type, title) + break + case ActionTypes.DELETE: + case ActionTypes.REMOVE: + handleDelete(type, title) + break + default: + console.log(`${programSuffix} Invalid Action. Valid Actions: ${getAllActionTypes()}`) +} diff --git a/bin/minify.js b/bin/minify.js new file mode 100644 index 0000000..e919b8e --- /dev/null +++ b/bin/minify.js @@ -0,0 +1,25 @@ +import { glob } from "glob"; +import chalk from "chalk"; +import { execSync } from "child_process"; +import path from "path"; +import fs from "fs"; + +const files = glob.sync("src/assets/js/*.js"); +console.log( + `${chalk.gray(`[`)}${chalk.green(`Ew`)}${chalk.gray(`/Terser]`)}`, + chalk.white(`Minifying ${files.length} file(s)`) +); +for (const file of files) { + const out = path.join("public/js", path.basename(file)); + fs.mkdirSync(path.dirname(out), { recursive: true }); + execSync(`terser "${file}" -o "${out}" -c -m`); + console.log( + `${chalk.gray(`[`)}${chalk.green(`Ew`)}${chalk.gray(`/Terser]`)}`, + chalk.white(`Writing ${out}`), + chalk.gray(`from ${file}`) + ); +} +console.log( + `${chalk.gray(`[`)}${chalk.green(`Ew`)}${chalk.gray(`/Terser]`)}`, + chalk.white(`Minification complete ${files.length} file(s)`) +); \ No newline at end of file diff --git a/ewtools.bat b/ewtools.bat new file mode 100644 index 0000000..cd8f9a7 --- /dev/null +++ b/ewtools.bat @@ -0,0 +1,3 @@ +@echo off +set SCRIPT_DIR=%~dp0 +node %SCRIPT_DIR%/bin/ewTools.js %* \ No newline at end of file diff --git a/ewtools.class.bat b/ewtools.class.bat new file mode 100644 index 0000000..122f965 --- /dev/null +++ b/ewtools.class.bat @@ -0,0 +1,3 @@ +@echo off +set SCRIPT_DIR=%~dp0 +node %SCRIPT_DIR%/bin/ewTools.class.js %* \ No newline at end of file diff --git a/lib/highlightjs-lines.js b/lib/highlightjs-lines.js new file mode 100644 index 0000000..f972dfb --- /dev/null +++ b/lib/highlightjs-lines.js @@ -0,0 +1,43 @@ +/** + * This will convert every lines that is not + * starts with - or + (diff-support) to be encase + * inside a div with the class of hljs-line + * + * @param {*} hljs + * @returns + */ + +export default function (hljs) { + function highlight(code, options) { + const result = hljs.highlight(code, { ...options, language: options.language }) + const lines = result.value.split('\n') + + const processedLines = lines.map(line => { + const hasAddition = line.startsWith(`+`) + const hasDeletion = line.startsWith(`-`) + const isEmpty = line === "" + let outputLine = line.replace("+ ", "+").replace("- ", "-"); + if (!isEmpty && !hasAddition && !hasDeletion) { + outputLine = `${line}` + } + + return outputLine + }).join('\n') + + return { + ...result, + code, + value: processedLines, + language: options.language + } + } + + return { + ...hljs, + highlight(codeOrLanguageName, optionsOrCode, ignoreIllegals = false) { + return typeof optionsOrCode === 'string' + ? highlight(optionsOrCode, { language: codeOrLanguageName, ignoreIllegals }) + : highlight(codeOrLanguageName, optionsOrCode) + } + } +} \ No newline at end of file diff --git a/lib/highlightjs-lines.test.js b/lib/highlightjs-lines.test.js new file mode 100644 index 0000000..b7793b4 --- /dev/null +++ b/lib/highlightjs-lines.test.js @@ -0,0 +1,122 @@ +import highlight from "highlight.js" +import highlightjsLines from "./highlightjs-lines.js" +import highlightDiff from "highlightjs-code-diff" +import markdownIt from "markdown-it" +import { describe, it } from 'node:test' +import assert from "node:assert" + +const hljsLinesWithDiff = highlightjsLines(highlightDiff(highlight)) +const hljsLines = highlightjsLines(highlight) + +describe('Highlight Lines using Markdown With Diff', () => { + const mIt = markdownIt({ + html: true, + linkify: true, + typographer: true, + highlight: function (str, lang) { + if (lang) { + try { + return hljsLinesWithDiff.highlight(str, { language: lang }).value + } catch (__) { } + } + return "" + } + }) + it('Test With Diff (Deletion)', () => { + const data = ` +\`\`\`diff:js +- setTimeout(() => throw new Error(),0) +\`\`\` +` + const value = mIt.render(data) + assert.strictEqual(value, `
-setTimeout(() => throw new Error(),0)\n
\n`) + }) + it('Test With Diff (Addition)', () => { + const data = ` +\`\`\`diff:js ++ setTimeout(() => throw new Error(),0) +\`\`\` +` + const value = mIt.render(data) + assert.strictEqual(value, `
+setTimeout(() => throw new Error(),0)\n
\n`) + }) + it('Normal Test', () => { + const data = ` +\`\`\`js +let time = new Date() +\`\`\` +` + const value = mIt.render(data) + assert.strictEqual(value, `
let time = new Date()\n
\n`) + }) +}) + +describe("Highlight Lines with Diff", async (t) => { + it("Test With Diff (Deletion)", () => { + const code = ` function helloWorld () { +- return 'Hello' + } +` + const { value } = hljsLinesWithDiff.highlight(code, { language: 'diff:js' }) + assert.strictEqual(value, ` function helloWorld () { +- return 'Hello' + }\n`) + }) + it("Test With Diff (Addition)", () => { + const code = ` function helloWorld () { ++ return 'Hello, world!' + } +` + const { value } = hljsLinesWithDiff.highlight(code, { language: 'diff:js' }) + assert.strictEqual(value, ` function helloWorld () { ++ return 'Hello, world!' + }\n`) + }) + it("Normal Test", () => { + const code = ` function helloWorld () { + return 'Hello' + return 'Hello, world!' + } +` + const { value } = hljsLinesWithDiff.highlight(code, { language: 'diff:js' }) + assert.strictEqual(value, ` function helloWorld () { + return 'Hello' + return 'Hello, world!' + }\n`) + }) +}) + +describe("Highlight Lines", async (t) => { + it("Test With Diff (Deletion)", () => { + const code = ` function helloWorld () { +- return 'Hello' + } +` + const { value } = hljsLines.highlight(code, { language: 'js' }) + assert.strictEqual(value, ` function helloWorld () { +- return 'Hello' + }\n`) + }) + it("Test With Diff (Addition)", () => { + const code = ` function helloWorld () { ++ return 'Hello, world!' + } +` + const { value } = hljsLines.highlight(code, { language: 'js' }) + assert.strictEqual(value, ` function helloWorld () { ++ return 'Hello, world!' + }\n`) + }) + it("Normal Test", () => { + const code = ` function helloWorld () { + return 'Hello' + return 'Hello, world!' + } +` + const { value } = hljsLines.highlight(code, { language: 'js' }) + assert.strictEqual(value, ` function helloWorld () { + return 'Hello' + return 'Hello, world!' + }\n`) + }) +}) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8a08ccc..3ac0b9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,24 +9,32 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@11ty/eleventy-img": "^6.0.4", + "chalk": "^5.4.1", "dotenv": "^16.5.0", + "glob": "^11.0.3", "highlight.js": "^11.5.1", + "highlightjs-code-diff": "^1.0.0", "markdown-it-anchor": "^9.2.0", "markdown-it-attrs": "^4.1.4", "markdown-it-bracketed-spans": "^1.0.1", "markdown-it-mark": "^4.0.0", "markdown-it-table-of-contents": "^0.9.0", "npm-run-all": "^4.1.5", - "sass": "^1.49.8" + "sass": "1.89.2", + "terser": "^5.42.0" }, "devDependencies": { "@11ty/eleventy": "^3.1.1", "@11ty/eleventy-plugin-rss": "^2.0.4", + "@playwright/test": "^1.53.1", + "@types/node": "^24.0.7", "autoprefixer": "^10.4.21", - "postcss": "^8.4.31", + "postcss": "^8.5.5", "postcss-cli": "^11.0.1", "postcss-import": "^16.1.0", - "postcss-nested": "^7.0.2" + "postcss-nested": "^7.0.2", + "prettier": "^3.5.3" } }, "node_modules/@11ty/dependency-tree": { @@ -53,9 +61,9 @@ } }, "node_modules/@11ty/eleventy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@11ty/eleventy/-/eleventy-3.1.1.tgz", - "integrity": "sha512-nsMCW44WSYzpi6JSQ1ar/wlotj/2cxuP4AABX5Dxqwol3IQ3SkEMgcAugP1t1mthv5I0kIB9lql1Jv/lhUHIkg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@11ty/eleventy/-/eleventy-3.1.2.tgz", + "integrity": "sha512-IcsDlbXnBf8cHzbM1YBv3JcTyLB35EK88QexmVyFdVJVgUU6bh9g687rpxryJirHzo06PuwnYaEEdVZQfIgRGg==", "dev": true, "license": "MIT", "dependencies": { @@ -66,13 +74,13 @@ "@11ty/eleventy-utils": "^2.0.7", "@11ty/lodash-custom": "^4.17.21", "@11ty/posthtml-urls": "^1.0.1", - "@11ty/recursive-copy": "^4.0.1", + "@11ty/recursive-copy": "^4.0.2", "@sindresorhus/slugify": "^2.2.1", "bcp-47-normalize": "^2.3.0", "chokidar": "^3.6.0", "debug": "^4.4.1", "dependency-graph": "^1.0.0", - "entities": "^6.0.0", + "entities": "^6.0.1", "filesize": "^10.1.6", "gray-matter": "^4.0.3", "iso-639-1": "^3.1.5", @@ -135,6 +143,49 @@ "url": "https://opencollective.com/11ty" } }, + "node_modules/@11ty/eleventy-fetch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-fetch/-/eleventy-fetch-5.1.0.tgz", + "integrity": "sha512-gSmCA3olJxRwtTkXyS+KIanq1kEufCC+JsHyTa7ta5NqmeUQlWA8zEngtXrDl+ebrAvFz2bNaxLd+0ERpnnSPQ==", + "license": "MIT", + "dependencies": { + "@11ty/eleventy-utils": "^2.0.7", + "@rgrove/parse-xml": "^4.2.0", + "debug": "^4.4.0", + "flatted": "^3.3.3", + "p-queue": "6.6.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-img": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-img/-/eleventy-img-6.0.4.tgz", + "integrity": "sha512-jSy9BmubVs0mN76dcXWfSYDgRU+1+/rq/SxUR3MgIvTUAJRDop5pFW+Z1f56CDcOlEHaiPqHgnfOlqRmJvXl7g==", + "license": "MIT", + "dependencies": { + "@11ty/eleventy-fetch": "^5.1.0", + "@11ty/eleventy-utils": "^2.0.7", + "brotli-size": "^4.0.0", + "debug": "^4.4.0", + "entities": "^6.0.0", + "image-size": "^1.2.1", + "p-queue": "^6.6.2", + "sharp": "^0.33.5" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, "node_modules/@11ty/eleventy-plugin-bundle": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-bundle/-/eleventy-plugin-bundle-3.0.6.tgz", @@ -175,7 +226,6 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/@11ty/eleventy-utils/-/eleventy-utils-2.0.7.tgz", "integrity": "sha512-6QE+duqSQ0GY9rENXYb4iPR4AYGdrFpqnmi59tFp9VrleOl0QSh8VlBr2yd6dlhkdtj7904poZW5PvGr9cMiJQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -216,9 +266,9 @@ } }, "node_modules/@11ty/recursive-copy": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@11ty/recursive-copy/-/recursive-copy-4.0.1.tgz", - "integrity": "sha512-Zsg1xgfdVTMKNPj9o4FZeYa73dFZRX856CL4LsmqPMvDr0TuIK4cH9CVWJyf0OkNmM8GmlibGX18fF0B75Rn1w==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@11ty/recursive-copy/-/recursive-copy-4.0.2.tgz", + "integrity": "sha512-174nFXxL/6KcYbLYpra+q3nDbfKxLxRTNVY1atq2M1pYYiPfHse++3IFNl8mjPFsd7y2qQjxLORzIjHMjL3NDQ==", "dev": true, "license": "ISC", "dependencies": { @@ -231,6 +281,473 @@ "node": ">=18" } }, + "node_modules/@emnapi/runtime": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@parcel/watcher": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", @@ -527,6 +1044,31 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@playwright/test": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.1.tgz", + "integrity": "sha512-Z4c23LHV0muZ8hfv4jw6HngPJkbbtZxTkxPNIg7cJcTc9C28N/p2q7g3JZS2SiKBBHJ3uM1dgDye66bB7LEk5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.53.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rgrove/parse-xml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@rgrove/parse-xml/-/parse-xml-4.2.0.tgz", + "integrity": "sha512-UuBOt7BOsKVOkFXRe4Ypd/lADuNIfqJXv8GvHqtXaTYXPPKkj2nS2zPllVsrtRjcomDhIJVBnZwfmlI222WH8g==", + "license": "ISC", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@sindresorhus/slugify": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz", @@ -585,6 +1127,16 @@ "license": "MIT", "peer": true }, + "node_modules/@types/node": { + "version": "24.0.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.7.tgz", + "integrity": "sha512-YIEUUr4yf8q8oQoXPpSlnvKNVKDQlPMWrmOcgzoduo7kvA2UF0/BwJ/eMKFTiTtkNL17I0M6Xe2tvwFU7be6iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, "node_modules/a-sync-waterfall": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", @@ -593,10 +1145,9 @@ "license": "MIT" }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -619,13 +1170,15 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { @@ -884,9 +1437,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -906,10 +1459,22 @@ "node": ">=8" } }, + "node_modules/brotli-size": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/brotli-size/-/brotli-size-4.0.0.tgz", + "integrity": "sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==", + "license": "MIT", + "dependencies": { + "duplexer": "0.1.1" + }, + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -927,8 +1492,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -939,6 +1504,12 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -987,9 +1558,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001721", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz", - "integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==", + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", "dev": true, "funding": [ { @@ -1008,26 +1579,15 @@ "license": "CC-BY-4.0" }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/chokidar": { @@ -1070,6 +1630,118 @@ "node": ">=12" } }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cliui/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1085,6 +1757,34 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -1102,28 +1802,17 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=4.8" - } - }, - "node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "license": "ISC", - "bin": { - "semver": "bin/semver" + "node": ">= 8" } }, "node_modules/cssesc": { @@ -1194,7 +1883,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -1345,9 +2033,9 @@ } }, "node_modules/dotenv": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", - "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.0.tgz", + "integrity": "sha512-Omf1L8paOy2VJhILjyhrhqwLIdstqm1BvcDPKg4NGAlkwEu9ODyrFbvk8UymUOMCT+HXo31jg1lArIrVAAhuGA==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -1370,6 +2058,17 @@ "node": ">= 0.4" } }, + "node_modules/duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1378,17 +2077,16 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.165", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz", - "integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==", + "version": "1.5.176", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.176.tgz", + "integrity": "sha512-2nDK9orkm7M9ZZkjO3PjbEd3VUulQLyg5T9O3enJdFvUg46Hzd4DUvTvAuEgbdHYXyFsiG4A5sO9IzToMH1cDg==", "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT" }, "node_modules/encodeurl": { @@ -1402,10 +2100,9 @@ } }, "node_modules/entities": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", - "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", - "dev": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -1640,6 +2337,12 @@ "node": ">= 8" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -1654,9 +2357,9 @@ } }, "node_modules/fdir": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", - "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1727,6 +2430,12 @@ "dev": true, "license": "MIT" }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "license": "ISC" + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -1742,6 +2451,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -1898,6 +2623,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -2081,6 +2829,12 @@ "node": ">=12.0.0" } }, + "node_modules/highlightjs-code-diff": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-code-diff/-/highlightjs-code-diff-1.0.0.tgz", + "integrity": "sha512-45hH1HHPNmnnUyG1IaiUdfe2Bz7cKDOD/WuwvVBd34jYi1/xh2eHlbumvhL+ky6d0HSGGCBP7COYB/+351rXrg==", + "license": "Unlicense" + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -2147,11 +2901,31 @@ "node": ">= 0.8" } }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "license": "MIT" + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/internal-slot": { @@ -2390,7 +3164,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2620,6 +3393,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2636,6 +3415,21 @@ "node": ">=6.0" } }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2763,19 +3557,24 @@ "node": ">=4" } }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, "engines": { "node": ">=4" } }, + "node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/luxon": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz", @@ -2874,13 +3673,26 @@ "dev": true, "license": "MIT", "dependencies": { - "array-differ": "^1.0.0", - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "minimatch": "^3.0.0" + "array-differ": "^1.0.0", + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "minimatch": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/maximatch/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, "node_modules/mdurl": { @@ -2961,15 +3773,18 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": "*" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -2986,7 +3801,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -3010,7 +3824,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -3130,6 +3943,108 @@ "node": ">= 4" } }, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/npm-run-all/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/nunjucks": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", @@ -3237,6 +4152,62 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/parse-srcset": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", @@ -3255,12 +4226,12 @@ } }, "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/path-parse": { @@ -3269,6 +4240,43 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3302,12 +4310,60 @@ } }, "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.10.0" + } + }, + "node_modules/playwright": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.1.tgz", + "integrity": "sha512-LJ13YLr/ocweuwxyGf1XNFWIU4M2zUSo149Qbp+A4cpwDjsxRPj7k6H25LBrEHiEwxvRbD8HdwvQmRMSvquhYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.53.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.1.tgz", + "integrity": "sha512-Z46Oq7tLAyT0lGoFx4DOuB1IA9D1TPj0QkYxpPVUnGDqHHvDpCftu1J2hM2PiWsNMoZh8+LQaarAWcDfPBc6zg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/please-upgrade-node": { @@ -3330,9 +4386,9 @@ } }, "node_modules/postcss": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", - "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -3401,9 +4457,9 @@ } }, "node_modules/postcss-import": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.0.tgz", - "integrity": "sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==", + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.1.tgz", + "integrity": "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3585,6 +4641,22 @@ "node": ">=12" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", @@ -3611,6 +4683,15 @@ "node": ">=6" } }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -3631,37 +4712,15 @@ "pify": "^2.3.0" } }, - "node_modules/read-cache/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "license": "MIT", "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "license": "MIT", - "dependencies": { - "pify": "^3.0.0" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, "engines": { "node": ">=4" @@ -3784,12 +4843,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -3806,12 +4859,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-push-apply/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", @@ -3830,9 +4877,9 @@ } }, "node_modules/sass": { - "version": "1.89.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.1.tgz", - "integrity": "sha512-eMLLkl+qz7tx/0cJ9wI+w09GQ2zodTkcE/aVfywwdlRcI3EO19xGnbmJwg/JMIm+5MxVJ6outddLZ4Von4E++Q==", + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz", + "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", "license": "MIT", "dependencies": { "chokidar": "^4.0.0", @@ -3864,12 +4911,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/sass/node_modules/immutable": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz", - "integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==", - "license": "MIT" - }, "node_modules/sass/node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -3901,7 +4942,6 @@ "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3993,25 +5033,73 @@ "dev": true, "license": "ISC" }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/sharp/node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", "dependencies": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/shell-quote": { @@ -4098,6 +5186,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4118,6 +5233,15 @@ "node": ">=8.0.0" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4127,6 +5251,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -4203,10 +5337,27 @@ } }, "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -4217,6 +5368,33 @@ "node": ">=8" } }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.padend": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", @@ -4292,10 +5470,25 @@ } }, "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -4304,6 +5497,15 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -4347,6 +5549,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, "node_modules/thenby": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", @@ -4394,6 +5620,13 @@ "node": ">=0.6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -4492,6 +5725,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -4568,15 +5808,18 @@ } }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/which-boxed-primitive": { @@ -4625,12 +5868,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-builtin-type/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, "node_modules/which-collection": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", @@ -4671,10 +5908,27 @@ } }, "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -4688,11 +5942,19 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -4704,11 +5966,10 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/color-convert": { + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -4717,13 +5978,56 @@ "node": ">=7.0.0" } }, - "node_modules/wrap-ansi/node_modules/color-name": { + "node_modules/wrap-ansi-cjs/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/ws": { "version": "8.18.2", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", @@ -4797,6 +6101,51 @@ "engines": { "node": ">=12" } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } } } } diff --git a/package.json b/package.json index c9ed8c5..fb7a5a6 100644 --- a/package.json +++ b/package.json @@ -6,15 +6,19 @@ "type": "module", "scripts": { "sass": "sass --no-source-map src/assets/scss/:public/css/", - "sass:compressed": "sass --style=compressed --no-source-map src/assets/scss:public/css/", - "postcss": "postcss public/css/*.css --use autoprefixer postcss-import postcss-nested --dir public/css", + "sass:compressed": "sass --no-source-map --style=compressed src/assets/scss:public/css/", + "postcss": "postcss public/css/*.css --use postcss-import autoprefixer postcss-nested --no-map --dir public/css", + "minify:js": "node bin/minify.js", + "prettify:html": "prettier --write public/**/*.html --ignore-path .prettierignore", "watch": "npm-run-all --parallel watch:*", "watch:eleventy": "eleventy --serve", "watch:sass": "npm run sass:compressed -- --watch", "watch:postcss": "npm run postcss -- --watch", - "build": "npm run sass:compressed && npm run postcss && eleventy", - "start": "npm-run-all sass:compressed postcss --parallel watch:*", - "dev": "npm run watch" + "build": "npm run sass:compressed && npm run postcss && npm run minify:js && eleventy && npm run prettify:html", + "start": "npm-run-all --parallel watch:eleventy", + "dev": "npm run watch", + "test:highlightjs-lines": "node ./lib/highlightjs-lines.test.js", + "test": "node --test" }, "repository": { "type": "git", @@ -30,21 +34,29 @@ "devDependencies": { "@11ty/eleventy": "^3.1.1", "@11ty/eleventy-plugin-rss": "^2.0.4", - "postcss": "^8.4.31", - "postcss-cli": "^11.0.1", + "@playwright/test": "^1.53.1", + "@types/node": "^24.0.7", "autoprefixer": "^10.4.21", + "postcss": "^8.5.5", + "postcss-cli": "^11.0.1", "postcss-import": "^16.1.0", - "postcss-nested": "^7.0.2" + "postcss-nested": "^7.0.2", + "prettier": "^3.5.3" }, "dependencies": { + "@11ty/eleventy-img": "^6.0.4", + "chalk": "^5.4.1", "dotenv": "^16.5.0", + "glob": "^11.0.3", "highlight.js": "^11.5.1", + "highlightjs-code-diff": "^1.0.0", "markdown-it-anchor": "^9.2.0", "markdown-it-attrs": "^4.1.4", "markdown-it-bracketed-spans": "^1.0.1", "markdown-it-mark": "^4.0.0", "markdown-it-table-of-contents": "^0.9.0", "npm-run-all": "^4.1.5", - "sass": "^1.49.8" + "sass": "1.89.2", + "terser": "^5.42.0" } } diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..4c09e71 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,35 @@ +import { defineConfig, devices } from '@playwright/test'; + +const localhost = "http://localhost:8080" + +export default defineConfig({ + testDir: './tests', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: [['html', { open: 'never' }]], + use: { + baseURL: localhost, + trace: 'on-first-retry', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + ], + webServer: { + command: 'npm start', + url: localhost, + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/settings.example.json b/settings.example.json index a8d6b26..403a8b7 100644 --- a/settings.example.json +++ b/settings.example.json @@ -15,6 +15,5 @@ "startHour": 11, "endHour": 14 } - ], - "colorScheme": "default" + ] } diff --git a/settings.json b/settings.json index e51c930..b4f3269 100644 --- a/settings.json +++ b/settings.json @@ -25,6 +25,5 @@ "startHour": 7, "endHour": 17 } - ], - "colorScheme": "default" + ] } diff --git a/src/_includes/article.njk b/src/_includes/article.njk deleted file mode 100644 index 8f28fb6..0000000 --- a/src/_includes/article.njk +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: 'layouts/secondary.njk' -style: 'article.css' ---- -{% set previousPost = collections.post | getPreviousCollectionItem(page) %} -{% set nextPost = collections.post | getNextCollectionItem(page) %} - -
- {{ title }} -
-
-
-
-
- - Home › - Blog › {{ title }} - -

{{ title }}

-

{{ description }}

-
-
- - - - {{ author }} -
-
- - - - -
-
-
-
- {{ content | safe }} -
-
- {% if nextPost %} -
- ← Next Post
{{ nextPost.data.title }} - -
- {% endif %} - {% if previousPost %} -
- Previous Post →
{{ previousPost.data.title }} - -
- {% endif %} -
-
-
-
- - \ No newline at end of file diff --git a/src/_includes/components/meta.njk b/src/_includes/components/meta.njk index 226da33..151537e 100644 --- a/src/_includes/components/meta.njk +++ b/src/_includes/components/meta.njk @@ -2,65 +2,78 @@ - - + {% if author %} - + {% else %} - + {% endif %} + content="EmptyWork, Portofolio, Seorang Web Developer, Web Developer, HTML5, CSS3, Javascript, Stevarth, Ambon" /> - {% if description %} - - - - + + + + {% else %} - - - - + + + + {% endif %} - - {% if image %} - - - + + + {% else %} - - - + + + {% endif %} - {% if title %} - - - + + + {% else %} - - - + + + {% endif %} - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + diff --git a/src/_includes/components/nav.njk b/src/_includes/components/nav.njk index b8dc780..1934fce 100644 --- a/src/_includes/components/nav.njk +++ b/src/_includes/components/nav.njk @@ -1,8 +1,12 @@ - diff --git a/src/_includes/components/snippets/article.njk b/src/_includes/components/snippets/post-card.njk similarity index 100% rename from src/_includes/components/snippets/article.njk rename to src/_includes/components/snippets/post-card.njk diff --git a/src/_includes/components/snippets/project.njk b/src/_includes/components/snippets/project-card.njk similarity index 94% rename from src/_includes/components/snippets/project.njk rename to src/_includes/components/snippets/project-card.njk index 71e3df9..5ec8129 100644 --- a/src/_includes/components/snippets/project.njk +++ b/src/_includes/components/snippets/project-card.njk @@ -4,9 +4,11 @@ {% if project.data.image %}
Image of {{ project.data.title }}
diff --git a/src/_includes/components/snippets/prototype.njk b/src/_includes/components/snippets/prototype-card.njk similarity index 100% rename from src/_includes/components/snippets/prototype.njk rename to src/_includes/components/snippets/prototype-card.njk diff --git a/src/_includes/layouts/article.njk b/src/_includes/layouts/article.njk new file mode 100644 index 0000000..6e963bd --- /dev/null +++ b/src/_includes/layouts/article.njk @@ -0,0 +1,88 @@ +--- +layout: 'layouts/secondary.njk' +styles: + - 'article.css' + - 'highlight.css' +--- + +{% set previousPost = collections.post | getPreviousCollectionItem(page) %} +{% set nextPost = collections.post | getNextCollectionItem(page) %} +
+ {{ title }} +
+
+
+
+
+ + Home › + Blog{{ title }} + +

{{ title }}

+

{{ description }}

+
+
+ + + + {{ author }} +
+
+ + + + +
+
+
+
+ {{ content | safe }} +
+
+ {% if not nextPost.data.draft and nextPost %} +
+ ← Next Post +
+ {{ nextPost.data.title }} +
+ {% endif %} + {% if nextPost and isDevelopment and nextPost.data.draft %} +
+ ← Next Post (Draft) +
+ {{ nextPost.data.title }} +
+ {% endif %} + {% if not previousPost.data.draft and previousPost %} +
+ Previous Post → +
+ {{ previousPost.data.title }} +
+ {% endif %} + {% if previousPost and isDevelopment and previousPost.data.draft %} +
+ Previous Post (Draft) → +
+ {{ previousPost.data.title }} +
+ {% endif %} +
+
+
+
+ diff --git a/src/_includes/layouts/secondary.njk b/src/_includes/layouts/secondary.njk index 8576138..6553648 100644 --- a/src/_includes/layouts/secondary.njk +++ b/src/_includes/layouts/secondary.njk @@ -11,11 +11,13 @@ rel="stylesheet" /> {% if style %} + {% elif styles %} + {% for style in styles %} + + {% endfor %} {% else %} {% endif %} - diff --git a/src/_includes/sections/featured.njk b/src/_includes/sections/featured.njk index faacd69..5cf7f83 100644 --- a/src/_includes/sections/featured.njk +++ b/src/_includes/sections/featured.njk @@ -1,57 +1,57 @@ - + diff --git a/src/_includes/sections/hero.njk b/src/_includes/sections/hero.njk index 8bde749..31fd9e0 100644 --- a/src/_includes/sections/hero.njk +++ b/src/_includes/sections/hero.njk @@ -78,10 +78,10 @@
  • + class="title-action" download> - - - - - + {{ "logo.svg" | svg | safe }} Sit tight, the page you trying to access is currently being load \ No newline at end of file diff --git a/src/_includes/sections/prototypes.njk b/src/_includes/sections/prototypes.njk index 9513d7b..cb072f1 100644 --- a/src/_includes/sections/prototypes.njk +++ b/src/_includes/sections/prototypes.njk @@ -1,35 +1,35 @@ -
    - - - - - - -
    -
    - - Prototypes that I do while learning new thing. -
    -
      - {%- for post in collections.prototype | reverse -%} - {% include "components/snippets/prototype.njk" %} - {%- endfor -%} -
    -
    - - - - -
    +
    + + + + + + +
    +
    + + Prototypes that I do while learning new thing. +
    +
      + {%- for post in collections.prototype | reverse -%} + {% include "components/snippets/prototype-card.njk" %} + {%- endfor -%} +
    +
    + + + + +
    diff --git a/src/_includes/sections/work.njk b/src/_includes/sections/work.njk index 2750857..9052776 100644 --- a/src/_includes/sections/work.njk +++ b/src/_includes/sections/work.njk @@ -1,42 +1,40 @@ -
    -
    -
    - - Projects that I participated on or worked on. -
    - {% set lengthProjects = collections.project | length %} - {% if lengthProjects > 2 %} -
      - {% set reverseProjects = collections.project | reverse %} - {% set projects = reverseProjects | sliceRecent %} - {% else %} -
        - {% set projects = collections.project | reverse %} - {% endif %} - {%- for project in projects -%} - {% include 'components/snippets/project.njk' %} - {%- endfor -%} -
      - {% if lengthProjects > 3 %} - See more in Github... - {% endif %} -
    - - - - -
    +
    +
    +
    + + Projects that I participated on or worked on. +
    + {% set lengthProjects = collections.project | length %} + {% set reverseProjects = collections.project | reverse %} + {% set projects = reverseProjects %} +
      + {% if lengthProjects > 2 %} + {% set projects = reverseProjects | sliceRecent %} + {% endif %} + {%- for project in projects -%} + {% include "components/snippets/project-card.njk" %} + {%- endfor -%} +
    + {% if lengthProjects > 3 %} + See more in Github... + {% endif %} +
    + + + + +
    diff --git a/src/admin/config.yml b/src/admin/config.yml index e7ed0ea..987d464 100644 --- a/src/admin/config.yml +++ b/src/admin/config.yml @@ -2,7 +2,7 @@ backend: name: git-gateway branch: main media_folder: 'src/assets/images/' -public_folder: '/images/' +public_folder: '/assets/images/' collections: - name: 'blog' label: 'Blog' diff --git a/src/articles/blog/2021-12-26-formatting-testing.md b/src/articles/blog/2021-12-26-formatting-testing.md new file mode 100644 index 0000000..2c3cf87 --- /dev/null +++ b/src/articles/blog/2021-12-26-formatting-testing.md @@ -0,0 +1,186 @@ +--- +title: Formatting test for Markdown +description: The purpose of this file is to test all the markdown support and syntaxs +author: EmptyWork +draft: false +date: 2021-12-26T09:30:51.516Z +tags: + - post + - html + - javascript + - markdown +--- + +[[toc]] + +# Main header + +> Another example of the formatting, this is what supposed to be a blockqoute ==This is an example of the highlight=={.highlight} + +## Code snippet + +> You can only do this if you have certain understanding regarding how to do the thing that are meant to be that thing. +> +> > `this is an inline-quote` does it works? click here to [jump to code-snippet-2](#code-snippet-2), or you can just scroll down, also you can hover on the code blocks. +> > +> > ```bash +> > npm run install && npm run update +> > ``` +> > +> > > This a blockqoute inside a blockqoute inside a blockquote, it is a `block-ception` +> > > +> > > ```js +> > > const isDarkThemePreferred = +> > > !localStorage.theme && +> > > window.matchMedia('(prefers-color-scheme: dark)').matches +> > > const dataset = document.documentElement.dataset // Getting all dataset +> > > ``` + +### Secondary header + +The markdown is being handle by the markdown-it and eleventy, while some extra works is being done by vanila javascript. + +```sql +SELECT * FROM `ikan`; +``` + +```sql +DROP table `ikan`; +``` + +#### Code snippet + +```python +def greet(name): + print(f"Hello, {name}! Welcome to the Markdown formatting test.") + +greet("Visitor") +``` + +Here is a simple JavaScript function that adds two numbers and logs the result. + +### Code snippet 2 + +```javascript +function add(a, b) { + return a + b +} + +console.log(add(2, 3)) // Output: 5 +``` + +Here is a simple JavaScript function that adds two numbers and logs the result. + +```js +const schedulerLoader = (schedules = {}) => { + let time = new Date() + let currentTime = time.getUTCHours() + 9 + if (currentTime > 23) currentTime -= 23 + + if (replaceStatus) replaceStatus.textContent = 'Idle' + + setTimeout(() => { + for (let i = 0; i < schedules.length; i++) { + let { day, endHour, startHour } = schedules[i] + if (!replaceStatus) return + + if (days[currentDay] === toCapitalizeWord(day)) { + if (currentTime > startHour && currentTime < endHour) { + replaceStatus.textContent = 'Studying' + } + break + } + replaceStatus.textContent = 'Idle' + } + }, 0) +} +``` + +```diff:js +const schedulerLoader = (schedules = {}) => { + let time = new Date() + let currentTime = time.getUTCHours() + 9 + if (currentTime > 23) currentTime -= 23 + + if (replaceStatus) replaceStatus.textContent = 'Idle' +- setTimeout(() => throw new Error(),0) ++ setTimeout(() => { ++ for (let i = 0; i < schedules.length; i++) { ++ let { day, endHour, startHour } = schedules[i] ++ if (!replaceStatus) return ++ ++ if (days[currentDay] === toCapitalizeWord(day)) { ++ if (currentTime > startHour && currentTime < endHour) { ++ replaceStatus.textContent = 'Studying' ++ } ++ break ++ } ++ replaceStatus.textContent = 'Idle' ++ } ++ }, 0) +``` + +```md +├─ docs +│ ├─ .vuepress +│ │ └─ config.js +│ └─ README.md +└─ package.json +``` +And this is an example of markdown list: + +```md +- line +- line +- line + 1. stop it + 2. what + 3. don't worry +``` + +- line +- line + - another list + - what is this? +- line + 1. stop it + 2. what + 3. don't worry + +#### Code snippet 3 + +```js +const Logger = (text, type = LoggerType.EMPTY) => { + const typeFormatted = type === LoggerType.EMPTY ? `${type}` : `${type}:` + if (isDevelopment) console.log(`${typeFormatted}`, text) +} +``` + +##### Last line header + +```css +#body { + color: red; +} +``` + +Another test with json c + d control + d or meta + d. + +```json +{ + "id": "s9RT21XS8399#!821838" +} +``` + +What about error? you can do a combo of Shift + A, with this as the sole purpose of testing any random stuff. + +### Secondary header + +[#code-snippet](#code-snippet-1) is type of reference that we needs[[1]](#code-snippet-5). + +## Table? + +| Side | Face | Timeline | +| --------------- | ---------------------------------------------- | --------------------- | +| Side | Another one bites the dust | 2025-02-01 | +| For The Emperor | Light up the sky with the flame of the emperor | | diff --git a/src/articles/blog/2022-04-13-accessibility-the-future-of-web-development.md b/src/articles/blog/2022-04-13-accessibility-the-future-of-web-development.md index 129f497..643bb4f 100644 --- a/src/articles/blog/2022-04-13-accessibility-the-future-of-web-development.md +++ b/src/articles/blog/2022-04-13-accessibility-the-future-of-web-development.md @@ -8,7 +8,7 @@ draft: false date: 2022-04-13T04:05:25.911Z tags: - post -image: /images/accessibility.png +image: /assets/images/accessibility.png --- [[toc]] diff --git a/src/articles/blog/2023-16-09-handling-file-from-php-javascript.md b/src/articles/blog/2023-16-09-handling-file-from-php-javascript.md new file mode 100644 index 0000000..b63f232 --- /dev/null +++ b/src/articles/blog/2023-16-09-handling-file-from-php-javascript.md @@ -0,0 +1,22 @@ +--- +title: Handling file from PHP to Javascript +description: One of the problem with using PHP and Javascript, is that handling + file(s) by both of the language(s) at the same time. +author: EmptyWork +draft: true +date: 2023-09-16T10:11:37.394Z +tags: + - post + - thesis + - javascript + - php +image: /assets/images/sunlight-landscape-forest-sunset-hill-nature-5912-wallhere.com.jpg +--- + +[[toc]] + +> As one of the requirement for me to get my bachelor degree on _Computer Science_, I created [_Accessibility Testing Program (ATP)_](https://github.com/EmptyWork/lighthouse-accessibility-thesis) to help me concurrently testing multiple website using Google Lighthouse. + +The problem arose when I was trying to implemented GUI for my current _ATP_[[1]](#definition), + + diff --git a/src/articles/blog/blog.json b/src/articles/blog/blog.json index f12ff44..60fe54a 100644 --- a/src/articles/blog/blog.json +++ b/src/articles/blog/blog.json @@ -1,7 +1,7 @@ { - "layout": "article.njk", + "layout": "layouts/article.njk", "tags": "post", "author": "EmptyWork", - "image": "/images/emptywork.my.id-default-post.jpg", + "image": "/assets/images/emptywork.my.id-default-post.jpg", "draft": true } diff --git a/src/articles/project/2021-10-29-jobfair-website.md b/src/articles/project/2021-10-29-jobfair-website.md index 22413d3..d413a68 100644 --- a/src/articles/project/2021-10-29-jobfair-website.md +++ b/src/articles/project/2021-10-29-jobfair-website.md @@ -4,7 +4,7 @@ author: EmptyWork date: 2021-05-01 endDate: false tags: ['project'] -image: /images/jobfair.jpg +image: /assets/images/jobfair.jpg description: An website that aim to help Ambonese looking for their new opportunties linkDemo: https://jobfair-gpmsyaloom.herokuapp.com/ linkCode: false diff --git a/src/articles/project/2021-10-29-ukimexpo2019-website.md b/src/articles/project/2021-10-29-ukimexpo2019-website.md index e25bd2f..bd08f8f 100644 --- a/src/articles/project/2021-10-29-ukimexpo2019-website.md +++ b/src/articles/project/2021-10-29-ukimexpo2019-website.md @@ -4,7 +4,7 @@ author: EmptyWork date: 2019-10-11 endDate: 2019-10-28 tags: ['project'] -image: /images/expo.jpg +image: /assets/images/expo.jpg description: A showcase website for promoting Informatics Faculty of Christian University of Indonesia, Moluccas linkDemo: https://emptywork.github.io/UKIMexpo/ linkCode: https://github.com/EmptyWork/UKIMexpo diff --git a/src/articles/project/2022-04-13-event-betaqris-website.md b/src/articles/project/2022-04-13-event-betaqris-website.md index be5b777..bc454be 100644 --- a/src/articles/project/2022-04-13-event-betaqris-website.md +++ b/src/articles/project/2022-04-13-event-betaqris-website.md @@ -7,7 +7,7 @@ date: 2022-03-25T03:41:29.743Z endDate: 2022-04-06T03:41:29.825Z tags: - project -image: /images/278038707_1063095627615778_2238829942384153761_n.jpg +image: /assets/images/278038707_1063095627615778_2238829942384153761_n.jpg linkDemo: https://web.archive.org/web/20220909003651/https://betaqris.com/ linkCode: https://www.instagram.com/p/CcAk3nbL3MX/ --- diff --git a/src/articles/project/2023-05-20-event-ambonfunrun-website.md b/src/articles/project/2023-05-20-event-ambonfunrun-website.md index 2060525..6c9a85b 100644 --- a/src/articles/project/2023-05-20-event-ambonfunrun-website.md +++ b/src/articles/project/2023-05-20-event-ambonfunrun-website.md @@ -7,7 +7,7 @@ date: 2023-05-10T15:41:29.743Z endDate: 2023-05-20T09:41:29.825Z tags: - project -image: /images/345349877_1186703082083563_922805273957392018_n.jpg -linkDemo: https://ambonfunrun.com +image: /assets/images/345349877_1186703082083563_922805273957392018_n.jpg +linkDemo: https://web.archive.org/web/20230804130617/https://ambonfunrun.com/ linkCode: https://www.instagram.com/p/CsBBHTardh9/ --- diff --git a/src/assets/Curriculum Vitae.pdf b/src/assets/Curriculum Vitae.pdf index a9f4eea..909ffca 100644 Binary files a/src/assets/Curriculum Vitae.pdf and b/src/assets/Curriculum Vitae.pdf differ diff --git a/src/assets/images/sunlight-landscape-forest-sunset-hill-nature-5912-wallhere.com.jpg b/src/assets/images/sunlight-landscape-forest-sunset-hill-nature-5912-wallhere.com.jpg new file mode 100644 index 0000000..ffd1a7b Binary files /dev/null and b/src/assets/images/sunlight-landscape-forest-sunset-hill-nature-5912-wallhere.com.jpg differ diff --git a/src/assets/js/blog.js b/src/assets/js/blog.js index 102fa2d..038836e 100644 --- a/src/assets/js/blog.js +++ b/src/assets/js/blog.js @@ -1,2 +1,271 @@ +const keyboardSymbols = { + CTRL: '⌃', + CONTROL: '⌃', + SHIFT: '⇧', + ALT: '⎇', + META: '⌘', + ENTER: '⏎', + ESC: '⎋', + TAB: '⇥', + BACKSPACE: '⌫', + DELETE: '⌦', + ARROWUP: '↑', + ARROWDOWN: '↓', + ARROWLEFT: '←', + ARROWRIGHT: '→', + PAGEUP: '⇞', + PAGEDOWN: '⇟', + HOME: '↖', + END: '↘', + SPACE: '␣' +} + +document.querySelectorAll('kbd').forEach(kbd => { + let gotSymbol = false + const originalText = kbd.innerHTML + const text = originalText.split(" ") + // console.log(text) + const newText = text.map(text => { + text = text.trim() + if (keyboardSymbols[text.toUpperCase()]) { + text = keyboardSymbols[text.toUpperCase()] + gotSymbol = true + } + return text + }) + if (!gotSymbol) return kbd.innerHTML = originalText + kbd.innerHTML = newText.join(" ") + kbd.setAttribute('aria-label', `Keyboard shortcut: ${originalText}`) +}) + +const getCodeType = pre => { + if (!pre.firstChild && !pre.firstChild.classList) return null + return Array.from(pre.firstChild.classList).find(cls => cls.startsWith('language-')) +} + +const createToolsContainer = (language, codeContent) => { + const toolsContainer = document.createElement('div') + const pre = codeContent?.parentElement + const parentOfPre = pre?.parentElement + let expandButton = null + let ariaLabel = pre?.getAttribute('aria-label') || 'Code block' + + const codeTypeDiv = createTypeDiv(language) + const copyButton = createCopyButton(codeContent) + if (parentOfPre.nodeName.toLowerCase() !== "blockquote") { + expandButton = createExpandButton(toolsContainer) + } + + toolsContainer.className = 'tools-container' + ariaLabel += `, ${copyButton.getAttribute('aria-label')}` + + toolsContainer.appendChild(codeTypeDiv) + toolsContainer.appendChild(copyButton) + if (expandButton) { + toolsContainer.appendChild(expandButton) + ariaLabel += `, ${expandButton.getAttribute('aria-label')}` + } + + pre.setAttribute('aria-label', ariaLabel) + return toolsContainer +} + +const createExpandablePre = (pre) => { + pre.className = 'expandable' + pre.setAttribute('tabindex', '0') + pre.setAttribute('role', 'region') + pre.addEventListener('keydown', (event) => { + const expandOption = pre.querySelector('#expand-option') + if (event.key === 'Enter') { + if (!expandOption) return + if (event.target.id === 'copy-option') return + event.currentTarget.classList.toggle('expanded') + pre.querySelector('#expand-option').firstChild.innerText = + pre.classList.contains('expanded') ? 'Collapse' : 'Expand' + } + + if (event.key === 'c' && event.ctrlKey) { + const copyButton = pre.querySelector('#copy-option') + if (copyButton) copyButton.click() + } + }) + return pre +} + +const createExpandButton = (container) => { + const expandButton = createInteractiveButton({ + text: 'Expand', + id: 'expand-option', + label: 'Press Enter to expand code block or collapse it', + defaultState: false, + }) + const shortcut = createKeyboardShortcut({ + keyCode: 'Enter', + }) + + expandButton.addEventListener('click', () => { + if (!container.parentElement.classList.contains('expanded')) { + container.parentElement.classList.add('expanded') + expandButton.firstChild.innerText = 'Collapse' + expandButton.setAttribute('aria-pressed', 'true') + } else { + container.parentElement.classList.remove('expanded') + expandButton.firstChild.innerText = 'Expand' + expandButton.setAttribute('aria-pressed', 'false') + } + }) + + expandButton.addEventListener('keydown', (event) => { + if (event.key === 'Enter') { + event.currentTarget.click() + } + }) + + expandButton.appendChild(shortcut) + + return expandButton +} + +const createTypeDiv = language => { + const typeDiv = document.createElement('div') + typeDiv.className = 'code-type' + typeDiv.innerText = language || 'Code' + return typeDiv +} + +const createCopyButton = codeContent => { + const shortcut = createKeyboardShortcut({ + keyCode: 'c', + keyModifier: 'Ctrl' + }) + const copyButton = createInteractiveButton({ + text: 'Copy', + id: 'copy-option', + label: 'Press Control + C to copy code to clipboard', + defaultState: false, + }) + + copyButton.addEventListener('click', () => { + navigator.clipboard.writeText(codeContent.textContent) + .then(() => { + copyButton.firstChild.innerText = 'Copied!' + setTimeout(() => { + copyButton.firstChild.innerText = 'Copy' + }, 2000) + }) + .catch(err => { + console.error('Failed to copy text: ', err) + copyButton.firstChild.innerText = 'Error' + }) + }) + copyButton.addEventListener('keydown', (event) => { + if (event.key === 'Enter') { + copyButton.click() + } + }) + + copyButton.appendChild(shortcut) + + return copyButton +} + +const createKeyboardShortcut = ({ keyCode, keyModifier }) => { + const shortcut = document.createElement('kbd') + const keyCodeElement = document.createElement('span') + let keyModifierElement + + switch (keyModifier) { + case 'Ctrl': + keyModifierElement = createKeyboardModifier({ + keyboardSymbol: getKeyboardCode("Ctrl"), + keyboardModifier: 'Control' + }) + break; + case 'Shift': + keyModifierElement = createKeyboardModifier({ + keyboardSymbol: getKeyboardCode("Shift"), + keyboardModifier: 'Shift' + }) + break; + } + + keyCodeElement.className = 'key-code' + keyCodeElement.innerText = getKeyboardCode(keyCode) + + shortcut.className = 'keyboard-shortcut' + if (keyModifier) shortcut.appendChild(keyModifierElement) + shortcut.appendChild(keyCodeElement) + return shortcut +} + +const getKeyboardCode = (text) => { + return keyboardSymbols[text.toUpperCase()] || text +} + +const createKeyboardModifier = ({ keyboardSymbol, keyboardModifier }) => { + const keyModifierElement = document.createElement('span') + const keyModifierText = document.createElement('span') + const keyModifierSymbol = document.createElement('span') + + keyModifierElement.className = 'key-modifier' + + keyModifierText.innerText = keyboardModifier ?? 'Control' + keyModifierText.className = 'sr-only' + + keyModifierSymbol.innerText = keyboardSymbol ?? '⌃' + keyModifierSymbol.ariaHidden = true + + keyModifierElement.appendChild(keyModifierText) + keyModifierElement.appendChild(keyModifierSymbol) + + return keyModifierElement +} + +const createInteractiveButton = ({ text, id, label, defaultState } = {}) => { + const button = document.createElement('button') + const buttonText = document.createElement('span') + buttonText.innerText = text || 'Button' + button.id = id || 'interactive-button' + button.setAttribute('aria-label', label || 'Interactive button') + button.setAttribute('aria-pressed', defaultState ? 'true' : 'false') + button.appendChild(buttonText) + return button +} + +const enhancePreElement = pre => { + const codeType = getCodeType(pre) + if (codeType) pre.setAttribute('data-language', codeType.replace('language-', '')) + + const codeContent = pre.querySelector('code') + const language = pre.getAttribute('data-language') + const toolsContainer = createToolsContainer(language, codeContent) + + createExpandablePre(pre) + pre.insertBefore(toolsContainer, pre.firstChild) +} + +const checkTableOfContents = () => { + const toc = document.getElementById("main").querySelector(".table-of-contents") + const skipToMain = document.querySelector('a[href="#main"]') + const navSkip = document.querySelector(".nav-skip") + if (!toc) return + + let nextCandidate = toc?.nextElementSibling + nextCandidate.id = "pass-toc" + + const skipLink = createSkipLink(nextCandidate.id) + navSkip.insertBefore(skipLink, skipToMain.nextSibling) +} + +const createSkipLink = (id, text = "Skip pass the Table of Contents") => { + const skipLink = document.createElement('a') + skipLink.className = 'nav-skipto' + skipLink.href = `#${id}` + skipLink.innerText = text + return skipLink +} + +document.querySelectorAll('pre').forEach(enhancePreElement) +checkTableOfContents() Array.from(document.links).filter(link => link.hostname != window.location.hostname) - .forEach(link => link.target = '_blank') \ No newline at end of file + .forEach(link => link.target = '_blank') diff --git a/src/assets/js/main.js b/src/assets/js/main.js index f891c8d..76e3801 100644 --- a/src/assets/js/main.js +++ b/src/assets/js/main.js @@ -1,224 +1,235 @@ -/** - * Fetching data from the said url - * @param {string} url - */ -const dataLoader = async (url) => { - const response = await fetch(url) - const data = await response.json() - if (response) { - schedulerLoader(data.schedules) - schemeLoader(data.colorScheme) - } -} - -dataLoader('/settings.json') - -const schemeLoader = (scheme) => { - localStorage.scheme = scheme -} - -/** - * Mobile hamburger menu logic - */ -const mobileButtons = document.querySelectorAll('[data-mobile-menu-button]') -const mobileMenuSection = document.querySelector('[data-mobile-menu-section]') -const mobileMenuLinks = document.querySelectorAll('body>ul>li') -const documentBody = document.body - -mobileButtons.forEach((button) => { - button.addEventListener('click', () => { - if (!mobileMenuSection.classList.contains('not-showing')) return setHidden() - return setShowing() - }) -}) - -const setShowing = () => { - mobileButtons[0].setAttribute('aria-label', 'Close the mobile menu') - mobileMenuSection.classList.remove('not-showing') - setTimeout(() => documentBody.classList.add('no-scroll'), 300) -} - -const setHidden = () => { - mobileButtons[0].setAttribute('aria-label', 'Open the mobile menu') - mobileMenuSection.classList.add('not-showing') - documentBody.classList.remove('no-scroll') -} - -/** - * Capitalize the word - * @param {string} string - */ - -const toCapitalizeWord = (string) => { - return string[0].toUpperCase() + string.substring(1) -} - -/** - * Changing the theme based on user toggle - */ -const themesButton = document.querySelector('[data-theme-button]') - -const themeHandler = () => { - if (document.documentElement.dataset.theme !== "dark") return setDark() - return setLight() -} - -themesButton?.addEventListener('click', themeHandler) - -const setLight = () => { - document.documentElement.dataset.theme = "light" - themesButton.setAttribute('aria-label', 'Switch to dark mode') - localStorage.theme = 'light' -} - -const setDark = () => { - document.documentElement.dataset.theme = "dark" - themesButton.setAttribute('aria-label', 'Switch to light mode') - localStorage.theme = 'dark' -} - -/** - * Printing details on console - */ - -console.log( - "%cEmptywork Console%c\nemptywork dot github dot io%c\n emptywork dot github dot io%c\nEnd of Console%c\n NOTE %c DON'T COPY ANY CODE INTO HERE", - 'background-color:#cecece; margin: 0.15em; padding: 0.1em; border-radius: 0.2em; color:transparent; font-size:1rem', - 'background-color:rebeccapurple; margin: 0.15em; padding: 0.1em; border-radius: 0.2em; color:transparent; font-size:1rem', - 'font-size:1.2rem', - 'background-color:#cecece; margin: 0.15em; padding: 0.1em; border-radius: 0.2em; color:transparent; font-size:1rem', - 'background-color:rebeccapurple; margin: 0.15em; padding: 0.1em; border-radius: 0.2em; font-size:1rem', - 'color: rebeccapurple; font-size: 1.1rem' -) - -/** - * Global Variables for Date related functions - */ -const days = [ - 'Sunday', - 'Monday', - 'Tuesday', - 'Wednesday', - 'Thursday', - 'Friday', - 'Saturday', -] - -const months = [ - 'January', - 'Febuary', - 'March', - 'April', - 'May', - 'June', - 'July', - 'August', - 'September', - 'October', - 'November', - 'December', -] - -const replaceStatus = document.querySelector('#activity') -const replaceMonth = document.querySelector('#month') -const replaceYear = document.querySelector('#year') - -const currentDate = new Date() -const currentDay = currentDate.getDay() -const currentMonth = currentDate.getMonth() -const currentYear = currentDate.getFullYear() - -if (replaceMonth) replaceMonth.textContent = months[currentMonth] -if (replaceYear) replaceYear.textContent = currentYear - -/** - * Updating activity status based on array of schedule - * @param {array} schedules - */ - -const schedulerLoader = (schedules = {}) => { - let time = new Date() - let currentTime = time.getUTCHours() + 9 - if (currentTime > 23) currentTime -= 23 - - if (replaceStatus) replaceStatus.textContent = 'Idle' - - setTimeout(() => { - for (let i = 0; i < schedules.length; i++) { - let { day, endHour, startHour } = schedules[i] - if (!replaceStatus) return - - if (days[currentDay] === toCapitalizeWord(day)) { - if (currentTime > startHour && currentTime < endHour) { - replaceStatus.textContent = 'Studying' - } - break - } - replaceStatus.textContent = 'Idle' - } - }, 0) -} - -// Injecting #main to main that doesn't have #main -const mainElement = - document.querySelector('#main') ?? document.querySelector('.main') -mainElement.id = 'main' - -/** - * Preload - */ - -const preloadElement = document.querySelector('.preload') -const delayElements = document.querySelectorAll('[data-delay]') - -delayElements?.forEach( - (el) => (el.style.transitionDelay = `${parseInt(el.dataset.delay) * 100}ms`) -) - -const observer = new IntersectionObserver((entires) => - entires.forEach((entry) => { - if (entry.isIntersecting) - return entry.target.classList.add('has-been-animated') - return entry.target.classList.remove('has-been-animated') - }) -) - -const toBeAnimateElements = document.querySelectorAll('[data-animation]') - -const removeElementAnimated = ({ timeout, targetElement }) => { - const timeoutDelay = timeout + timeout * 1.5 - setTimeout(() => targetElement?.classList.add('hidden'), timeout) - setTimeout(() => targetElement?.classList.add('disabled'), timeoutDelay) - setTimeout( - () => toBeAnimateElements?.forEach((el) => observer.observe(el)), - timeout - ) -} - -const loadedDocumentBody = () => { - if (!documentBody.classList.contains('not-loaded')) return - removeElementAnimated({ - targetElement: preloadElement, - timeout: 1000, - }) - documentBody.classList.remove('not-loaded') -} - -window.addEventListener('load', loadedDocumentBody, false) - -const makeExternalWarningForExternalLink = (parentElement) => { - const warningElement = document.createElement('span') - warningElement.classList.add('new-tab-warning') - warningElement.textContent = 'Open a new tab' - parentElement.appendChild(warningElement) -} - -const makeA11yExternalLink = (externalLink) => { - const linkWarning = externalLink.querySelector('.new-tab-warning') - if (linkWarning) return - return makeExternalWarningForExternalLink(externalLink) -} - -for (const externalLink of document.querySelectorAll('[target=_blank]')) { - makeA11yExternalLink(externalLink) -} +/** + * Fetching data from the said url + * @param {string} url + */ +const dataLoader = async (url) => { + const response = await fetch(url) + const data = await response.json() + if (response) { + schedulerLoader(data.schedules) + schemeLoader(data.colorScheme) + } +} + +dataLoader('/settings.json') + +const schemeLoader = (scheme) => { + localStorage.scheme = scheme +} + +/** + * Mobile hamburger menu logic + */ +const mobileButtons = document.querySelectorAll('[data-mobile-menu-button]') +const mobileMenuSection = document.querySelector('[data-mobile-menu-section]') +const mobileMenuLinks = document.querySelectorAll('body>ul>li') +const documentBody = document.body + +mobileButtons.forEach((button) => { + button.addEventListener('click', () => { + if (!mobileMenuSection.classList.contains('not-showing')) return setHidden() + return setShowing() + }) +}) + +const setShowing = () => { + mobileButtons[0].setAttribute('aria-label', 'Close the mobile menu') + mobileButtons[0].setAttribute('aria-expanded', 'true') + mobileMenuSection.setAttribute('aria-hidden', 'false') + mobileMenuSection.classList.remove('not-showing') + setTimeout(() => documentBody.classList.add('no-scroll'), 300) +} + +const setHidden = () => { + mobileButtons[0].setAttribute('aria-label', 'Open the mobile menu') + mobileButtons[0].setAttribute('aria-expanded', 'false') + mobileMenuSection.setAttribute('aria-hidden', 'true') + mobileMenuSection.classList.add('not-showing') + documentBody.classList.remove('no-scroll') +} + +/** + * Capitalize the word + * @param {string} string + */ + +const toCapitalizeWord = (string) => { + return string[0].toUpperCase() + string.substring(1) +} + +/** + * Changing the theme based on user toggle + */ +const themesButton = document.querySelector('[data-theme-button]') + +const themeHandler = () => { + if (document.documentElement.dataset.theme !== "dark") return setDark() + return setLight() +} + +themesButton?.addEventListener('click', themeHandler) + +const setLight = () => { + document.documentElement.dataset.theme = "light" + themesButton.setAttribute('aria-label', 'Switch to dark mode') + localStorage.theme = 'light' +} + +const setDark = () => { + document.documentElement.dataset.theme = "dark" + themesButton.setAttribute('aria-label', 'Switch to light mode') + localStorage.theme = 'dark' +} + +/** + * Printing details on console + */ + +console.log( + "%cEmptywork Console%c\nemptywork dot github dot io%c\n emptywork dot github dot io%c\nEnd of Console%c\n NOTE %c DON'T COPY ANY CODE INTO HERE", + 'background-color:#cecece; margin: 0.15em; padding: 0.1em; border-radius: 0.2em; color:transparent; font-size:1rem', + 'background-color:rebeccapurple; margin: 0.15em; padding: 0.1em; border-radius: 0.2em; color:transparent; font-size:1rem', + 'font-size:1.2rem', + 'background-color:#cecece; margin: 0.15em; padding: 0.1em; border-radius: 0.2em; color:transparent; font-size:1rem', + 'background-color:rebeccapurple; margin: 0.15em; padding: 0.1em; border-radius: 0.2em; font-size:1rem', + 'color: rebeccapurple; font-size: 1.1rem' +) + +/** + * Global Variables for Date related functions + */ +const days = [ + 'Sunday', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', +] + +const months = [ + 'January', + 'Febuary', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', +] + +const replaceStatus = document.querySelector('#activity') +const replaceMonth = document.querySelector('#month') +const replaceYear = document.querySelector('#year') + +const currentDate = new Date() +const currentDay = currentDate.getDay() +const currentMonth = currentDate.getMonth() +const currentYear = currentDate.getFullYear() + +if (replaceMonth) replaceMonth.textContent = months[currentMonth] +if (replaceYear) replaceYear.textContent = currentYear + +/** + * Updating activity status based on array of schedule + * @param {array} schedules + */ + +const schedulerLoader = (schedules = {}) => { + let time = new Date() + let currentTime = time.getUTCHours() + 9 + if (currentTime > 23) currentTime -= 23 + + if (replaceStatus) replaceStatus.textContent = 'Idle' + + setTimeout(() => { + schedules.forEach(schedule => { + let { day, endHour, startHour } = schedule + if (!replaceStatus) return + if (days[currentDay] === toCapitalizeWord(day)) { + if (currentTime > startHour && currentTime < endHour) { + replaceStatus.textContent = 'Studying' + } + return + } + replaceStatus.textContent = 'Idle' + }) + }, 0) +} + +// Injecting #main to main that doesn't have #main +const mainElement = + document.querySelector('#main') ?? document.querySelector('.main') +mainElement.id = 'main' + +/** + * Preload + */ + +const preloadElement = document.querySelector('.preload') +const delayElements = document.querySelectorAll('[data-delay]') + +delayElements?.forEach( + (el) => { + let delayModifier = 0 + const parentElement = el.parentElement + const getAmountOfDelayElems = parentElement.querySelectorAll('[data-delay]').length + if (!parentElement.hasAttribute("data-delay-parent")) parentElement.setAttribute('data-delay-parent', true) + delayModifier = getAmountOfDelayElems + + el.setAttribute("data-transition-delay", `${parseInt(el.dataset.delay) * 50 * (delayModifier / 2)}ms`) + } +) + +const observer = new IntersectionObserver((entires) => + entires.forEach((entry) => { + if (entry.isIntersecting) + return entry.target.setAttribute('data-has-been-animated', true) + return entry.target.removeAttribute('data-has-been-animated') + }) +) + +const toBeAnimateElements = document.querySelectorAll('[data-animation]') + +const removeElementAnimated = ({ timeout, targetElement }) => { + const timeoutDelay = timeout + timeout * 1.5 + setTimeout(() => targetElement?.classList.add('hidden'), timeout) + setTimeout(() => targetElement?.classList.add('disabled'), timeoutDelay) + setTimeout( + () => toBeAnimateElements?.forEach((el) => observer.observe(el)), + timeout + ) +} + +const loadedDocumentBody = () => { + if (!documentBody.classList.contains('not-loaded')) return + removeElementAnimated({ + targetElement: preloadElement, + timeout: 1000, + }) + documentBody.classList.remove('not-loaded') +} + +window.addEventListener('load', loadedDocumentBody, false) + +const makeExternalWarningForExternalLink = (parentElement) => { + const warningElement = document.createElement('span') + warningElement.classList.add('new-tab-warning') + warningElement.textContent = 'Open a new tab' + parentElement.appendChild(warningElement) +} + +const makeA11yExternalLink = (externalLink) => { + const linkWarning = externalLink.querySelector('.new-tab-warning') + if (linkWarning) return + return makeExternalWarningForExternalLink(externalLink) +} + +for (const externalLink of document.querySelectorAll('[target=_blank]')) { + makeA11yExternalLink(externalLink) +} diff --git a/src/assets/scss/abstracts/_typography.scss b/src/assets/scss/abstracts/_typography.scss new file mode 100644 index 0000000..3133e47 --- /dev/null +++ b/src/assets/scss/abstracts/_typography.scss @@ -0,0 +1,7 @@ +.font-montserrat { + font-family: 'Montserrat', sans-serif; +} + +.font-monospace { + font-family: monospace, "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", "Lucida Sans", Arial, sans-serif; +} \ No newline at end of file diff --git a/src/assets/scss/abstracts/_vars.scss b/src/assets/scss/abstracts/_vars.scss index 9c240f6..19740bb 100644 --- a/src/assets/scss/abstracts/_vars.scss +++ b/src/assets/scss/abstracts/_vars.scss @@ -29,7 +29,7 @@ html:root { --bx-shadow-two-side: 0 0.3125rem 0.625rem hsla(var(--_hs) 0%, 0.2), -0.3125rem -0.3125rem 0.625rem hsla(var(--_hs) 100%, 0.8); - --table-header: hsl(var(--_hs) 30%); + --table-header: var(--clr-accent-light); &[data-color-scheme=sunglow] { --_h: 34; @@ -53,6 +53,7 @@ html:root { --_h: 140; --_s: 10%; --clr-complementary: hsl(20, 50%, 40%); + --clr-href: hsl(var(--_h), 40%, 50%); } &[data-theme=dark] { @@ -69,6 +70,5 @@ html:root { --bx-shadow-two-side: 0 0.3125rem 0.625rem rgb(0 0 0 / 20%), -0.3125rem -0.3125rem 0.625rem rgb(58 58 58 / 63%); - --table-header: var(--clr-accent-light); } } \ No newline at end of file diff --git a/src/assets/scss/base/_global.scss b/src/assets/scss/base/_global.scss index 4fce297..d5abc99 100644 --- a/src/assets/scss/base/_global.scss +++ b/src/assets/scss/base/_global.scss @@ -1,4 +1,5 @@ @use "../abstracts/_mixin" as mix; +@use "../abstracts/typography"; html { scroll-behavior: smooth; @@ -44,11 +45,12 @@ html:root { } body { + @extend .font-montserrat; + display: flex; flex-direction: column; min-height: 100vh; background-color: var(--clr-background-900); - font-family: 'Montserrat', sans-serif; color: var(--clr-foreground); line-height: 1.6; overflow-x: hidden; @@ -155,7 +157,7 @@ li { .sr-only { border: 0; - clip: rect(0 0 0 0); + clip-path: rect(0 0 0 0); height: 0.0625rem; margin: -0.0625rem; overflow: hidden; diff --git a/src/assets/scss/components/_animation.scss b/src/assets/scss/components/_animation.scss index 5b19ab2..78ac97f 100644 --- a/src/assets/scss/components/_animation.scss +++ b/src/assets/scss/components/_animation.scss @@ -1,6 +1,7 @@ [data-animation] { opacity: 0; transition: translate var(--_translate, 1s) ease-in-out, scale var(--_scale, 1s) ease-in-out, rotate var(--_rotate, 1s) ease-in-out, opacity var(--_opacity, 1s) ease-in-out; + transition-delay: attr(data-transition-delay, 0ms); } [data-animation='fade-in'] { @@ -31,7 +32,7 @@ } } -.has-been-animated { +.has-been-animated, [data-has-been-animated] { opacity: unset; scale: unset; rotate: unset; diff --git a/src/assets/scss/components/_card.scss b/src/assets/scss/components/_card.scss index e7ab333..ad70b05 100644 --- a/src/assets/scss/components/_card.scss +++ b/src/assets/scss/components/_card.scss @@ -38,6 +38,13 @@ border-radius: 0.3rem; overflow: hidden; + picture { + height: 100%; + object-fit: cover; + object-position: center; + border-radius: 0.3rem; + } + &::after { content: ''; position: absolute; diff --git a/src/assets/scss/components/_featured.scss b/src/assets/scss/components/_featured.scss index 34b5b78..52b4038 100644 --- a/src/assets/scss/components/_featured.scss +++ b/src/assets/scss/components/_featured.scss @@ -37,6 +37,7 @@ background-color: var(--clr-variant); flex: 1 auto; position: relative; + width: fit-content; justify-content: center; align-items: center; @@ -44,11 +45,18 @@ min-height: 10em; } - img { - all: unset; + picture { overflow: clip !important; width: min(100%, 26.1875rem); aspect-ratio: 1/1; + + img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; + border-radius: 0.3em; + } } #svg { diff --git a/src/assets/scss/components/_hero.scss b/src/assets/scss/components/_hero.scss index d124865..dcea8b1 100644 --- a/src/assets/scss/components/_hero.scss +++ b/src/assets/scss/components/_hero.scss @@ -17,32 +17,32 @@ } &-decoration { - --width: 95vmin; - --increase: 1.2; - --duration: 12s; - --delay: calc((var(--duration) * 0.1)); + --_width: 95vmin; + --_increase: 1.2; + --_duration: 12s; + --_delay: calc((var(--_duration) * 0.1)); position: absolute; - right: calc(var(--width) / -2); - bottom: calc(var(--width) / -2); + right: calc(var(--_width) / -2); + bottom: calc(var(--_width) / -2); z-index: -1; - width: var(--width); + width: var(--_width); aspect-ratio: 1/1; border-radius: 50%; background-color: var(--clr-accent); - animation: twiching var(--duration) var(--delay) infinite; + animation: twiching var(--_duration) var(--_delay) infinite; } @keyframes twiching { 50% { - width: calc(var(--width) * var(--increase)); + width: calc(var(--_width) * var(--_increase)); } } &-bigger { - --width: 120vmin; - --increase: 2; - --duration: 10s; - --delay: calc((var(--duration) * 0.2)); + --_width: 120vmin; + --_increase: 2; + --_duration: 10s; + --_delay: calc((var(--_duration) * 0.2)); background-color: transparent; border: max(0.5vmin, 0.125rem) solid var(--clr-accent); } diff --git a/src/assets/scss/components/_nav.scss b/src/assets/scss/components/_nav.scss index 5d3b4ae..73cf2e6 100644 --- a/src/assets/scss/components/_nav.scss +++ b/src/assets/scss/components/_nav.scss @@ -6,10 +6,22 @@ flex-shrink: 0; position: sticky; top: 0; - padding: 0.8rem; z-index: 20; + &-skip { + position: relative; + width: 100%; + height: 0; + transition: height 200ms ease; + justify-content: center; + + &:has(:focus) { + height: calc(1em + 0.6em); + } + } + &-container { + padding: 0.8rem; width: 100%; max-width: 64rem; margin: 0 auto; @@ -29,17 +41,22 @@ &-skipto { background-color: var(--clr-accent); color: var(--clr-white); - display: flex; padding: 0 0.3rem; - align-self: center; - scale: 0 1; - order: 2; - transform-origin: left; - transition: scale 200ms ease; + position: absolute !important; + left: 0; + right: 0; + top: 0; + width: 100%; + height: max-content; + text-align: center; + opacity: 0; + transition: opacity 200ms ease; + pointer-events: none; &:focus { + pointer-events: unset; + opacity: 1; color: var(--clr-white); - scale: 1; } } diff --git a/src/assets/scss/components/_note.scss b/src/assets/scss/components/_note.scss index 7400033..0ad4982 100644 --- a/src/assets/scss/components/_note.scss +++ b/src/assets/scss/components/_note.scss @@ -1,4 +1,3 @@ - .note { color: var(--clr-white); display: flex; diff --git a/src/assets/scss/components/_partof.scss b/src/assets/scss/components/_partof.scss index 0946bb6..4e559d1 100644 --- a/src/assets/scss/components/_partof.scss +++ b/src/assets/scss/components/_partof.scss @@ -34,7 +34,7 @@ padding: 3em 2em 2em 2em; width: 100%; - h2 { + .header-title { text-transform: uppercase; letter-spacing: 0.1em; @@ -112,11 +112,9 @@ border: none; color: var(--clr-white); background: var(--clr-accent-dark); - background-image: linear-gradient( - -90deg, - var(--clr-accent), - var(--clr-accent-dark) - ); + background-image: linear-gradient(-90deg, + var(--clr-accent), + var(--clr-accent-dark)); background-size: 400%; animation: positionanimate 1s infinite alternate; } @@ -164,7 +162,8 @@ 0% { background-position: left; } + 100% { background-position: right; } -} +} \ No newline at end of file diff --git a/src/assets/scss/components/_post.scss b/src/assets/scss/components/_post.scss index ca5fac1..563c2ae 100644 --- a/src/assets/scss/components/_post.scss +++ b/src/assets/scss/components/_post.scss @@ -34,6 +34,7 @@ color: var(--clr-white); margin-bottom: 1em; width: fit-content; + height: fit-content; flex-wrap: wrap; gap: .5em; display: flex; diff --git a/src/assets/scss/components/_preload.scss b/src/assets/scss/components/_preload.scss index 485b280..ecbbc40 100644 --- a/src/assets/scss/components/_preload.scss +++ b/src/assets/scss/components/_preload.scss @@ -17,7 +17,8 @@ svg { rotate: 0deg; - max-width: 10em; + width: min(10em, 100%); + height: min(10em, 100%); animation: preload 2s infinite; } @@ -52,4 +53,4 @@ 100% { rotate: 0deg; } -} +} \ No newline at end of file diff --git a/src/assets/scss/components/_prototype.scss b/src/assets/scss/components/_prototype.scss index 43da82b..1d2907a 100644 --- a/src/assets/scss/components/_prototype.scss +++ b/src/assets/scss/components/_prototype.scss @@ -156,7 +156,7 @@ background-color: var(--_item-color); background-image: linear-gradient(90deg, var(--_item-color), var(--clr-accent) var(--_range)); border-radius: 0.3rem; - + &:is(.error, .active) { --_range: 400%; color: var(--clr-white); @@ -165,18 +165,16 @@ &.error { --_item-color: hsl(0, 70%, 43%); } - + &.active { --_item-color: hsl(120, 70%, 26%); } } .status { - background-image: linear-gradient( - -90deg, - var(--clr-accent), - var(--clr-accent-dark) - ); + background-image: linear-gradient(-90deg, + var(--clr-accent), + var(--clr-accent-dark)); padding: 0 0.7rem; display: flex; align-items: center; @@ -224,14 +222,18 @@ scrollbar-width: none; position: relative; - &::-webkit-scrollbar { + &::-webkit-scrollbar, + &::-webkit-scrollbar:vertical { width: 0; } } pre { overflow: scroll; - &::-webkit-scrollbar { + scrollbar-width: none; + + &::-webkit-scrollbar, + &::-webkit-scrollbar:vertical { width: 0; } } @@ -264,4 +266,4 @@ color: var(--clr-background); } } -} +} \ No newline at end of file diff --git a/src/assets/scss/components/_title.scss b/src/assets/scss/components/_title.scss index 193bfd2..5173f5d 100644 --- a/src/assets/scss/components/_title.scss +++ b/src/assets/scss/components/_title.scss @@ -30,13 +30,13 @@ } &:hover { - .note.has-been-animated { + .note:is([data-has-been-animated], .has-been-animated) { rotate: 0deg; scale: 1.1; } } - .note.has-been-animated { + .note:is([data-has-been-animated], .has-been-animated) { rotate: 3deg; --_scale: 300ms; --_rotate: 700ms; diff --git a/src/assets/scss/highlight.scss b/src/assets/scss/highlight.scss new file mode 100644 index 0000000..b60866d --- /dev/null +++ b/src/assets/scss/highlight.scss @@ -0,0 +1,140 @@ +pre code.hljs { + display: block; + overflow-x: auto; + padding: 1em +} + +code.hljs { + padding: 3px 5px +} + +/*! + Theme: Tokyo-night-Dark + origin: https://github.com/enkia/tokyo-night-vscode-theme + Description: Original highlight.js style + Author: (c) Henri Vandersleyen + License: see project LICENSE + Touched: 2022 +*/ +.hljs-comment, +.hljs-meta { + color: #565f89 +} + +.hljs-deletion, +.hljs-doctag, +.hljs-regexp, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-selector-id, +.hljs-selector-pseudo, +.hljs-tag, +.hljs-template-tag, +.hljs-variable.language_ { + color: #f7768e +} + +.hljs-link, +.hljs-literal, +.hljs-number, +.hljs-params, +.hljs-template-variable, +.hljs-type, +.hljs-variable { + color: #ff9e64 +} + +.hljs-attribute, +.hljs-built_in { + color: #e0af68 +} + +.hljs-keyword, +.hljs-property, +.hljs-subst, +.hljs-title, +.hljs-title.class_, +.hljs-title.class_.inherited__, +.hljs-title.function_ { + color: #7dcfff +} + +.hljs-selector-tag { + color: #73daca +} + +.hljs-addition, +.hljs-bullet, +.hljs-quote, +.hljs-string, +.hljs-symbol { + color: #9ece6a +} + +.hljs-code, +.hljs-formula, +.hljs-section { + color: #7aa2f7 +} + +.hljs-attr, +.hljs-char.escape_, +.hljs-keyword, +.hljs-name, +.hljs-operator { + color: #bb9af7 +} + +.hljs-punctuation { + color: #c0caf5 +} + +.hljs { + background: #1a1b26; + color: #9aa5ce +} + +.hljs-emphasis { + font-style: italic +} + +.hljs-strong { + font-weight: 700 +} + +/*! + Author: (c) EmptyWork + Touched: 2025 + + Additional styling for diff support +*/ + +.hljs-line, +.hljs-addition, +.hljs-deletion { + --_distance: 1em; + --_border: 0em; + --_offset: 0.61875em; + display: inline-block; + width: calc(100% + calc(var(--_distance) * 2)); + margin-inline: calc(var(--_distance) * -1); + padding-inline: 1em; + padding-left: calc(0.5em - var(--_border) + var(--_offset)); +} + +.hljs-addition, +.hljs-deletion { + --_border: 0.25em; + --_offset: 0em; + --_color: 349, 89%, 72%; + background-color: hsl(var(--_color), 0.2); + border-left: var(--_border) solid hsl(var(--_color), 0.2); +} + +.hljs-addition { + --_color: 89, 51%, 61%; +} + +.hljs-deletion { + --_color: 349, 89%, 72%; +} \ No newline at end of file diff --git a/src/assets/scss/layouts/_footer.scss b/src/assets/scss/layouts/_footer.scss index d5107a1..c5df4bd 100644 --- a/src/assets/scss/layouts/_footer.scss +++ b/src/assets/scss/layouts/_footer.scss @@ -69,4 +69,4 @@ flex-direction: row; } } -} +} \ No newline at end of file diff --git a/src/assets/scss/layouts/_header.scss b/src/assets/scss/layouts/_header.scss index d86013c..999e15b 100644 --- a/src/assets/scss/layouts/_header.scss +++ b/src/assets/scss/layouts/_header.scss @@ -18,4 +18,4 @@ height: auto; } } -} +} \ No newline at end of file diff --git a/src/assets/scss/pages/_article.scss b/src/assets/scss/pages/_article.scss index 4f8f154..c924c57 100644 --- a/src/assets/scss/pages/_article.scss +++ b/src/assets/scss/pages/_article.scss @@ -1,7 +1,12 @@ @use "../abstracts/mixin" as mix; +@use "../abstracts/typography"; .article { --_l: 20%; + --_bh: calc(var(--_h) - 8); + --_background: hsla(var(--_h), 20%, 20%, 0.5); + --_blockquote-color: hsla(var(--_h), 90%, 20%, 0.8); + --_drop-shadow-color: hsla(var(--_bh), 71%, 22%, 40%); --_article-color: hsla(var(--_hs), var(--_l), 0.5); -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; @@ -11,6 +16,7 @@ margin: auto; main { + margin-top: 1em; position: relative; .table-of-contents { @@ -48,6 +54,10 @@ } } + pre+pre { + margin-top: 0.5em; + } + span { a:hover, @@ -149,10 +159,63 @@ } blockquote { - color: var(--clr-foreground-900); + padding: .5em; + background-color: var(--_blockquote-color); + color: var(--clr-white); margin: 0; padding-left: 2em; border-left: 0.5em var(--clr-accent) solid; + border-radius: 0.5em; + box-shadow: 0px 3px 10px 0px var(--_drop-shadow-color); + + .tools-container { + kbd { + color: var(--clr-white); + background-color: var(--clr-accent); + } + + #expand-option { + display: none; + } + } + + pre, + code, + kbd, + samp { + display: inline-block; + align-items: center; + background-color: hsl(var(--_hs) 10%); + color: var(--clr-white); + padding: 0.2em 0.4em; + border-radius: 0.3em; + } + + pre { + @include mix.breakpoint-down("small") { + &::after { + content: ""; + position: absolute; + right: 0; + top: 0; + bottom: 0; + background: linear-gradient(90deg, transparent, hsl(var(--_hs) 6%)); + width: 2em; + } + } + } + + a { + --_normal-color: var(--clr-accent-light); + --_hover-color: var(--clr-white); + color: var(--_normal-color); + text-decoration-color: var(--_normal-color); + + &:hover { + color: var(--_hover-color); + text-decoration-color: var(--_hover-color); + } + } } hr { @@ -169,24 +232,117 @@ code, kbd, samp { - padding: 0 0.3em; - font-family: monospace, "Trebuchet MS", "Lucida Sans Unicode", "Lucida Grande", "Lucida Sans", Arial, sans-serif; - // font-size: 0.8rem; + @extend .font-monospace; + position: relative; line-height: 1.4; } + kbd { + display: inline-block; + align-items: center; + width: max-content; + font-size: 0.8rem; + color: var(--clr-white); + background-color: var(--clr-accent); + padding: 0.2em 0.4em; + border-radius: 0.3em; + } + pre { - border: 0.1rem solid var(--_article-color); - background-color: hsl(0, 3%, 12%); + border: 0.1rem solid var(--_background); + background-color: hsl(var(--_hs) 10%); border-radius: 0.5rem; padding: 1rem; white-space: pre; white-space: pre-wrap; word-wrap: break-word; + overflow: hidden; + width: min(80ch, 100%); + transition: width 0.3s ease-in-out; + + &.expanded { + width: 100%; + } + + .tools-container { + display: flex; + position: absolute; + z-index: 10; + padding-left: 0.5em; + opacity: 0; + top: 0; + backdrop-filter: blur(0.125rem); + right: 0; + margin-top: -2rem; + flex-direction: row; + align-items: center; + background-color: var(--_background); + color: var(--clr-white); + transition: margin-top 0.2s ease, opacity 0.3s ease; + + .code-type { + margin-right: 0.5em; + } + + .keyboard-shortcut { + display: flex; + gap: 0.2em; + } + + @include mix.breakpoint-down('medium') { + #expand-option { + display: none; + } + } + + button { + gap: 0.5em; + align-items: center; + display: flex; + padding: 0.5em; + background: transparent; + border: none; + cursor: pointer; + background-color: var(--_background); + color: var(--clr-accent-light); + + &:is(:hover, :focus, :active) { + color: var(--clr-accent); + } + } + } + + &:hover { + outline: 0.125rem solid var(--clr-accent-dark); + } + + &:hover, + &:focus, + &:has(:hover, :focus) { + .tools-container { + margin-top: 0; + opacity: 1; + + @starting-style { + opacity: 0; + margin-top: -2rem; + } + } + } code { + color: var(--clr-white); box-shadow: none; - background-color: transparent; + width: 100%; + + .hljs-comment, + .hljs-meta { + color: hsl(230, 100%, 89%, 0.9) + } + + @include mix.breakpoint-down('small') { + overflow-x: auto; + } } } @@ -267,7 +423,8 @@ overflow-x: scroll; border-spacing: 0; width: 100%; - background-color: var(--clr-background); + background-color: hsl(var(--_hs) 10%); + color: var(--clr-white); border-radius: 0.5rem; overflow: hidden; diff --git a/src/blog.njk b/src/blog.njk index 32d9c29..067a2ef 100644 --- a/src/blog.njk +++ b/src/blog.njk @@ -1,150 +1,149 @@ ---- -title : 'Recent Articles - Blog | EmptyWork' -description : "List of all articles that was written by me, its mostly contain topics about Accessibility, PHP, HTML, CSS and Fullstack development" -layout: 'layouts/secondary.njk' -style: 'blog.css' ---- - -{% set posts = [] %} -{% set recentPosts = [] %} -{% for post in collections.post | reverse %} - {% if not post.data.draft %} - {% set postDate = { - year: post.data.date | postYear, - fullDate: post.data.date - } %} - {% set _ = posts.push({ - title: post.data.title, - url: post.url, - date: postDate, - desc: post.data.description, - author: post.data.author, - slug: post.fileSlug - }) %} - {% set _ = recentPosts.push({ - title: post.data.title, - description: post.data.description, - url: post.url, - slug: post.fileSlug, - author: post.data.author, - date: post.data.date, - image: post.data.image - }) %} - {% endif %} - {% if isDevelopment and post.data.draft %} - {% set postDate = { - year: post.data.date | postYear, - fullDate: post.data.date - } %} - {% set _ = posts.push({ - title: post.data.title, - url: post.url, - date: postDate, - desc: post.data.description, - author: post.data.author, - slug: post.fileSlug - }) %} - {% set _ = recentPosts.push({ - title: post.data.title, - description: post.data.description, - url: post.url, - slug: post.fileSlug, - author: post.data.author, - date: post.data.date, - image: post.data.image - }) %} - {% endif %} -{% endfor %} -{% if recentPosts | length > 3 %} - {% set recentPosts = recentPosts | sliceRecent %} -{% endif %} -
    -
    -
    -
    - - Home › - Blog - - -
    -
    -
      - {%- for post in recentPosts -%} - {% include "components/snippets/article.njk" %} - {% else %} -
    • -

      You haven't post anything yet.

      - It seems like there is no article right now... -
    • - {%- endfor -%} -
    -
    - {% set groupedPosts = posts | groupby("date.year") %} - {% for year, posts in groupedPosts | reverseGroupedPosts %} -
    -

    {{ year }}

    - -
    - {% endfor %} -
    -
    -
    - +--- +title : 'Recent Articles - Blog | EmptyWork' +description : "List of all articles that was written by me, its mostly contain topics about Accessibility, PHP, HTML, CSS and Fullstack development" +layout: 'layouts/secondary.njk' +style: 'blog.css' +--- + +{% set posts = [] %} +{% set recentPosts = [] %} +{% for post in collections.post | reverse %} + {% if not post.data.draft %} + {% set postDate = { + year: post.data.date | postYear, + fullDate: post.data.date + } %} + {% set _ = posts.push({ + title: post.data.title, + url: post.url, + date: postDate, + desc: post.data.description, + author: post.data.author, + slug: post.fileSlug + }) %} + {% set _ = recentPosts.push({ + title: post.data.title, + description: post.data.description, + url: post.url, + slug: post.fileSlug, + author: post.data.author, + date: post.data.date, + image: post.data.image + }) %} + {% endif %} + {% if isDevelopment and post.data.draft %} + {% set postDate = { + year: post.data.date | postYear, + fullDate: post.data.date + } %} + {% set _ = posts.push({ + title: post.data.title, + url: post.url, + date: postDate, + desc: post.data.description, + author: post.data.author, + slug: post.fileSlug + }) %} + {% set _ = recentPosts.push({ + title: post.data.title, + description: post.data.description, + url: post.url, + slug: post.fileSlug, + author: post.data.author, + date: post.data.date, + image: post.data.image + }) %} + {% endif %} +{% endfor %} +{% if recentPosts | length > 3 %} + {% set recentPosts = recentPosts | sliceRecent %} +{% endif %} +
    +
    +
    +
    + + Home › + Blog + + +
    +
    +
      + {%- for post in recentPosts -%} + {% include "components/snippets/post-card.njk" %} + {% else %} +
    • +

      You haven't post anything yet.

      + It seems like there is no article right now... +
    • + {%- endfor -%} +
    +
    + {% set groupedPosts = posts | groupby("date.year") %} + {% for year, posts in groupedPosts | reverseGroupedPosts %} +
    +

    {{ year }}

    +
      + {% for post in posts %} +
    • +

      + + + + + + {{ post.author }} + +

      +

      + + + {{ post.title }} + +

      +

      {{ post.desc | truncate(60) }}

      +
    • + {% endfor %} +
    +
    + {% endfor %} +
    +
    +
    + diff --git a/src/contact.njk b/src/contact.njk index a7e6d6f..1bf2b3d 100644 --- a/src/contact.njk +++ b/src/contact.njk @@ -23,18 +23,18 @@ style: 'contact.css'

    {{ contact.summary | renderMarkdownInline | safe }}

    - - + {% for socials, containerClass in [ + [contact.mainSocials, "social-container"], + [contact.socials, "social-container social-container__small"] + ] %} +
      + {% for social in socials %} + {% if containerClass == "social-container" or social.hidden !== true %} +
    • {% include "components/snippets/social-items.njk" %}
    • + {% endif %} + {% endfor %} +
    + {% endfor %}
    diff --git a/src/error.njk b/src/error.njk index 67daf23..cfb432e 100644 --- a/src/error.njk +++ b/src/error.njk @@ -31,15 +31,10 @@ style: 'error.css'

    - 404 — The page that you - are looking for - does not exist + 404 — The page that you are looking for does not exist

    - Please make sure that the URL you - typed are - correct, if you think that the - URL is correct but you still cannot seems to access it please contact me via email. + Please make sure that the URL you typed is correct, if you think that the URL is correct but you still cannot seems to access it please contact me via email.

      diff --git a/src/feed.njk b/src/feed.njk index a6e984c..2df4a29 100644 --- a/src/feed.njk +++ b/src/feed.njk @@ -14,28 +14,33 @@ } --- + - {{ metadata.title }} - {{ metadata.subtitle }} - - - {{ collections.post | getNewestCollectionItemDate | dateToRfc3339 }} - {{ link.website }} - - {{ metadata.author.name }} - {{ metadata.author.email }} - - {%- for post in collections.post | reverse %} - {%- set absolutePostUrl = post.url | absoluteUrl(link.website) %} - +{{ metadata.title }} +{{ metadata.subtitle }} + + +{{ collections.post | getNewestCollectionItemDate | dateToRfc3339 }} +{{ link.website }} + +{{ metadata.author.name }} +{{ metadata.author.email }} + +{%- for post in collections.post | reverse %} + {% if not post.data.draft %} + {%- set absolutePostUrl = post.url | absoluteUrl(link.website) %} + {{ post.data.title }} - + {{ post.date | dateToRfc3339 }} {{ absolutePostUrl }} - {%- set templateContent = post.templateContent | htmlToAbsoluteUrls(absolutePostUrl) %} - {{ templateContent | safe }} + {%- set templateContent = post.templateContent | htmlToAbsoluteUrls(absolutePostUrl) %} + {{ templateContent }} - - {%- endfor %} - \ No newline at end of file + + {% endif %} +{%- endfor %} + diff --git a/src/index.njk b/src/index.njk index 946f8e5..ed6f7a4 100644 --- a/src/index.njk +++ b/src/index.njk @@ -2,11 +2,12 @@ title: 'EmptyWork' layout: 'layouts/primary.njk' --- +
      - {% include 'sections/featured.njk' %} - {% include 'sections/work.njk' %} - {% include 'sections/clients.njk' %} - {% include 'sections/partof.njk' %} - {% include 'sections/about.njk' %} - {% include 'sections/prototypes.njk' %} -
      \ No newline at end of file + {% include "sections/featured.njk" %} + {% include "sections/work.njk" %} + {% include "sections/clients.njk" %} + {% include "sections/partof.njk" %} + {% include "sections/about.njk" %} + {% include "sections/prototypes.njk" %} + diff --git a/src/pretty-atom-feed.xsl b/src/pretty-atom-feed.xsl new file mode 100644 index 0000000..6331549 --- /dev/null +++ b/src/pretty-atom-feed.xsl @@ -0,0 +1,89 @@ + + + + + + + + <xsl:value-of select="atom:feed/atom:title"/> + + + + + + +
      +
      +

      + + + + + + + + + + + + + + + + + + + Web Feed Preview +

      +

      +

      +

      This preview only shows titles, but the actual feed contains the full content.

      + + + + + Visit Website → + +
      +

      Recent Items

      + +
      + + +
      + +
      +

      + + + + + + +

      + + Published: + +
      +
      +
      \ No newline at end of file diff --git a/src/sitemap.njk b/src/sitemap.njk index 63e67a4..3d9ec5b 100644 --- a/src/sitemap.njk +++ b/src/sitemap.njk @@ -3,14 +3,15 @@ permalink: /sitemap.xml eleventyExcludeFromCollections: true --- + - {% for page in collections.all %} +{% for page in collections.all %} {% if not page.data.draft %} - + {{ link.website }}{{ page.url | url }} {{ page.date.toISOString() }} {{ page.data.changeFreq if page.data.changeFreq else "monthly" }} - + {% endif %} - {% endfor %} - \ No newline at end of file +{% endfor %} + diff --git a/src/sitemap.xsl b/src/sitemap.xsl new file mode 100644 index 0000000..d221962 --- /dev/null +++ b/src/sitemap.xsl @@ -0,0 +1,42 @@ + + + + +Powered by Sitemap Style + + + + + + + + + + Sitemap for <xsl:value-of select="$hostname"/> + + + +
      +

      Pages on

      +
        + + + +
      • + + + () + +
      • +
        +
      +

      pages

      +
      + + +
      +
      \ No newline at end of file diff --git a/tests/landing-page.spec.ts b/tests/landing-page.spec.ts new file mode 100644 index 0000000..8b7cafa --- /dev/null +++ b/tests/landing-page.spec.ts @@ -0,0 +1,20 @@ +import { test, expect } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('/'); + await expect(page).toHaveTitle(/EmptyWork/); +}); + +test('find a curriculum vitae and download', async ({ page }) => { + test.setTimeout(0); + + await page.goto('/'); + + const downloadPromise = page.waitForEvent('download'); + await page.getByRole('link', { name: 'Download my Curriculum Vitae' }).click(); + const download = await downloadPromise; + + const fileName = download.suggestedFilename(); + expect(fileName).toMatch(/\.pdf$/); + +});