Implement custom yaml loader

This commit is contained in:
ryan 2025-09-23 10:37:27 -07:00
parent c5a73d0ccb
commit 24ad55bb57
18 changed files with 503 additions and 250 deletions

View File

@ -1,34 +1,50 @@
// @ts-check // @ts-check
import { defineConfig } from "astro/config"; import { defineConfig } from "astro/config";
import starlight from "@astrojs/starlight"; import starlight from "@astrojs/starlight";
import YAML from "yaml";
import fs from "fs";
const vocabListFile = fs.readFileSync("src/vocab_list.yaml", "utf8");
const vocabListJson = YAML.parse(vocabListFile);
const categories = vocabListJson.map(
(/** @type {{ slug: string }} */ category) => category.slug,
);
const vocabList = [
{
label: "Index",
link: `/vocab/index`,
},
].concat(
categories.map((/** @type {string} */ category) => ({
label: category.charAt(0).toUpperCase() + category.slice(1),
link: `/vocab/${category}`,
})),
);
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
vite: {
resolve: {
alias: {
"@": "/src",
},
},
},
integrations: [ integrations: [
starlight({ starlight({
title: "हिंदकी", title: "हिंदकी",
social: [
{
icon: "github",
label: "GitHub",
href: "https://github.com/withastro/starlight",
},
],
customCss: [ customCss: [
// Path to your custom CSS file // Path to your custom CSS file
"./src/styles/custom.css", "./src/styles/custom.css",
], ],
sidebar: [ sidebar: [
{ {
label: "Guides", label: "Vocabulary",
items: [ items: vocabList,
// Each item here is one entry in the navigation menu.
{ label: "Example Guide", slug: "guides/example" },
],
}, },
{ {
label: "Reference", label: "Grammar",
autogenerate: { directory: "reference" }, autogenerate: { directory: "grammar" },
}, },
], ],
}), }),

15
package-lock.json generated
View File

@ -10,7 +10,8 @@
"dependencies": { "dependencies": {
"@astrojs/starlight": "^0.36.0", "@astrojs/starlight": "^0.36.0",
"astro": "^5.6.1", "astro": "^5.6.1",
"sharp": "^0.34.2" "sharp": "^0.34.2",
"yaml": "^2.8.1"
}, },
"devDependencies": { "devDependencies": {
"prettier": "^3.6.2", "prettier": "^3.6.2",
@ -6296,6 +6297,18 @@
"integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/yaml": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
}
},
"node_modules/yargs-parser": { "node_modules/yargs-parser": {
"version": "21.1.1", "version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",

View File

@ -12,7 +12,8 @@
"dependencies": { "dependencies": {
"@astrojs/starlight": "^0.36.0", "@astrojs/starlight": "^0.36.0",
"astro": "^5.6.1", "astro": "^5.6.1",
"sharp": "^0.34.2" "sharp": "^0.34.2",
"yaml": "^2.8.1"
}, },
"devDependencies": { "devDependencies": {
"prettier": "^3.6.2", "prettier": "^3.6.2",

View File

@ -0,0 +1,50 @@
---
import {Badge} from '@astrojs/starlight/components';
interface Props {
word: {
english: string;
hindi: string;
gender?: "m" | "f";
note?: string;
examples: Array<{
hindi: string;
english: string;
}>
}
}
const {word}: Props = Astro.props;
const gender_lookup: Record<"m" | "f", ["note" | "tip", string]> = {
"m": ["note", "male"],
"f": ["tip", "female"],
};
function highlight(text: string, term: string) {
const regex = new RegExp(`(${term})`, 'gi');
const parts = text.split(regex);
return parts.map((part) => regex.test(part) ? `<mark>${part}</mark>` : part).join('');
}
---
<li class="word-entry">
<h4>
{word.hindi} ({word.english})
{word.gender && <Badge variant={gender_lookup[word.gender][0]} text={gender_lookup[word.gender][1]} class="gender-badge"/>}
</h4>
{word.note &&
<div>
{word.note}
</div>
}
{
word.examples &&
<div>
For example,
{word.examples.map((e) =>
<p>
<span set:html={highlight(e.hindi, word.hindi)}></span> - <span set:html={highlight(e.english, word.english)}></span>
</p>
)}
</div>
</li>

View File

@ -1,7 +1,32 @@
import { defineCollection } from 'astro:content'; import { defineCollection } from "astro:content";
import { docsLoader } from '@astrojs/starlight/loaders'; import { docsLoader } from "@astrojs/starlight/loaders";
import { docsSchema } from '@astrojs/starlight/schema'; import { docsSchema } from "@astrojs/starlight/schema";
import { file } from "astro/loaders";
import { z } from "zod";
export const collections = { export const collections = {
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
vocabList: defineCollection({
loader: file("src/vocab_list.yaml"),
schema: z.object({
about: z.string().optional(),
words: z.array(
z.object({
type: z.string(),
english: z.string(),
hindi: z.string(),
gender: z.enum(["m", "f"]).optional(),
note: z.string().optional(),
examples: z
.array(
z.object({
english: z.string(),
hindi: z.string(),
}),
)
.optional(),
}),
),
}),
}),
}; };

View File

@ -0,0 +1,13 @@
---
title: Grammar Introduction
---
Yo yo.
## Here's an h2.
Stuff.
### Here's an h3.
More shit.

View File

@ -8,8 +8,8 @@ head:
hero: hero:
tagline: Welcome to Hindki! tagline: Welcome to Hindki!
actions: actions:
- text: Dictionary - text: Jump In
link: /dictionary/index/ link: /vocabulary
icon: right-arrow icon: right-arrow
- text: Hindi Textbook (260 MB) - text: Hindi Textbook (260 MB)
link: https://files.pandu.ski/share/2BfsIfEb link: https://files.pandu.ski/share/2BfsIfEb

View File

@ -0,0 +1,14 @@
---
title: Dates and Times
---
When specifying a point in time, generally, को is used, e.g. रात को, सोमवार को. However, for some reason, सुबह में is used instead.
## General words
Words for the various units of date and time.
Starting off strong with:
- तारिख / _Date_
- **Time** / समय

View File

@ -0,0 +1,10 @@
---
title: Education
---
Learning about learning.
## Roles
- शिक्षक / _teacher_
- चात्र / _student_

View File

@ -0,0 +1,11 @@
---
title: Food and Drink
---
जो खाएं और पिये जाते हैं.
## Nouns
- <VocabWord hindi="अंडा" english="egg" gender="m" />
## Verbs

View File

@ -0,0 +1,7 @@
---
title: Index
---
## Welcome to the Hindki Docs!
Test test test.

View File

@ -0,0 +1,13 @@
---
title: Media
---
import VocabWord from "@/components/VocabWord.astro";
जो पढ़ीं और देखीं जाते हैं.
## Reading words
- <VocabWord english="newspaper" hindi="अखबार" gender="m" />
## Viewing words

View File

@ -0,0 +1,9 @@
---
title: Elements and Substances
---
Stuff in the world.
## Elements
- आग / _fire_

View File

@ -0,0 +1,63 @@
---
import { getCollection, getEntry } from 'astro:content';
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import VocabWord from '@/components/VocabWord.astro';
function titlecase(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
export async function getStaticPaths() {
const vocabList = await getCollection("vocabList");
return vocabList.map((category) => ({
params: { category: category.id },
}));
}
const { category } = Astro.params;
const entry = await getEntry("vocabList", category);
entry!.id = titlecase(entry!.id);
const wordtypes = [...new Set(entry!.data.words.map((word) => word.type))];
const wordsByType = wordtypes.map((type) => ({
type: titlecase(type) + "s",
words: entry!.data.words.filter((word) => word.type === type),
}));
const headings = wordsByType.map(({type, words}) => {
return [{
text: type,
depth: 2,
slug: type.toLowerCase().replace(/\s+/g, '-'),
}].concat(words.map((word) => ({
text: word.english,
depth: 3,
slug: word.english.toLowerCase().replace(/\s+/g, '-'),
})));
})[0];
---
<StarlightPage
frontmatter={{ title: "Vocabulary: " + (entry?.id ?? "Uncategorized") }}
headings={headings}
>
<div>
{ entry?.data.about}
</div>
{
wordsByType.map(({type, words}) => (
<div class="word-type-section">
<h3>{type}</h3>
<ul class="part-of-speech-list">
{
words.map((word) => (
<VocabWord {word} />
))
}
</ul>
</div>
))
}
</StarlightPage>

View File

@ -1,4 +1,8 @@
/* MinionPro (Default serif font) */ /* =================================================================
FONT DECLARATIONS
================================================================= */
/* MinionPro (Serif font) */
@font-face { @font-face {
font-family: "MinionPro"; font-family: "MinionPro";
src: url("/fonts/MinionPro_Regular.otf") format("opentype"); src: url("/fonts/MinionPro_Regular.otf") format("opentype");
@ -31,7 +35,7 @@
font-display: swap; font-display: swap;
} }
/* MyriadPro (Default sans-serif font) */ /* MyriadPro (Sans-serif font) */
@font-face { @font-face {
font-family: "MyriadPro"; font-family: "MyriadPro";
src: url("/fonts/MyriadPro_Regular.otf") format("opentype"); src: url("/fonts/MyriadPro_Regular.otf") format("opentype");
@ -64,99 +68,11 @@
font-display: swap; font-display: swap;
} }
/* Custom font for Devanagari script - Multiple weights */ /* Devanagari fonts with auto-detection via unicode-range */
@font-face {
font-family: "hindi";
src: url("/fonts/Martel-Light.ttf") format("truetype");
font-weight: 300;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "hindi";
src: url("/fonts/Martel-Regular.ttf") format("truetype");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "hindi";
src: url("/fonts/Martel-SemiBold.ttf") format("truetype");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "hindi";
src: url("/fonts/Martel-Bold.ttf") format("truetype");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "hindi";
src: url("/fonts/Martel-Black.ttf") format("truetype");
font-weight: 900;
font-style: normal;
font-display: swap;
}
/* Apply default fonts and Devanagari font to content */
:root {
--sl-font-system: "MyriadPro", system-ui, sans-serif;
--sl-font: "MyriadPro", system-ui, sans-serif;
}
/* Set global font default to sans-serif */
body {
font-family: "MyriadPro", system-ui, sans-serif;
}
/* Apply serif font to content/reading areas */
p,
.sl-markdown-content p,
blockquote,
.sl-markdown-content blockquote,
li,
.sl-markdown-content li,
.content,
.prose,
.sl-markdown-content {
font-family: "MinionPro", Georgia, serif;
font-size: 1.5rem; /* 18px, up from default 16px */
}
/* Keep UI elements as sans-serif with Devanagari support */
.sl-nav,
.sl-sidebar,
.sl-page-title,
.card,
.starlight-card,
button,
.sl-search-box,
nav,
header,
footer {
font-family: "DevanagariAuto", "MyriadPro", system-ui, sans-serif;
}
/* Specific targeting for Devanagari text */
[lang="hi"],
[lang="sa"],
.devanagari {
font-family: "hindi", "Noto Sans Devanagari", sans-serif !important;
}
/* Auto-detect Devanagari characters using CSS unicode-range approach */
/* This creates a fallback system that will use the hindi font for Devanagari characters */
@font-face { @font-face {
font-family: "DevanagariAuto"; font-family: "DevanagariAuto";
src: url("/fonts/Martel-Regular.ttf") format("truetype"); src: url("/fonts/Martel-Light.ttf") format("truetype");
font-weight: 400; font-weight: 300;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
unicode-range: unicode-range:
@ -165,8 +81,8 @@ footer {
@font-face { @font-face {
font-family: "DevanagariAuto"; font-family: "DevanagariAuto";
src: url("/fonts/Martel-Light.ttf") format("truetype"); src: url("/fonts/Martel-Regular.ttf") format("truetype");
font-weight: 300; font-weight: 400;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
unicode-range: U+0900-097F, U+200C-200D; unicode-range: U+0900-097F, U+200C-200D;
@ -199,146 +115,38 @@ footer {
unicode-range: U+0900-097F, U+200C-200D; unicode-range: U+0900-097F, U+200C-200D;
} }
/* Update font stacks to include automatic Devanagari detection */ /* =================================================================
body { CSS CUSTOM PROPERTIES (VARIABLES)
font-family: "DevanagariAuto", "MyriadPro", system-ui, sans-serif; ================================================================= */
}
/* Apply serif font to content/reading areas with Devanagari support */
p,
.sl-markdown-content p,
blockquote,
.sl-markdown-content blockquote,
li,
.sl-markdown-content li,
.content,
.prose,
.sl-markdown-content {
font-family: "DevanagariAuto", "MinionPro", Georgia, serif;
font-size: 1.5rem; /* 18px, up from default 16px */
}
/* Alternative approach: Apply to all text content */
/* Uncomment if you want the Devanagari font to be the primary font */
/*
body {
font-family: 'DevanagariFont', 'Noto Sans Devanagari', system-ui, sans-serif;
}
*/
/* Ensure proper rendering for complex scripts */
.devanagari,
[lang="hi"],
[lang="sa"] {
font-feature-settings:
"kern" 1,
"liga" 1;
text-rendering: optimizeLegibility;
}
/* Font weight utility classes */
.hindi-light {
font-family: "hindi", "Noto Sans Devanagari", sans-serif;
font-weight: 300;
}
.hindi-regular {
font-family: "hindi", "Noto Sans Devanagari", sans-serif;
font-weight: 400;
}
.hindi-semibold {
font-family: "hindi", "Noto Sans Devanagari", sans-serif;
font-weight: 600;
}
.hindi-bold {
font-family: "hindi", "Noto Sans Devanagari", sans-serif;
font-weight: 700;
}
.hindi-black {
font-family: "hindi", "Noto Sans Devanagari", sans-serif;
font-weight: 900;
}
/* Override Starlight's heading fonts */
h1,
.sl-markdown-content h1 {
font-family: "DevanagariAuto", "MyriadPro", system-ui, sans-serif;
font-weight: 700; /* Bold */
}
h2,
.sl-markdown-content h2 {
font-family: "DevanagariAuto", "MyriadPro", system-ui, sans-serif;
font-weight: 700; /* Bold */
}
h3,
.sl-markdown-content h3,
h4,
.sl-markdown-content h4 {
font-family: "DevanagariAuto", "MyriadPro", system-ui, sans-serif;
font-weight: 700; /* Bold */
}
h5,
.sl-markdown-content h5,
h6,
.sl-markdown-content h6 {
font-family: "DevanagariAuto", "MyriadPro", system-ui, sans-serif;
font-weight: 400; /* Regular */
}
/* Font family utility classes */
.minion,
.serif {
font-family: "DevanagariAuto", "MinionPro", Georgia, serif;
}
.myriad,
.sans {
font-family: "DevanagariAuto", "MyriadPro", system-ui, sans-serif;
}
/* Font weight utilities for main fonts */
.minion-regular,
.serif-regular {
font-family: "DevanagariAuto", "MinionPro", Georgia, serif;
font-weight: 400;
}
.minion-bold,
.serif-bold {
font-family: "DevanagariAuto", "MinionPro", Georgia, serif;
font-weight: 700;
}
.myriad-regular,
.sans-regular {
font-family: "DevanagariAuto", "MyriadPro", system-ui, sans-serif;
font-weight: 400;
}
.myriad-bold,
.sans-bold {
font-family: "DevanagariAuto", "MyriadPro", system-ui, sans-serif;
font-weight: 700;
}
/* Custom background colors for a more paper-like feel */
:root { :root {
/* Font stacks */
--font-sans: "DevanagariAuto", "MyriadPro", system-ui, sans-serif;
--font-serif: "DevanagariAuto", "MinionPro", Georgia, serif;
--font-devanagari: "DevanagariAuto", "Noto Sans Devanagari", sans-serif;
/* Starlight overrides */
--sl-font-system: var(--font-sans);
--sl-font: var(--font-sans);
/* Light theme - warm paper-like background */ /* Light theme - warm paper-like background */
--sl-color-bg: #fefcf8; /* Warm off-white, like aged paper */ --sl-color-bg: #fefcf8; /* Warm off-white, like aged paper */
--sl-color-bg-nav: #faf8f4; /* Slightly darker for navigation */ --sl-color-bg-nav: #faf8f4; /* Slightly darker for navigation */
--sl-color-bg-sidebar: #f8f6f2; /* Even softer for sidebar */ --sl-color-bg-sidebar: #f8f6f2; /* Even softer for sidebar */
--sl-color-bg-inline-code: #f4f2ee; /* Subtle for inline code */ --sl-color-bg-inline-code: #f4f2ee; /* Subtle for inline code */
/* Custom accent color for links and buttons */ /* Custom accent color */
--sl-color-accent: #336699; --sl-color-accent: #336699;
--sl-color-accent-low: #336699; --sl-color-accent-low: #336699;
--sl-color-accent-high: #336699; --sl-color-accent-high: #336699;
/* Gender badge colors */
/* Cyan for masculine */
--sl-badge-note-bg: #3399cc;
--sl-badge-note-border: #3388bb;
/* Magenta for feminine */
--sl-badge-tip-bg: #cc3366;
--sl-badge-tip-border: #bb3355;
} }
/* Dark theme overrides */ /* Dark theme overrides */
@ -348,18 +156,185 @@ h6,
--sl-color-bg-sidebar: #16140f; --sl-color-bg-sidebar: #16140f;
--sl-color-bg-inline-code: #2a2620; --sl-color-bg-inline-code: #2a2620;
/* Keep the same accent color in dark theme */
--sl-color-accent: #336699; --sl-color-accent: #336699;
--sl-color-accent-low: #336699; --sl-color-accent-low: #336699;
--sl-color-accent-high: #336699; --sl-color-accent-high: #336699;
} }
/* Additional paper-like styling */ /* =================================================================
BASE STYLES
================================================================= */
body { body {
font-family: var(--font-sans);
background-color: var(--sl-color-bg); background-color: var(--sl-color-bg);
} }
/* Subtle texture effect (optional) */ /* Content areas use serif font */
p,
.sl-markdown-content p,
blockquote,
.content,
.prose,
.sl-markdown-content {
font-family: var(--font-serif);
font-size: 1.5rem; /* 18px, up from default 16px */
}
/* UI elements remain sans-serif */
.sl-nav,
.sidebar-content,
.sl-page-title,
.card,
.starlight-card,
button,
.sl-search-box,
nav,
header,
footer {
font-family: var(--font-sans);
}
/* Headings */
h1,
.sl-markdown-content h1,
h2,
.sl-markdown-content h2,
h3,
.sl-markdown-content h3,
h4,
.sl-markdown-content h4 {
font-family: var(--font-sans);
font-weight: 700;
}
h5,
.sl-markdown-content h5,
h6,
.sl-markdown-content h6 {
font-family: var(--font-sans);
font-weight: 400;
}
.gender-badge {
margin-left: 0.5rem;
vertical-align: middle;
font-family: var(--font-sans);
}
.word-entry {
margin-bottom: 2em;
}
/* =================================================================
LANGUAGE-SPECIFIC STYLES
================================================================= */
/* Devanagari text optimization */
[lang="hi"],
[lang="sa"],
.devanagari {
font-family: var(--font-devanagari) !important;
font-feature-settings:
"kern" 1,
"liga" 1;
text-rendering: optimizeLegibility;
}
/* =================================================================
UTILITY CLASSES
================================================================= */
/* Font family utilities */
.serif {
font-family: var(--font-serif);
}
.sans {
font-family: var(--font-sans);
}
.devanagari-font {
font-family: var(--font-devanagari);
}
/* Font weight utilities */
.font-light {
font-weight: 300;
}
.font-regular {
font-weight: 400;
}
.font-semibold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.font-black {
font-weight: 900;
}
/* Combined font family + weight utilities */
.serif-regular {
font-family: var(--font-serif);
font-weight: 400;
}
.serif-bold {
font-family: var(--font-serif);
font-weight: 700;
}
.sans-regular {
font-family: var(--font-sans);
font-weight: 400;
}
.sans-bold {
font-family: var(--font-sans);
font-weight: 700;
}
/* Devanagari-specific weight utilities */
.hindi-light {
font-family: var(--font-devanagari);
font-weight: 300;
}
.hindi-regular {
font-family: var(--font-devanagari);
font-weight: 400;
}
.hindi-semibold {
font-family: var(--font-devanagari);
font-weight: 600;
}
.hindi-bold {
font-family: var(--font-devanagari);
font-weight: 700;
}
.hindi-black {
font-family: var(--font-devanagari);
font-weight: 900;
}
/* Legacy aliases */
.minion-regular {
font-family: var(--font-serif);
font-weight: 400;
}
.minion-bold {
font-family: var(--font-serif);
font-weight: 700;
}
.myriad-regular {
font-family: var(--font-sans);
font-weight: 400;
}
.myriad-bold {
font-family: var(--font-sans);
font-weight: 700;
}
/* =================================================================
THEME ENHANCEMENTS
================================================================= */
.sl-markdown-content, .sl-markdown-content,
main { main {
background-color: var(--sl-color-bg); background-color: var(--sl-color-bg);

10
src/types.ts Normal file
View File

@ -0,0 +1,10 @@
export interface VocabWord {
type: "noun" | "verb" | "adjective" | "adverb" | "phrase" | "other";
english: string;
hindi: string;
gender?: "m" | "f";
}
export interface VocabCategory {
slug: string;
words: VocabWord[];
}

17
src/vocab_list.yaml Normal file
View File

@ -0,0 +1,17 @@
- slug: general
about: Common everyday vocabulary words.
words:
- type: noun
english: thing
hindi: बात
gender: f
note: More abstract than "चीज़", often used for matters, topics, or affairs.
see_also:
- hindi: चीज़
examples:
- english: "No problem. (Literally: (there is) nothing.)"
hindi: कोई बात नहीं.
- type: noun
english: help
hindi: मदद
gender: f

View File

@ -1,5 +1,11 @@
{ {
"extends": "astro/tsconfigs/strict", "extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"], "include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"] "exclude": ["dist"],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
} }