Local metadata link commit at 2025-06-06 15:40:57 — file:///home/mrhavens/git-local-repos/git-sigil.git

This commit is contained in:
Mark Randall Havens 2025-06-06 15:40:57 -05:00
commit b548318725
134 changed files with 15789 additions and 0 deletions

25
src/utils/index.ts Normal file
View file

@ -0,0 +1,25 @@
import path from "path";
const MONTHS = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
export const toTitleCase = (str: string) =>
str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
export const getMonthName = (date: Date) => MONTHS[new Date(date).getMonth()];
export const getSlugFromPathname = (pathname: string) =>
path.basename(pathname, path.extname(pathname));

View file

@ -0,0 +1,29 @@
import { callout } from "./schema/callout.mdoc";
import { link } from "./schema/link.mdoc";
import { tweetEmbed } from "./schema/tweet-embed.mdoc";
import { tabs } from "./schema/tabs.mdoc";
import { ytEmbed } from "./schema/yt-embed.mdoc";
/** @type {import('@markdoc/markdoc').Config} */
export const config = {
tags: {
callout,
link,
tweet: tweetEmbed,
yt: ytEmbed,
tabs,
},
functions: {
getCountryEmoji: {
transform(parameters) {
const [country] = Object.values(parameters);
const countryToEmojiMap = {
japan: "🇯🇵",
spain: "🇪🇸",
france: "🇫🇷",
};
return countryToEmojiMap[country as string] ?? "🏳";
},
},
},
};

View file

@ -0,0 +1,18 @@
import { component } from "@astrojs/markdoc/config";
/** @type {import('@markdoc/markdoc').Schema} */
export const callout = {
render: component("./src/components/mdoc/Callout.astro"),
children: ["paragraph", "tag", "list"],
attributes: {
type: {
type: String,
default: "note goes here...",
matches: ["error", "check", "note", "warning"],
errorLevel: "critical",
},
title: {
type: String,
},
},
};

View file

@ -0,0 +1,34 @@
import { component } from "@astrojs/markdoc/config";
const SITE_DOMAIN = "astro-ink.vercel.app";
function getHrefTarget(attributes) {
const href = attributes.href;
if (
href.includes(SITE_DOMAIN) ||
href.startsWith("/") ||
href.startsWith("#") ||
href.startsWith("?")
) {
return "_self";
} else {
return "_blank";
}
}
/** @type {import('@markdoc/markdoc').Schema} */
export const link = {
render: component("./src/components/mdoc/Link.astro"),
children: ["strong", "em", "s", "code", "text", "tag"],
attributes: {
href: {
type: String,
required: true,
},
title: {
type: String,
},
target: {
type: String,
},
},
};

View file

@ -0,0 +1,15 @@
import { component } from "@astrojs/markdoc/config";
/** @type {import('@markdoc/markdoc').Schema} */
export const tabs = {
render: component("./src/components/mdoc/Tabs/Tabs.astro"),
children: ["paragraph", "tag", "list"],
attributes: {
tabs: {
type: Array,
},
heading: {
type: String,
},
},
};

View file

@ -0,0 +1,12 @@
import { component } from "@astrojs/markdoc/config";
/** @type {import('@markdoc/markdoc').Schema} */
export const tweetEmbed = {
render: component("./src/components/mdoc/TweetEmbed.astro"),
attributes: {
url: {
type: String,
required: true,
},
},
};

View file

@ -0,0 +1,16 @@
import { component } from "@astrojs/markdoc/config";
/** @type {import('@markdoc/markdoc').Schema} */
export const ytEmbed = {
render: component("./src/components/mdoc/YTVideoEmbed.astro"),
attributes: {
url: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
},
};

View file

@ -0,0 +1,34 @@
import type { MediaExternallyHostedVideo } from "./media";
export type Feed2JsonYtFeedItem = {
guid: `yt:video:${string}`;
url: string;
title: string;
date_published: string;
author: {
name: string;
};
};
export function toFeedToJsonUrl(ytVideoChannelId: string) {
return `https://feed2json.org/convert?url=https://www.youtube.com/feeds/videos.xml?channel_id=${ytVideoChannelId}`;
}
export function toMediaFormatFromFeed2JsonUrl(posts: {
items: Array<Feed2JsonYtFeedItem>;
}): Array<MediaExternallyHostedVideo> {
return posts?.items?.length
? posts.items.map((post) => ({
title: post.title,
description: "",
url: post.url,
participants: [],
date: post.date_published,
host: post.author.name,
thumbnail: `https://img.youtube.com/vi/${post.guid.substring(
post.guid.lastIndexOf(":") + 1,
post.guid.length,
)}/0.jpg`,
}))
: [];
}

15
src/utils/media.ts Normal file
View file

@ -0,0 +1,15 @@
export type GithubContentURL =
`https://api.github.com/repos/${string}/contents/${string}`;
export type MediaExternallyHostedVideo = {
title: string;
description: string;
url: string;
host: string;
participants: Array<string>;
date: string;
thumbnail?: string;
};
export const DEFAULT_MEDIA_URL: GithubContentURL =
"https://api.github.com/repos/one-aalam/astro-ink/contents/src/data/astro-media.json";

View file

@ -0,0 +1,16 @@
const client = new Map<string, number>();
export const getViewsBySlug = async (slug: string) => {
if (slug) {
const prevValue = client.get(slug);
let newValue = 1;
if (prevValue) {
newValue = parseInt(`${prevValue}`) + 1;
client.set(slug, newValue);
} else {
client.set(slug, 1);
}
return newValue;
}
return 0;
};

View file

@ -0,0 +1,17 @@
import Redis from 'ioredis'
const client = new Redis(import.meta.env.REDIS_URI)
export const getViewsBySlug = async (slug: string) => {
if (slug) {
const prevValue = await client.get(slug);
let newValue = 1;
if (prevValue) {
newValue = parseInt(`${prevValue}`) + 1;
await client.set(slug, newValue);
} else {
await client.set(slug, 1);
}
return newValue;
}
return 0;
};

32
src/utils/views/turso.ts Normal file
View file

@ -0,0 +1,32 @@
import { createClient } from "@libsql/client";
export const client = createClient({
url: import.meta.env.TURSO_DB_URL,
authToken: import.meta.env.TURSO_DB_AUTH_TOKEN
});
export const getViewsBySlug = async (slug: string) => {
if(!slug) return 0;
try {
const initialViewCount = 0
const transaction = await client.transaction("write");
const rsSelected = await transaction.execute({
sql: 'SELECT * FROM post_stats WHERE slug = :slug',
args: { slug }
});
const prevViewCount = rsSelected?.rows?.length ? rsSelected.rows[0].views as number : initialViewCount;
const rsUpdated = await transaction.execute({
sql: 'INSERT INTO post_stats (uid, slug, views) VALUES (:uid, :slug, :views) ON CONFLICT(slug) DO UPDATE SET views = :views RETURNING views',
args: {
uid: crypto.randomUUID(),
slug,
views: prevViewCount + 1
}
});
await transaction.commit()
return rsUpdated.rows[0].views as number
} catch (e) {
console.error(e);
return 0;
}
}