feat: Initial working site, with single network article.
This commit is contained in:
@@ -5,58 +5,72 @@ import PathKit
|
||||
import SagaParsleyMarkdownReader
|
||||
import SagaSwimRenderer
|
||||
|
||||
func permalink(item: Item<ArticleMetadata>) {
|
||||
// Insert the publication year into the permalink.
|
||||
// If the `relativeDestination` was "articles/looking-for-django-cms/index.html", then it becomes "articles/2009/looking-for-django-cms/index.html"
|
||||
var components = item.relativeDestination.components
|
||||
components.insert("\(Calendar.current.component(.year, from: item.date))", at: 1)
|
||||
item.relativeDestination = Path(components: components)
|
||||
}
|
||||
|
||||
func removingBreaks<M>(item: Item<M>) {
|
||||
// remove explicit <br /> from items that show up likely due to how prettier formats
|
||||
// markdown files inside of neovim.
|
||||
item.body = item.body.removeBreaks
|
||||
}
|
||||
|
||||
@main
|
||||
struct Run {
|
||||
static func main() async throws {
|
||||
// try await Saga(input: "content", output: "deploy")
|
||||
// // All markdown files within the "articles" subfolder will be parsed to html,
|
||||
// // using ArticleMetadata as the Item's metadata type.
|
||||
// // Furthermore we are only interested in public articles.
|
||||
// .register(
|
||||
// folder: "articles",
|
||||
// metadata: ArticleMetadata.self,
|
||||
// readers: [.parsleyMarkdownReader],
|
||||
// itemProcessor: sequence(removingBreaks, publicationDateInFilename, permalink),
|
||||
// filter: \.public,
|
||||
// writers: [
|
||||
// .itemWriter(swim(renderArticle)),
|
||||
// .listWriter(swim(renderArticles)),
|
||||
// .tagWriter(swim(renderTag), tags: \.metadata.tags),
|
||||
// .yearWriter(swim(renderYear)),
|
||||
// // Atom feed for all articles, and a feed per tag
|
||||
// .listWriter(
|
||||
// atomFeed(
|
||||
// title: SiteMetadata.name,
|
||||
// author: SiteMetadata.author,
|
||||
// baseURL: SiteMetadata.url,
|
||||
// summary: \.metadata.summary
|
||||
// ),
|
||||
// output: "feed.xml"
|
||||
// ),
|
||||
// .tagWriter(
|
||||
// atomFeed(
|
||||
// title: SiteMetadata.name,
|
||||
// author: SiteMetadata.author, baseURL: SiteMetadata.url, summary: \.metadata.summary
|
||||
// ),
|
||||
// output: "tag/[key]/feed.xml",
|
||||
// tags: \.metadata.tags
|
||||
// )
|
||||
// ]
|
||||
// )
|
||||
// // All the remaining markdown files will be parsed to html,
|
||||
// // using the default EmptyMetadata as the Item's metadata type.
|
||||
// .register(
|
||||
// metadata: PageMetadata.self,
|
||||
// readers: [.parsleyMarkdownReader],
|
||||
// itemProcessor: removingBreaks,
|
||||
// itemWriteMode: .keepAsFile, // need to keep 404.md as 404.html, not 404/index.html
|
||||
// writers: [.itemWriter(swim(renderPage))]
|
||||
// )
|
||||
//
|
||||
// // Run the steps we registered above
|
||||
// .run()
|
||||
// // All the remaining files that were not parsed to markdown, so for example images, raw html files and css,
|
||||
// // are copied as-is to the output folder.
|
||||
// .staticFiles()
|
||||
try await Saga(input: "content", output: "deploy")
|
||||
// All markdown files within the "articles" subfolder will be parsed to html,
|
||||
// using ArticleMetadata as the Item's metadata type.
|
||||
// Furthermore we are only interested in public articles.
|
||||
.register(
|
||||
folder: "articles",
|
||||
metadata: ArticleMetadata.self,
|
||||
readers: [.parsleyMarkdownReader],
|
||||
itemProcessor: sequence(removingBreaks, publicationDateInFilename, permalink),
|
||||
filter: \.public,
|
||||
writers: [
|
||||
.itemWriter(swim(renderArticle)),
|
||||
.listWriter(swim(renderArticles)),
|
||||
.tagWriter(swim(renderTag), tags: \.metadata.tags),
|
||||
.yearWriter(swim(renderYear)),
|
||||
// Atom feed for all articles, and a feed per tag
|
||||
.listWriter(
|
||||
atomFeed(
|
||||
title: SiteMetadata.name,
|
||||
author: SiteMetadata.author,
|
||||
baseURL: SiteMetadata.url,
|
||||
summary: \.metadata.summary
|
||||
),
|
||||
output: "feed.xml"
|
||||
),
|
||||
.tagWriter(
|
||||
atomFeed(
|
||||
title: SiteMetadata.name,
|
||||
author: SiteMetadata.author, baseURL: SiteMetadata.url, summary: \.metadata.summary
|
||||
),
|
||||
output: "tag/[key]/feed.xml",
|
||||
tags: \.metadata.tags
|
||||
)
|
||||
]
|
||||
)
|
||||
// All the remaining markdown files will be parsed to html,
|
||||
// using the default EmptyMetadata as the Item's metadata type.
|
||||
.register(
|
||||
metadata: PageMetadata.self,
|
||||
readers: [.parsleyMarkdownReader],
|
||||
itemProcessor: removingBreaks,
|
||||
itemWriteMode: .keepAsFile, // need to keep 404.md as 404.html, not 404/index.html
|
||||
writers: [.itemWriter(swim(renderPage))]
|
||||
)
|
||||
|
||||
// Run the steps we registered above
|
||||
.run()
|
||||
// All the remaining files that were not parsed to markdown, so for example images, raw html files and css,
|
||||
// are copied as-is to the output folder.
|
||||
.staticFiles()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func baseLayout(
|
||||
body(class: "bg-page text-white pb-5 font-avenir \(section.rawValue)") {
|
||||
siteHeader(section)
|
||||
|
||||
div(class: "container pt-12 lg:pt-28") {
|
||||
div(class: "container") {
|
||||
children()
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ private func siteHeader(_ section: Section) -> Node {
|
||||
div(class: "header__logo") {
|
||||
a(href: "/") {
|
||||
div(class: "logo") {
|
||||
"mhoush.com"
|
||||
"docs.housh.dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,44 +59,27 @@ private func siteHeader(_ section: Section) -> Node {
|
||||
}
|
||||
|
||||
private func footer(_ rssLink: String) -> Node {
|
||||
div(class: "site-footer text-gray gray-links border-t border-light text-center pt-6 mt-8 text-sm") {
|
||||
p {
|
||||
div(class: "site-footer text-slate-200 border-t border-light text-center pt-6 mt-8 text-sm") {
|
||||
div {
|
||||
"Copyright © Michael Housh 2023-\(Date().description.prefix(4))."
|
||||
}
|
||||
p {
|
||||
p(class: "mb-2") {
|
||||
"Built in Swift using"
|
||||
a(href: "https://github.com/loopwerk/Saga", rel: "nofollow", target: "_blank") { "Saga" }
|
||||
a(
|
||||
class: "text-orange-400 [&:hover]:border-b border-green-400",
|
||||
href: "https://github.com/loopwerk/Saga",
|
||||
rel: "nofollow",
|
||||
target: "_blank"
|
||||
) { "Saga" }
|
||||
"("
|
||||
%a(href: "https://github.com/m-housh/mhoush.com", rel: "nofollow", target: "_blank") { "source" }
|
||||
%a(
|
||||
class: "[&:hover]:border-b border-green-400",
|
||||
href: "https://github.com/m-housh/mhoush.com",
|
||||
rel: "nofollow",
|
||||
target: "_blank"
|
||||
) { "source" }
|
||||
%")."
|
||||
}
|
||||
p {
|
||||
a(
|
||||
href: "\(SiteMetadata.url.absoluteString)/articles/\(rssLink)feed.xml",
|
||||
rel: "nofollow",
|
||||
target: "_blank"
|
||||
) { "RSS" }
|
||||
" | "
|
||||
a(href: "https://github.com/m-housh", rel: "nofollow", target: "_blank") { "Github" }
|
||||
" | "
|
||||
a(
|
||||
href: "https://www.youtube.com/channel/UCb58SeURd5bObfTiL0KoliA",
|
||||
rel: "nofollow",
|
||||
target: "_blank"
|
||||
) { "Youtube" }
|
||||
" | "
|
||||
a(href: "https://www.facebook.com/michael.housh", rel: "nofollow", target: "_blank") { "Facebook" }
|
||||
" | "
|
||||
a(href: "mailto:michael@mhoush.com", rel: "nofollow") { "Email" }
|
||||
}
|
||||
p {
|
||||
span {
|
||||
"All articles are licensed under Creative-Commons (CC BY-NC) 4.0"
|
||||
}
|
||||
a(href: "https://creativecommons.org/licenses/by-nc/4.0/") {
|
||||
img(class: "justify-center", src: "/static/images/by-nc.png", width: "100")
|
||||
}
|
||||
}
|
||||
script(src: "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js")
|
||||
script(src: "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/keep-markup/prism-keep-markup.min.js")
|
||||
script(src: "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js")
|
||||
|
||||
@@ -14,7 +14,7 @@ func tagPrefix(index: Int, totalTags: Int) -> Node {
|
||||
}
|
||||
|
||||
func renderArticleInfo(_ article: Item<ArticleMetadata>) -> Node {
|
||||
div(class: "text-gray gray-links text-sm") {
|
||||
div(class: "text-slate-400 gray-links text-sm mb-8") {
|
||||
span(class: "border-r border-gray pr-2 mr-2") {
|
||||
article.date.formatted("MMMM dd, yyyy")
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func renderArticleInfo(_ article: Item<ArticleMetadata>) -> Node {
|
||||
Node.raw("""
|
||||
<i class="fa fa-home"></i>
|
||||
"""),
|
||||
%a(class: "text-orange [&:hover]:border-b border-green", href: "/articles/tag/\(tag.slugified)/") {
|
||||
%a(class: "text-orange-400 [&:hover]:border-b border-green-400", href: "/articles/tag/\(tag.slugified)/") {
|
||||
tag
|
||||
}
|
||||
])
|
||||
@@ -100,12 +100,12 @@ func renderArticle(context: ItemRenderingContext<ArticleMetadata>) -> Node {
|
||||
title: context.item.title,
|
||||
extraHeader: generateHeader(.article(context.item))
|
||||
) {
|
||||
article(class: "prose") {
|
||||
article(class: "prose pt-8") {
|
||||
h1 { context.item.title }
|
||||
div(class: "-mt-6") {
|
||||
div {
|
||||
renderArticleInfo(context.item)
|
||||
}
|
||||
img(alt: "banner", src: context.item.imagePath)
|
||||
// img(alt: "banner", src: context.item.imagePath)
|
||||
Node.raw(context.item.body)
|
||||
}
|
||||
|
||||
@@ -135,23 +135,6 @@ func renderArticle(context: ItemRenderingContext<ArticleMetadata>) -> Node {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Giscus comment section.
|
||||
commentSection
|
||||
|
||||
div(class: "border-t border-light mt-8 pt-8") {
|
||||
h2(class: "text-4xl font-extrabold mb-8") { "Author" }
|
||||
div(class: "flex flex-col lg:flex-row gap-8") {
|
||||
div(class: "flex-[0_0_120px]") {
|
||||
img(class: "w-[120px] h-[120px] rounded-full", src: "/static/images/avatar.png")
|
||||
}
|
||||
|
||||
div(class: "prose") {
|
||||
h3(class: "!m-0") { SiteMetadata.author }
|
||||
p(class: "text-gray") { SiteMetadata.summary }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,26 +154,3 @@ func renderArticleForGrid(article: Item<ArticleMetadata>) -> Node {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var commentSection: Node {
|
||||
div(class: "border-t border-light pt-8") {
|
||||
Node.raw("""
|
||||
<script src="https://giscus.app/client.js"
|
||||
data-repo="m-housh/mhoush.com"
|
||||
data-repo-id="R_kgDOJagAXA"
|
||||
data-category="Article Discussions"
|
||||
data-category-id="DIC_kwDOJagAXM4CnLfv"
|
||||
data-mapping="pathname"
|
||||
data-strict="0"
|
||||
data-reactions-enabled="1"
|
||||
data-emit-metadata="0"
|
||||
data-input-position="bottom"
|
||||
data-theme="preferred_color_scheme"
|
||||
data-lang="en"
|
||||
data-loading="lazy"
|
||||
crossorigin="anonymous"
|
||||
async>
|
||||
</script>
|
||||
""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func renderPage(context: ItemRenderingContext<PageMetadata>) -> Node {
|
||||
|
||||
func renderHome(body: String) -> Node {
|
||||
div {
|
||||
img(alt: "Avatar", class: "my-24 w-[315px] h-200px mx-auto", src: "/static/images/avatar.png")
|
||||
// img(alt: "Avatar", class: "my-24 w-[315px] h-200px mx-auto", src: "/static/images/avatar.png")
|
||||
|
||||
div(class: "my-24 uppercase font-avenir text-[40px] leading-[1.25] font-thin text-center [&>h1>strong]:font-bold") {
|
||||
Node.raw(body)
|
||||
|
||||
7
content/about.md
Normal file
7
content/about.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
section: about
|
||||
---
|
||||
|
||||
# About
|
||||
|
||||
Internal documentation site for **Housh - The Home Energy Experts**
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
date: 2025-4-02
|
||||
updated: 2025-04-02
|
||||
author: "Michael Housh"
|
||||
tags: network, infrastructure
|
||||
---
|
||||
|
||||
# Networking
|
||||
@@ -65,7 +65,7 @@ in the future).
|
||||
The unifi management console is what handles firewall rules for the networks. It
|
||||
is accessed via `Settings -> Security -> Firewall` on the management console.
|
||||
|
||||

|
||||

|
||||
|
||||
This is where settings are made to either allow or deny traffic on the networks
|
||||
from communicating with other networks or the internet.
|
||||
@@ -74,7 +74,7 @@ from communicating with other networks or the internet.
|
||||
|
||||
DNS is what translates IP addresses to domain names (i.e. `po.housh.dev` ->
|
||||
`192.168.50.6`). This is managed by the unifi management console and is accessed
|
||||
via `Settigns -> Routing -> DNS`.
|
||||
via `Settings -> Routing -> DNS`.
|
||||
|
||||
We primarily use wildcard records, which allow the actual routing to be handled
|
||||
by the servers to the correct service.
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
section: home
|
||||
---
|
||||
|
||||
BIN
content/static/favicon.ico
Normal file
BIN
content/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
@@ -0,0 +1,136 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
/*
|
||||
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
||||
so we've added these compatibility styles to make sure everything still
|
||||
looks the same as it did with Tailwind CSS v3.
|
||||
|
||||
If we ever want to remove these styles, we need to add an explicit border
|
||||
color utility to any element that depends on these defaults.
|
||||
*/
|
||||
@layer base {
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
border-color: var(--color-gray-200, currentColor);
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--accent: #a6e3a1;
|
||||
--background: rgb(34, 33, 41);
|
||||
--color: #fff;
|
||||
--border-color: hsla(0, 0%, 100%, 0.1);
|
||||
--phoneWidth: (max-width: 684px);
|
||||
--tabletWidth: (max-width: 900px) --orange: #f5a87f;
|
||||
--green: #a6e3a1;
|
||||
}
|
||||
|
||||
/* HEADER */
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
.header__inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-center: space-between;
|
||||
}
|
||||
.header__logo {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
.header__logo:after {
|
||||
content: "";
|
||||
background: repeating-linear-gradient(
|
||||
90deg,
|
||||
#ffa86a,
|
||||
#ffa86a 2px,
|
||||
transparent 0,
|
||||
transparent 10px
|
||||
);
|
||||
background: repeating-linear-gradient(
|
||||
90deg,
|
||||
var(--accent),
|
||||
var(--accent) 2px,
|
||||
transparent 0,
|
||||
transparent 10px
|
||||
);
|
||||
display: block;
|
||||
width: 100%;
|
||||
right: 10px;
|
||||
}
|
||||
.header__logo a {
|
||||
flex: 0 0 auto;
|
||||
max-width: 100%;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
background: #ffa86a;
|
||||
background: var(--accent);
|
||||
color: #000;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.header .menu {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
@apply border-b-2 border-orange-400;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-slate-900;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-4xl;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-3xl mb-4 pt-4;
|
||||
color: var(--green);
|
||||
}
|
||||
h3 {
|
||||
@apply text-2xl text-amber-500 py-4;
|
||||
}
|
||||
|
||||
p {
|
||||
@apply mb-8;
|
||||
}
|
||||
|
||||
img {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
article h2 {
|
||||
@apply border-b-2 border-slate-200;
|
||||
}
|
||||
|
||||
.container {
|
||||
@apply px-10;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
nav a:active {
|
||||
@apply border-yellow-300 text-white;
|
||||
}
|
||||
|
||||
2
content/static/output.css
Normal file
2
content/static/output.css
Normal file
File diff suppressed because one or more lines are too long
123
content/static/prism.css
Normal file
123
content/static/prism.css
Normal file
@@ -0,0 +1,123 @@
|
||||
/* PrismJS 1.29.0
|
||||
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+swift */
|
||||
/**
|
||||
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
|
||||
* Based on https://github.com/chriskempson/tomorrow-theme
|
||||
* @author Rose Pritchard
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: #ccc;
|
||||
background: none;
|
||||
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: 0.5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #2d2d2d;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: 0.1em;
|
||||
border-radius: 0.3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.block-comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.token.tag,
|
||||
.token.attr-name,
|
||||
.token.namespace,
|
||||
.token.deleted {
|
||||
color: #e2777a;
|
||||
}
|
||||
|
||||
.token.function-name {
|
||||
color: #6196cc;
|
||||
}
|
||||
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.function {
|
||||
color: #f08d49;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.class-name,
|
||||
.token.constant,
|
||||
.token.symbol {
|
||||
color: #f8c555;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.important,
|
||||
.token.atrule,
|
||||
.token.keyword,
|
||||
.token.builtin {
|
||||
color: #cc99cd;
|
||||
}
|
||||
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.attr-value,
|
||||
.token.regex,
|
||||
.token.variable {
|
||||
color: #7ec699;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url {
|
||||
color: #67cdcc;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.token.inserted {
|
||||
color: green;
|
||||
}
|
||||
10
content/static/style.css
Normal file
10
content/static/style.css
Normal file
@@ -0,0 +1,10 @@
|
||||
body {
|
||||
font-family: Helvetica;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
8
justfile
8
justfile
@@ -0,0 +1,8 @@
|
||||
[private]
|
||||
default:
|
||||
@just --list
|
||||
|
||||
# Run the development server.
|
||||
[group('dev')]
|
||||
run:
|
||||
@swift run watch content Sources deploy
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"name": "docs.housh.dev",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
@@ -12,13 +13,9 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"tailwindcss": "^3.4.17"
|
||||
"tailwindcss": "^4.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.5.3",
|
||||
"sass": "^1.85.0",
|
||||
"tailwind": "^4.0.0"
|
||||
"@tailwindcss/cli": "^4.0.8"
|
||||
}
|
||||
}
|
||||
|
||||
3123
pnpm-lock.yaml
generated
3123
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user