1
0
mirror of https://github.com/avinal/avinal.github.io.git synced 2026-07-03 23:30:09 +05:30

remove blogs from elm, switch to hugo

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>

rh-pre-commit.version: 2.2.0
rh-pre-commit.check-secrets: ENABLED
This commit is contained in:
2024-03-30 12:42:14 +05:30
parent 5a21dd69a8
commit 1c20213cd4
7 changed files with 1 additions and 851 deletions
-86
View File
@@ -1,86 +0,0 @@
module Layouts.Blog exposing (Model, Msg, Props, layout)
import Components.Footer exposing (footerLinksToSide)
import Effect exposing (Effect)
import Html
import Html.Attributes exposing (class, id)
import Layout exposing (Layout)
import Route exposing (Route)
import Shared
import Utils.Constants exposing (..)
import View exposing (View)
type alias Props =
{}
layout : Props -> Shared.Model -> Route () -> Layout () Model Msg contentMsg
layout props shared route =
Layout.new
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
-- MODEL
type alias Model =
{}
init : () -> ( Model, Effect Msg )
init _ =
( {}
, Effect.none
)
-- UPDATE
type Msg
= ReplaceMe
update : Msg -> Model -> ( Model, Effect Msg )
update msg model =
case msg of
ReplaceMe ->
( model
, Effect.none
)
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- VIEW
blogTheme : String
blogTheme =
"prose prose-invert mx-auto lg:prose-lg prose-a:decoration-cyan-500 hover:prose-a:decoration-pink-500"
view : { toContentMsg : Msg -> contentMsg, content : View contentMsg, model : Model } -> View contentMsg
view { toContentMsg, model, content } =
{ title = content.title
, body =
[ Html.div [ class "min-h-screen flex flex-col justify-center relative overflow-hidden" ]
[ Html.div [ class "relative w-full bg-neutral md:max-w-3xl md:mx-auto lg:max-w-4xl lg:pb-28" ]
[ Html.article [ class blogTheme ]
content.body
, Html.div [ id "remark42", class "md:px-4 mb-16" ] []
]
]
, footerLinksToSide
]
}
-168
View File
@@ -1,168 +0,0 @@
module Pages.Posts exposing (Model, Msg, page)
import Components.Footer exposing (footerLinksToSide)
import Effect exposing (Effect)
import Html exposing (Html)
import Html.Attributes exposing (alt, class, datetime, href, src, target)
import Http
import Json.Decode as Json
import Page exposing (Page)
import Route exposing (Route)
import Shared
import Url exposing (Protocol(..))
import Utils.Constants exposing (..)
import Utils.Utils as UU
import View exposing (View)
page : Shared.Model -> Route () -> Page Model Msg
page _ _ =
Page.new
{ init = init
, update = update
, subscriptions = subscriptions
, view = view
}
-- INIT
type alias JsonMeta =
{ title : String
, date : String
, description : String
, category : String
, slug : String
, image : String
}
type alias Model =
{ error : Maybe String
, blogList : Maybe (List JsonMeta)
}
init : () -> ( Model, Effect Msg )
init () =
let
cmd : Cmd Msg
cmd =
Http.get
{ url = "/content/posts/posts.json"
, expect = Http.expectJson BloglistReceived (Json.list jsonMetaDecoder)
}
in
( { error = Nothing
, blogList = Nothing
}
, Effect.sendCmd cmd
)
-- UPDATE
type Msg
= BloglistReceived (Result Http.Error (List JsonMeta))
update : Msg -> Model -> ( Model, Effect Msg )
update msg model =
case msg of
BloglistReceived (Ok data) ->
( { model | blogList = Just data }
, Effect.none
)
BloglistReceived (Err err) ->
( { model | error = Just (UU.errorToString err) }, Effect.none )
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
-- VIEW
view : Model -> View Msg
view model =
let
maincard : List JsonMeta -> Html msg
maincard bloglist =
case bloglist of
first :: rest ->
Html.div [ class "max-w-6xl p-6 mx-auto space-y-6 sm:space-y-12 mb-16" ]
[ Html.div [ class "block max-w-sm gap-3 mx-auto sm:max-w-full group hover:no-underline focus:no-underline lg:grid lg:grid-cols-12 bg-neutral-900" ]
[ Html.a [ class "lg:col-span-7", href <| "/posts/" ++ first.category ++ "/" ++ first.slug ] [ Html.img [ class "object-cover w-full h-64 rounded sm:h-96 lg:col-span-7", src first.image, alt first.title ] [] ]
, Html.div [ class "p-6 space-y-2 lg:col-span-5" ]
[ Html.a [ href <| "/posts/" ++ first.category ++ "/" ++ first.slug ]
[ Html.h3 [ class "text-2xl font-semibold sm:text-4xl group-hover:underline group-focus:underline" ]
[ Html.text first.title ]
, Html.time [ class "text-gray-400", datetime first.date ] [ Html.text <| UU.getFormattedDate first.date False ]
, Html.p [] [ Html.text <| String.left 200 first.description ]
]
, Html.a [ href <| "/posts/" ++ first.category, target "_blank" ] [ UU.categoryNtags first.category [] ]
]
]
, Html.div [ class "grid justify-center grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3" ] <| List.map card rest
]
[] ->
Html.div [] []
card : JsonMeta -> Html msg
card blog =
Html.div [ class "max-w-sm mx-auto group hover:no-underline focus:no-underline bg-neutral-900", href ("/posts/" ++ blog.category ++ "/" ++ blog.slug) ]
[ Html.a [ href <| "/posts/" ++ blog.category ++ "/" ++ blog.slug ] [ Html.img [ class "object-cover w-full h-44 rounded", src blog.image, alt blog.title ] [] ]
, Html.div [ class "p-6 space-y-2" ]
[ Html.a [ href <| "/posts/" ++ blog.category ++ "/" ++ blog.slug ]
[ Html.h3 [ class "text-2xl font-semibold group-hover:underline group-focus:underline" ] [ Html.text blog.title ]
, Html.time [ class " text-gray-400", datetime blog.date ] [ Html.text <| UU.getFormattedDate blog.date False ]
, Html.p [] [ Html.text <| String.left 200 blog.description ]
]
, Html.a [ href <| "/posts/" ++ blog.category, target "_blank" ] [ UU.categoryNtags blog.category [] ]
]
]
in
case model.blogList of
Just blogList ->
{ title = "Blog"
, body =
[ Html.section [ class "text-gray-100" ]
[ Html.h1 [ class "text-5xl font-bold mb-6 mt-12 text-center text-white" ] [ Html.text <| "Welcome to my blog" ]
, maincard blogList
, footerLinksToSide
]
]
}
Nothing ->
{ title = "Something went wrong"
, body = [ Html.text <| Maybe.withDefault "" model.error ]
}
-- UTILS
jsonMetaDecoder : Json.Decoder JsonMeta
jsonMetaDecoder =
Json.map6 JsonMeta
(Json.field "title" Json.string)
(Json.field "date" Json.string)
(Json.field "description" Json.string)
(Json.field "category" Json.string)
(Json.field "slug" Json.string)
(Json.field "image" Json.string)
-164
View File
@@ -1,164 +0,0 @@
module Pages.Posts.Category_ exposing (Model, Msg, page)
import Components.Footer exposing (footerLinksToSide)
import Effect exposing (Effect)
import Html exposing (Html)
import Html.Attributes exposing (alt, class, datetime, href, src)
import Http
import Json.Decode as Json
import Page exposing (Page)
import Route exposing (Route)
import Shared
import Utils.Constants exposing (..)
import Utils.Utils as UU
import View exposing (View)
page : Shared.Model -> Route { category : String } -> Page Model Msg
page _ route =
Page.new
{ init = init route
, update = update
, subscriptions = subscriptions
, view = view
}
-- INIT
type alias JsonMeta =
{ title : String
, date : String
, description : String
, category : String
, slug : String
, image : String
}
type alias Model =
{ error : Maybe String
, blogList : Maybe (List JsonMeta)
, category : String
}
init : Route { category : String } -> () -> ( Model, Effect Msg )
init route () =
let
cmd : Cmd Msg
cmd =
Http.get
{ url = "/content/posts/posts.json"
, expect = Http.expectJson BloglistReceived (Json.list jsonMetaDecoder)
}
in
( { error = Nothing
, blogList = Nothing
, category = route.params.category
}
, Effect.sendCmd cmd
)
-- UPDATE
type Msg
= BloglistReceived (Result Http.Error (List JsonMeta))
update : Msg -> Model -> ( Model, Effect Msg )
update msg model =
case msg of
BloglistReceived (Ok data) ->
( { model | blogList = Just data }
, Effect.none
)
BloglistReceived (Err err) ->
( { model | error = Just (UU.errorToString err) }, Effect.none )
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
-- VIEW
view : Model -> View Msg
view model =
let
card : JsonMeta -> Html msg
card blog =
Html.a [ class "flex flex-wrap mb-6 ", href <| "/posts/" ++ blog.category ++ "/" ++ blog.slug ]
[ Html.div [ class "grow-0 shrink-0 basis-auto w-full md:w-3/12 md:mb-0 ml-auto" ]
[ Html.div [ class "overflow-hidden relative bg-no-repeat bg-cover ripple" ]
[ Html.img [ src blog.image, class "w-full h-44 object-cover rounded", alt blog.title ] []
]
]
, Html.div [ class "grow-0 shrink-0 basis-auto w-full md:w-9/12 xl:w-7/12 p-3 md:mb-0 mr-auto bg-neutral-900" ]
[ Html.h5 [ class "text-2xl font-bold mb-2" ] [ Html.text blog.title ]
, Html.time [ class "text-gray-400 text-sm", datetime blog.date ] [ Html.text <| UU.getFormattedDate blog.date True ]
, Html.p [ class "text-gray-500 mt-4 text-md" ] [ Html.text <| String.left 200 blog.description ]
]
]
in
case model.blogList of
Just blogList ->
let
filteredList =
filterBlogListByCategory blogList model.category
in
case filteredList of
[] ->
{ title = "No posts in this category"
, body = []
}
clist ->
{ title = "Posts in " ++ model.category ++ " category"
, body =
[ Html.div []
[ Html.section [ class "mb-32 text-gray-200 text-center md:text-left" ] <|
Html.h1 [ class "text-5xl font-bold mb-12 mt-12 text-center text-white" ] [ Html.text "Posts in ", Html.i [ class "text-pink-600" ] [ Html.text model.category ], Html.text " category" ]
:: List.map card clist
, footerLinksToSide
]
]
}
Nothing ->
{ title = "There is no such category"
, body = []
}
-- UTILS
jsonMetaDecoder : Json.Decoder JsonMeta
jsonMetaDecoder =
Json.map6 JsonMeta
(Json.field "title" Json.string)
(Json.field "date" Json.string)
(Json.field "description" Json.string)
(Json.field "category" Json.string)
(Json.field "slug" Json.string)
(Json.field "image" Json.string)
filterBlogListByCategory : List JsonMeta -> String -> List JsonMeta
filterBlogListByCategory blogList category =
List.filter (\meta -> meta.category == category) blogList
-239
View File
@@ -1,239 +0,0 @@
module Pages.Posts.Category_.Post_ exposing (Model, Msg, page)
import Effect exposing (Effect)
import Html exposing (Html)
import Html.Attributes exposing (alt, class, datetime, href, src, title)
import Http
import Layouts
import Page exposing (Page)
import Route exposing (Route)
import Shared
import String exposing (words)
import Url exposing (Protocol(..))
import Utils.Utils as UU
import View exposing (View)
import Yaml.Decode as Yaml
page : Shared.Model -> Route { category : String, post : String } -> Page Model Msg
page _ route =
Page.new
{ init = init route
, update = update
, subscriptions = subscriptions
, view = view
}
|> Page.withLayout layout
-- LAYOUT
layout : Model -> Layouts.Layout Msg
layout _ =
Layouts.Blog
{}
-- INIT
type alias Model =
{ blog : Maybe Blog
, requestUrl : String
, success : Bool
, fragment : String
, error : Maybe String
}
type alias Blog =
{ meta : YamlMeta
, content : String
, words : Int
}
init : Route { category : String, post : String } -> () -> ( Model, Effect Msg )
init route () =
let
requestUrl =
UU.contentUrl
{ category = route.params.category
, post = route.params.post
}
cmd : Cmd Msg
cmd =
Http.get
{ url = requestUrl
, expect = Http.expectString RawMarkdownReceived
}
in
( { blog = Nothing
, requestUrl = requestUrl
, success = False
, fragment = Maybe.withDefault "" route.hash
, error = Nothing
}
, Effect.sendCmd cmd
)
-- UPDATE
type Msg
= RawMarkdownReceived (Result Http.Error String)
update : Msg -> Model -> ( Model, Effect Msg )
update msg model =
case msg of
RawMarkdownReceived (Ok data) ->
case splitMetaContent data of
Ok blog ->
( { model | blog = Just blog, success = True }, Effect.none )
Err err ->
( { model | success = False, error = Just err }, Effect.none )
RawMarkdownReceived (Err err) ->
( { model | success = False, error = Just (UU.errorToString err) }, Effect.none )
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
-- VIEW
view : Model -> View Msg
view model =
case model.blog of
Just blog ->
{ title = blog.meta.title ++ " | Blog"
, body =
[ Html.div [ class "bg-neutral-900 md:-mx-8 lg:-mx-16 px-8 py-1" ]
[ Html.header [ class "relative" ]
[ Html.img
[ class "object-cover w-full h-60 sm:h-96 brightness-50 "
, src blog.meta.image
, alt blog.meta.title
]
[]
, Html.h1 [ class "absolute top-3/4 left-1/2 -translate-x-1/2 -translate-y-1/2 text-center w-full" ]
[ Html.text blog.meta.title ]
]
, Html.span [ class "text-base font-light font-sans oldstyle-nums" ]
[ Html.a [ href "/pages/about-me", class "font-bold no-underline hover:text-pink-500" ]
[ Html.text "Avinal Kumar" ]
, Html.text " | "
, Html.time [ datetime blog.meta.date ] [ Html.text <| UU.getFormattedDate blog.meta.date False ]
, Html.text <|
" | "
++ String.fromInt blog.words
++ " words | ~"
++ String.fromInt (blog.words // 200)
++ " mins read"
]
, Html.span [ class "text-base font-light float-right" ]
[ Html.a
[ String.dropLeft 8 model.requestUrl
|> String.dropRight 3
|> String.append "https://null.avinal.space"
|> href
, class "hover:text-pink-500"
]
[ Html.abbr [ class "fa-solid fa-terminal no-underline", title "See a basic version of this page." ] [] ]
]
, articleNode blog.content model.fragment blog.meta.title blog.meta.description
]
, UU.categoryNtags blog.meta.category blog.meta.tags
]
}
Nothing ->
{ title = "Avinal Kumar | Something went wrong"
, body =
[ case model.error of
Just err ->
UU.errorView err
Nothing ->
Html.div [ class "flex items-center justify-center flex-col object-cover object-center " ]
[]
]
}
articleNode : String -> String -> String -> String -> Html Msg
articleNode data fragment title description =
Html.node "rendered-md"
[ Html.Attributes.attribute "markdowndata" data
, Html.Attributes.attribute "fragment" fragment
, Html.Attributes.attribute "title" title
, Html.Attributes.attribute "description" description
, class "line-numbers"
]
[]
-- UTILITIES
type alias YamlMeta =
{ title : String
, date : String
, description : String
, tags : List String
, category : String
, image : String
, modified : Maybe String
}
splitMetaContent : String -> Result String Blog
splitMetaContent data =
let
headIndices : List Int
headIndices =
String.indices "---" data |> List.take 2
metadata =
String.slice ((Maybe.withDefault 0 <| List.head headIndices) + 3)
((Maybe.withDefault 0 <| List.head <| List.reverse headIndices) - 1)
data
content =
String.dropLeft ((Maybe.withDefault 0 <| List.head <| List.reverse headIndices) + 3) data
in
case Yaml.fromString metaDecoder metadata of
Ok meta ->
Ok { meta = meta, content = content, words = List.length <| words content }
Err err ->
Err ("YAML front matter parsing failed: " ++ Yaml.errorToString err)
metaDecoder : Yaml.Decoder YamlMeta
metaDecoder =
Yaml.map7 YamlMeta
(Yaml.field "title" Yaml.string)
(Yaml.field "date" Yaml.string)
(Yaml.field "description" Yaml.string)
(Yaml.field "tags" (Yaml.list Yaml.string))
(Yaml.field "category" Yaml.string)
(Yaml.field "image" Yaml.string)
(Yaml.maybe (Yaml.field "modified" Yaml.string))
+1 -2
View File
@@ -1,4 +1,3 @@
import "./web-components/MarkedRender.js";
import "./web-components/Calcom.js";
export const onReady = ({ app, env }) => {};
export const onReady = ({ app, env }) => { };
-144
View File
@@ -1,144 +0,0 @@
import { marked } from "marked";
import { markedHighlight } from "marked-highlight";
import Prism from "prismjs";
import "./Prism";
customElements.define(
"rendered-md",
class extends HTMLElement {
constructor() {
super();
this.lead = 0; // Moved lead here
}
connectedCallback() {
this.runMarked();
}
updateMeta(title, description) {
const metaInfo = {
'meta[name="title"]': `${title} | Avinal Kumar | Personal Website`,
'meta[name="description"]': description,
'meta[property="og:title"]': `${title} | Avinal Kumar | Personal Website`,
'meta[property="og:description"]': description,
'meta[property="og:url"]': window.location,
'meta[property="twitter:title"]': `${title} | Avinal Kumar | Personal Website`,
'meta[property="twitter:description"]': description,
'meta[property="twitter:url"]': window.location,
};
for (const [selector, content] of Object.entries(metaInfo)) {
const element = document.querySelector(selector);
if (element) {
element.setAttribute("content", content);
} else {
console.warn(`Element ${selector} not found`);
}
}
}
addComments() {
window.remark_config = {
host: "https://remark42.avinal.space",
site_id: "remark",
theme: "dark",
show_rss_subscription: false,
no_footer: true,
};
!(function (e, n) {
for (var o = 0; o < e.length; o++) {
var r = n.createElement("script"),
c = ".js",
d = n.head || n.body;
"noModule" in r
? ((r.type = "module"), (c = ".mjs"))
: (r.async = !0),
(r.defer = !0),
(r.src = remark_config.host + "/web/" + e[o] + c),
d.appendChild(r);
}
})(remark_config.components || ["embed"], document);
}
runMarked() {
const {
markdowndata: data,
fragment,
title,
description,
} = this.attributes;
this.updateMeta(title.value, description.value);
const renderer = new marked.Renderer();
renderer.heading = (text, level) => {
const escapedText = text
.trim()
.toLowerCase()
.replace(/[^\w]+/g, "-");
return `<h${level} id="${escapedText}">
${text}
<a title="Permalink to ${text}" href="#${escapedText}">
#
</a>
</h${level}>`;
};
renderer.paragraph = (text) => {
this.lead++;
return this.lead === 1
? `<p class="lead">${text}</p>`
: `<p>${text}</p>`;
};
renderer.image = (href, title, text) => {
let pos = text.search(":");
let direction = text.substr(pos + 1);
text = text.substr(0, pos);
let put = "max-w-xs ";
switch (direction) {
case "right":
put += "float-right ";
break;
case "left":
put += "float-left ";
break;
default:
put = "float-none ";
}
return `<img class="${put} grow-0 shrink-0 basis-auto w-full px-3" src=${href} alt="${text}">`;
};
marked.setOptions({
renderer: renderer,
});
marked.use(
markedHighlight({
highlight(code, lang) {
const grammer = Prism.languages[lang];
if (!grammer) {
console.warn(`Unable to find prism highlight for '${lang}'`);
return;
}
return Prism.highlight(code, grammer, lang);
},
})
);
this.innerHTML = marked(data.value);
this.addComments();
if (fragment && fragment.value) {
window.location = `#${fragment.value}`;
}
}
static get observedAttributes() {
return ["markdowndata", "fragment", "title", "description"];
}
}
);
-48
View File
@@ -1,48 +0,0 @@
// Load Plugins
// import 'prismjs/plugins/toolbar/prism-toolbar';
// import "prismjs/plugins/line-numbers/prism-line-numbers";
// import "prismjs/plugins/line-numbers/prism-line-numbers.css";
// import "prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard";
// load languages
import "prismjs/components/prism-bash";
import "prismjs/components/prism-markup";
import "prismjs/components/prism-css";
import "prismjs/components/prism-clike";
import "prismjs/components/prism-javascript";
import "prismjs/components/prism-bash";
import "prismjs/components/prism-basic";
import "prismjs/components/prism-brainfuck";
import "prismjs/components/prism-c";
import "prismjs/components/prism-cpp";
import "prismjs/components/prism-cmake";
import "prismjs/components/prism-diff";
import "prismjs/components/prism-docker";
import "prismjs/components/prism-elm";
import "prismjs/components/prism-git";
import "prismjs/components/prism-go";
import "prismjs/components/prism-go-module";
import "prismjs/components/prism-graphql";
import "prismjs/components/prism-haskell";
import "prismjs/components/prism-http";
import "prismjs/components/prism-java";
import "prismjs/components/prism-json";
import "prismjs/components/prism-json5";
import "prismjs/components/prism-jsonp";
import "prismjs/components/prism-makefile";
import "prismjs/components/prism-markdown";
import "prismjs/components/prism-matlab";
import "prismjs/components/prism-mermaid";
import "prismjs/components/prism-powershell";
import "prismjs/components/prism-protobuf";
import "prismjs/components/prism-python";
import "prismjs/components/prism-rest";
import "prismjs/components/prism-rust";
import "prismjs/components/prism-shell-session";
import "prismjs/components/prism-sql";
import "prismjs/components/prism-toml";
import "prismjs/components/prism-typescript";
import "prismjs/components/prism-vim";
import "prismjs/components/prism-wasm";
import "prismjs/components/prism-yaml";