Typst Rewrite Part 2

This commit is contained in:
2026-01-19 21:46:16 +01:00
parent 576fb84ce1
commit 59d80ac5b5
5 changed files with 658 additions and 43 deletions

View File

@ -0,0 +1,315 @@
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 100;
font-stretch: normal;
font-style: normal;
src: url('../fonts/WOFF2/IosevkaAile-Thin.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-Thin.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 100;
font-stretch: normal;
font-style: oblique;
src: url('../fonts/WOFF2/IosevkaAile-ThinOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ThinOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web Oblique';
font-display: swap;
font-weight: 100;
font-stretch: normal;
src: url('../fonts/WOFF2/IosevkaAile-ThinOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ThinOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 100;
font-stretch: normal;
font-style: italic;
src: url('../fonts/WOFF2/IosevkaAile-ThinItalic.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ThinItalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 200;
font-stretch: normal;
font-style: normal;
src: url('../fonts/WOFF2/IosevkaAile-ExtraLight.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ExtraLight.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 200;
font-stretch: normal;
font-style: oblique;
src: url('../fonts/WOFF2/IosevkaAile-ExtraLightOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ExtraLightOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web Oblique';
font-display: swap;
font-weight: 200;
font-stretch: normal;
src: url('../fonts/WOFF2/IosevkaAile-ExtraLightOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ExtraLightOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 200;
font-stretch: normal;
font-style: italic;
src: url('../fonts/WOFF2/IosevkaAile-ExtraLightItalic.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ExtraLightItalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 300;
font-stretch: normal;
font-style: normal;
src: url('../fonts/WOFF2/IosevkaAile-Light.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-Light.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 300;
font-stretch: normal;
font-style: oblique;
src: url('../fonts/WOFF2/IosevkaAile-LightOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-LightOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web Oblique';
font-display: swap;
font-weight: 300;
font-stretch: normal;
src: url('../fonts/WOFF2/IosevkaAile-LightOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-LightOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 300;
font-stretch: normal;
font-style: italic;
src: url('../fonts/WOFF2/IosevkaAile-LightItalic.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-LightItalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 400;
font-stretch: normal;
font-style: normal;
src: url('../fonts/WOFF2/IosevkaAile-Regular.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 400;
font-stretch: normal;
font-style: oblique;
src: url('../fonts/WOFF2/IosevkaAile-Oblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-Oblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web Oblique';
font-display: swap;
font-weight: 400;
font-stretch: normal;
src: url('../fonts/WOFF2/IosevkaAile-Oblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-Oblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 400;
font-stretch: normal;
font-style: italic;
src: url('../fonts/WOFF2/IosevkaAile-Italic.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-Italic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 500;
font-stretch: normal;
font-style: normal;
src: url('../fonts/WOFF2/IosevkaAile-Medium.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-Medium.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 500;
font-stretch: normal;
font-style: oblique;
src: url('../fonts/WOFF2/IosevkaAile-MediumOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-MediumOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web Oblique';
font-display: swap;
font-weight: 500;
font-stretch: normal;
src: url('../fonts/WOFF2/IosevkaAile-MediumOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-MediumOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 500;
font-stretch: normal;
font-style: italic;
src: url('../fonts/WOFF2/IosevkaAile-MediumItalic.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-MediumItalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 600;
font-stretch: normal;
font-style: normal;
src: url('../fonts/WOFF2/IosevkaAile-SemiBold.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-SemiBold.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 600;
font-stretch: normal;
font-style: oblique;
src: url('../fonts/WOFF2/IosevkaAile-SemiBoldOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-SemiBoldOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web Oblique';
font-display: swap;
font-weight: 600;
font-stretch: normal;
src: url('../fonts/WOFF2/IosevkaAile-SemiBoldOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-SemiBoldOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 600;
font-stretch: normal;
font-style: italic;
src: url('../fonts/WOFF2/IosevkaAile-SemiBoldItalic.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-SemiBoldItalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 700;
font-stretch: normal;
font-style: normal;
src: url('../fonts/WOFF2/IosevkaAile-Bold.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-Bold.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 700;
font-stretch: normal;
font-style: oblique;
src: url('../fonts/WOFF2/IosevkaAile-BoldOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-BoldOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web Oblique';
font-display: swap;
font-weight: 700;
font-stretch: normal;
src: url('../fonts/WOFF2/IosevkaAile-BoldOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-BoldOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 700;
font-stretch: normal;
font-style: italic;
src: url('../fonts/WOFF2/IosevkaAile-BoldItalic.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-BoldItalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 800;
font-stretch: normal;
font-style: normal;
src: url('../fonts/WOFF2/IosevkaAile-ExtraBold.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ExtraBold.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 800;
font-stretch: normal;
font-style: oblique;
src: url('../fonts/WOFF2/IosevkaAile-ExtraBoldOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ExtraBoldOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web Oblique';
font-display: swap;
font-weight: 800;
font-stretch: normal;
src: url('../fonts/WOFF2/IosevkaAile-ExtraBoldOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ExtraBoldOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 800;
font-stretch: normal;
font-style: italic;
src: url('../fonts/WOFF2/IosevkaAile-ExtraBoldItalic.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-ExtraBoldItalic.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 900;
font-stretch: normal;
font-style: normal;
src: url('../fonts/WOFF2/IosevkaAile-Heavy.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-Heavy.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 900;
font-stretch: normal;
font-style: oblique;
src: url('../fonts/WOFF2/IosevkaAile-HeavyOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-HeavyOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web Oblique';
font-display: swap;
font-weight: 900;
font-stretch: normal;
src: url('../fonts/WOFF2/IosevkaAile-HeavyOblique.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-HeavyOblique.ttf') format('truetype');
}
@font-face {
font-family: 'Iosevka Aile Web';
font-display: swap;
font-weight: 900;
font-stretch: normal;
font-style: italic;
src: url('../fonts/WOFF2/IosevkaAile-HeavyItalic.woff2') format('woff2'), url('../fonts/TTF/IosevkaAile-HeavyItalic.ttf') format('truetype');
}

View File

@ -1,4 +1,6 @@
@import url(/assets/style/fonts.css); @import url(/assets/style/fonts.css);
@import url(/assets/style/IosevkaAile.css);
:root { :root {
--bg: #282828; --bg: #282828;
--bg-accent: #504945; --bg-accent: #504945;
@ -9,12 +11,12 @@
--info: #83a598; --info: #83a598;
--warn: #fabd2f; --warn: #fabd2f;
--error: #fb4934; --error: #fb4934;
--font-main: Andika, sans-serif; --font-main: Iosevka Aile Web, sans-serif;
--font-mono: Iosevka, monospace; --font-mono: Iosevka, monospace;
} }
* { * {
font-family: var(--font-main); font-family: var(--font-main);
font-feature-settings: "ss13" on; font-weight: 300;
padding: 0pt; padding: 0pt;
margin: 0pt; margin: 0pt;
} }
@ -28,6 +30,12 @@ header {
background-color: var(--bg-accent); background-color: var(--bg-accent);
text-align: center; text-align: center;
} }
h1, h2, h3, h4, h5, h6 {
font-weight: 800;
}
strong {
font-weight: 600;
}
header h1 a { header h1 a {
text-decoration: none; text-decoration: none;
@ -59,7 +67,7 @@ a {
} }
ul.sresult { ul.sresult {
width: 50vw; width: 50vw;
height: 50vw; height: 20vw;
overflow: scroll; overflow: scroll;
justify-content: center; justify-content: center;
position: absolute; position: absolute;
@ -82,17 +90,17 @@ main {
padding: 25pt 10pt; padding: 25pt 10pt;
} }
@media (min-width: 800px) { @media (min-width: 1000px) {
#main-body {display: flex;} #main-body {display: flex;}
main { main {
width: calc(100% - 10em); width: calc(100% - 20em);
} }
aside { aside {
margin: 10pt 10pt 10pt 0pt; margin: 10pt 10pt 10pt 0pt;
width: 10em; width: 20em;
} }
} }
@media (max-width: 800px) { @media (max-width: 1000px) {
main { main {
width: 100% - 20pt width: 100% - 20pt
} }
@ -111,11 +119,6 @@ main li {
main table { main table {
margin: 1em; margin: 1em;
} }
main table, main td, main tr {
text-align: center;
border-collapse: collapse;
border: 2px solid var(--fg);
}
main td { main td {
padding: 0em 1em; padding: 0em 1em;
margin: 0; margin: 0;
@ -130,12 +133,22 @@ main .high {
font-family: inherit; font-family: inherit;
} }
main em { main em {
color: var(--accent) color: var(--accent);
}
main table, main td, main tr {
border-collapse: collapse;
margin: 10px;
border-collapse: collapse;
border: 1px solid var(--fg-disabled);
text-align: center;
}
main h2, main h3, main h4, main h5, main h6{
padding-top: 1em;
} }
/****** GLOSSES ******/ /****** GLOSSES ******/
.gloss, .gloss tr, .gloss td { .gloss, .gloss tr, .gloss td {
stroke: none;
border: none; border: none;
text-align: left; text-align: left;
padding: 0 .25em; padding: 0 .25em;

View File

@ -6,29 +6,50 @@ import { JSDOM } from "jsdom";
import { Dirent } from "node:fs"; import { Dirent } from "node:fs";
import { mkdir, stat } from "node:fs/promises"; import { mkdir, stat } from "node:fs/promises";
function out_path(path: string) { function out_path(path: string) {
} }
type LogLevel = "INFO"|"WARN"|"ERROR"|"FATAL" enum LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3,
FATAL = 4
}
let {DEBUG, INFO, WARN, ERROR, FATAL} = LogLevel
let minLogLevel : LogLevel = INFO
function log(level: LogLevel, message: any) { function log(level: LogLevel, message: any) {
const p = Bun.argv[1]?.split("/").slice(-1)[0] ?? "build script"; const p = Bun.argv[1]?.split("/").slice(-1)[0] ?? "build script";
switch (level) { switch (level) {
case "INFO": console.log(`\x1b[90m${p}: \x1b[1;42;97m [INFO] \x1b[0m ${message}`); break; case DEBUG: if (minLogLevel <= DEBUG) console.log(`\x1b[90m${p}: \x1b[1;45;97m [DEBUG] \x1b[0m ${message}`); break;
case "WARN": console.log(`\x1b[90m${p}: \x1b[1;48;5;208;97m [WARN] \x1b[0m ${message}`); break; case INFO: if (minLogLevel <= INFO) console.log(`\x1b[90m${p}: \x1b[1;44;97m [INFO] \x1b[0m ${message}`); break;
case "ERROR": console.log(`\x1b[90m${p}: \x1b[1;41;97m [ERROR] \x1b[0m ${message}`); break; case WARN: if (minLogLevel <= WARN) console.log(`\x1b[90m${p}: \x1b[1;48;5;208;97m [WARN] \x1b[0m ${message}`); break;
case "FATAL": console.log(`\x1b[90m${p}: \x1b[1;41;97m [FATAL] ${message} \x1b[0m`); process.exit(); break; case ERROR: if (minLogLevel <= ERROR) console.log(`\x1b[90m${p}: \x1b[1;41;97m [ERROR] \x1b[0m ${message}`); break;
case FATAL: /*Cannot suppress fatal*/console.log(`\x1b[90m${p}: \x1b[1;41;97m [FATAL] ${message} \x1b[0m`); process.exit(); break;
default: log(FATAL, `${level} isn't a valid log level`);
} }
} }
log(INFO, "Annwans wikis custom build script v2")
async function build_typst_file(file: Dirent) { async function build_typst_file(file: Dirent) {
let path = file.parentPath + "/" + file.name let path = file.parentPath + "/" + file.name
let dir = "public_html" + file.parentPath.slice(3) let dir = "public_html" + file.parentPath.slice(3)
let slug = path.slice(4, -4) let slug = path.slice(4, -4)
let outfile = `public_html/${slug}.html` let outfile = `public_html/${slug}.html`
log("INFO", `Building ${path}`) log(INFO, `Compiling ${slug}`)
await mkdir(dir, {recursive: true}) await mkdir(dir, {recursive: true})
await $`typst c --features html --format html --root . ${path} ${outfile}` await $`typst c --features html --format html --root . ${path} ${outfile}`
log("INFO", `Built ${path}`) log(DEBUG, `Checking if ${slug} needs post processing`)
let html = await Bun.file(outfile).text();
let heads = html.match(new RegExp("<head>.*?</head>", "sg"));
if (heads != null && heads.length >= 2) {
log(DEBUG, `Postprocessing ${slug}`)
html = html.replace(heads[1], "");
html = html.replace(heads[0], heads[1]);
await Bun.write(outfile, html);
}
log(INFO, `${slug} done`)
} }
@ -43,26 +64,30 @@ async function build_all_typst() {
} }
async function copy_assets() { async function copy_assets() {
log("INFO", "Updating assets") log(INFO, "Updating assets")
await $`rm -rf public_html/assets/` await $`rm -rf ${minLogLevel == DEBUG ? "-v" : ""} public_html/assets/`
await $`cp -r assets/ public_html/` await $`cp -r ${minLogLevel == DEBUG ? "-v" : ""} assets/ public_html/`
} }
type IndexEntry = { id: string; title: string; body: string; tags: string;} type IndexEntry = { id: string; title: string; body: string; tags: string;}
async function collect_data(f:string) : Promise<IndexEntry> { async function collect_data(f:string) : Promise<IndexEntry> {
let id = f.slice(12, -5) let id = f.slice(12, -5)
log("INFO", `collecting metadata for ${id}`) log(INFO, `collecting metadata for ${id}`)
let dom = await JSDOM.fromFile(f) let dom = await JSDOM.fromFile(f)
let body = "" log(DEBUG, `${id}: read`)
let things = dom.window.document.getElementsByTagName("main") let things = dom.window.document.getElementsByTagName("main")
let body = ""
for (let thing of things) body += thing.textContent; for (let thing of things) body += thing.textContent;
log(DEBUG, `${id}: body extracted`)
let title = dom.window.document.getElementById("search-title")?.getAttribute("content") ?? "" let title = dom.window.document.getElementById("search-title")?.getAttribute("content") ?? ""
log(DEBUG, `${id} title extracted: ${title}`)
let tags = dom.window.document.getElementById("search-tags")?.getAttribute("content") ?? "" let tags = dom.window.document.getElementById("search-tags")?.getAttribute("content") ?? ""
log(DEBUG, `${id}: tags extracted: ${tags}`)
return {id, title, body, tags} return {id, title, body, tags}
} }
async function gen_index() { async function gen_index() {
let files = await readdir("public_html", {withFileTypes: true, recursive: true}) let files = await readdir("public_html", {withFileTypes: true, recursive: true})
log(INFO, "Generating the search index");
let promises : Promise<IndexEntry>[] = []; let promises : Promise<IndexEntry>[] = [];
for (let f of files) { for (let f of files) {
if (f.isFile() && f.name.endsWith(".html")) { if (f.isFile() && f.name.endsWith(".html")) {
@ -70,6 +95,7 @@ async function gen_index() {
} }
} }
let data : IndexEntry[] = await Promise.all(promises) let data : IndexEntry[] = await Promise.all(promises)
log(INFO, `Writing index`)
await Bun.write("public_html/index.json", JSON.stringify(data), {mode: 0o644, createPath: true}) await Bun.write("public_html/index.json", JSON.stringify(data), {mode: 0o644, createPath: true})
} }
@ -79,13 +105,27 @@ const args = parseArgs({
options: { options: {
"build": {type: "boolean", short: "b"}, "build": {type: "boolean", short: "b"},
"assets": {type: "boolean", short: "a"}, "assets": {type: "boolean", short: "a"},
"index": { "index": {short: "i", type: "boolean" },
short: "i", type: "boolean" "verbose": {short: "v", type: "string"},
} "help": {type: "boolean", short: "h"}
}, },
allowPositionals: true allowPositionals: true
}).values }).values
if (args.verbose) {
try {
minLogLevel = parseInt(args.verbose);
} catch {
log(FATAL, `'${args.verbose}' isn't a number`);
}
}
if (args.help) {
log(INFO, `USAGE:`)
log(INFO, `\t${Bun.argv[1]} [-b|--build] [-a|--assets] [-i|--index] [-v ⟨n⟩|--verbose ⟨n⟩]`)
log(INFO, `\t${Bun.argv[1]} -h|--help`)
} else {
if (!args.build && !args.assets && !args.index) { if (!args.build && !args.assets && !args.index) {
await build_all_typst() await build_all_typst()
await copy_assets() await copy_assets()
@ -94,3 +134,5 @@ if (!args.build && !args.assets && !args.index) {
if (args.build) await build_all_typst(); if (args.build) await build_all_typst();
if (args.assets) await copy_assets(); if (args.assets) await copy_assets();
if (args.index) await gen_index(); if (args.index) await gen_index();
log(INFO, "DONE");
}

View File

@ -5,12 +5,16 @@
#show: conf.with(page-title: "Mosici") #show: conf.with(page-title: "Mosici")
%lang %mos %lang %mos
#set heading(numbering: "1.") #set heading(numbering: "1.")
#let low = html.span.with(class: "low")
#let gloss-opts = ( #let gloss-opts = (
txt-style: sn, txt-style: sn,
translation-style: html.span.with(class: "low"), translation-style: low
) )
#let g = gloss.with(..gloss-opts) #let g = gloss.with(..gloss-opts)
#let ex = example.with(..gloss-opts) #let ex = example.with(..gloss-opts)
#let bl = "["
#let br = "]"
#let ann = it => sub(sc(it))
= Sound ⁊ Letters <sec-sound-letters> = Sound ⁊ Letters <sec-sound-letters>
== Phonolgy <sec-phonology> == Phonolgy <sec-phonology>
Mosici has the following phonemes Mosici has the following phonemes
@ -50,7 +54,7 @@ There is also the following allophony rules:
- Plosives are realised as voiced next to other phonemically voiced consonants. - Plosives are realised as voiced next to other phonemically voiced consonants.
- Plosives are realised as non-sibilant fricatives of the same place of articulation word finally. - Plosives are realised as non-sibilant fricatives of the same place of articulation word finally.
== Coalsecence <sec-coalescence> == Coalescence <sec-coalescence>
Mosici doesn't allow consecutive vowels inside of words. To resolve would-be hiatuses, a coalescence process is used. Mosici doesn't allow consecutive vowels inside of words. To resolve would-be hiatuses, a coalescence process is used.
This process is historic for all native words, but it still current to resolve diphthongs in loan words and is necessary This process is historic for all native words, but it still current to resolve diphthongs in loan words and is necessary
@ -108,14 +112,255 @@ called the #sn[] 〈sitrapaóha〉.
sn[], [m], [/m/], sn[], [[miʎɔ]], sn[], [m], [/m/], sn[], [[miʎɔ]],
sn[], [t], [/t/], sn[], [[tɛɟjo]], sn[], [t], [/t/], sn[], [[tɛɟjo]],
) )
== Examples <ex-orthopho>
= Morphology <morphology> #sn[] ⟨tráihéins⟩ "fox"
#ex( - \*/tʀɔiːɛins/
caption: [foo], - \*/tʀ#high[øːi]ns/ #low[Coalescense 1]
txt: [ #high[ ] ], - /tʀ#high[yː]ns/ #low[Coalescense 2]
translit: ([il], high[vionreapt], high[anap], [cirtan]), - [d#high[ʁʏ̃ː]s] #low[Allophony]
phono: ([\[eẅ], high[vjõʀɛpθ], high[ãnaɸ], [ceɐ̯dã\]]),
morphemes: (sc[1s.act], high[eat#sc[.gno.pcp.pat]], high(sc[loctmp.gno]), [write] + sc[.gno.2s]), #sn[] ⟨moséceec⟩ "of islands"
translation: [#high[Whenever] I #high[eat], you write], - \*/mosikɛɛk/
lbl: "example" - /mosik#high[i]k/ #low[Coalescense]
- [mosi#high[c]i#high[ç]/ #low[Allophony]
#sn[] ⟨an·nielvc⟩ "of Nyelaf"
- \*/annjɛʟvk/
- /a#high[n]jɛʟvk/ #low[Particle shenanigans]
- [#high[ã]njɛ#high[]v#high[ɣ]] #low[Allophony]
= Morphology <morpho>
== Nouns <morpho-nouns>
=== Number <morpho-nouns-number>
Number is marked by reduplicating the last orthographic vowel of the root without the sitrapaóha #low[(see following table)]. In most cases the pronunciation of the plural isn't directly derivable from the pronunciation of the singular.
#table(columns: 2,
[*Singular*], [*Plural*],
sn[], sn[],
sn[], sn[],
sn[], sn[],
sn[], sn[],
sn[], sn[],
sn[], sn[],
sn[], sn[],
sn[], sn[],
) )
=== Cases <morpho-nouns-cases>
Nouns are also marked for case. There are 5 simple cases --- agent, patient,
genitive, dative and ablative --- and 2 compound cases --- spatial and temporal
locative.
They are 7 patterns for the simple cases based on the the coda consonant(s) of
the agent form: the concatenative pattern, and the 6 substitutive patterns
(#sn[], #sn[], #sn[], #sn[], #sn[], #sn[])
#table(
columns: 8, align: center,
[], [*C*], [*Sub* #sn[]], [*Sub* #sn[]], [*Sub* #sn[]], [*Sub* #sn[]], [*Sub* #sn[]], [*Sub* #sn[]],
[*Agent*], [], sn[], sn[], sn[], sn[], sn[], sn[],
[*Patient*], table.cell(colspan: 7, sn[]),
[*Genitive*], table.cell(colspan: 7, sn[]),
[*Dative*], table.cell(colspan: 2, sn[]), sn[], sn[], sn[],
sn[], sn[],
[*Ablative*], table.cell(colspan: 3, sn[]),table.cell(colspan: 2, sn[]),
table.cell(colspan: 2, sn[])
)
==== Agent <case-agent>
The Agent case #low(sc[age]) indicates the agent of a transitive clause, or the subject of an active intransitive clause.
#ex(
caption: [Agent case in an transitive clause],
txt: [#high[]  ],
translit: (high[loarne], [aint], [ilaalih.]),
phono: (bl + high[ʟɔɐ̯nɛ], [ɛ̃ð], [eʎɔʎeː] + br),
morphemes: (high[Loarne.#sc[age]], sc[dem.pat], [say.#sc[pst.3sa]]),
translation: [#high[Loarne] said that],
lbl: "ex-case-age-trans"
)
#ex(
caption: [Agent case in an active intransitve clause],
txt: [#high[] #high[] ],
translit: ([#high[loarne]], [i], high[mazealn],[vionreor.]),
phono: (bl + high[ʟɔɐ̯nɛ], [e], high[mazɛ̃ẅ], [vjõʀøɐ̯] + br),
morphemes: (high[Loarne.#sc[age]], [and], high[Mazealn.#sc[age]], [eat.#sc[fut.3pa]]),
translation: [#high[Loarne] and #high[Mazealn] will eat],
lbl: "ex-case-age-intrans"
)
==== Patient <case-patient>
The Patient case #low(sc[pat]) indicates the patient of a transitive clause, or the subject of a stative intransitive clause.
#ex(
caption: [Patient case in a transitive clause#footnote[Note that while the pronoun is dropped thanks to the verb conjugation, but the clause is still transitive]<fn-pat-trans>],
txt: [#high[] ],
translit: (high[nriiht],[vionreef.]),
phono: (bl + high[nʁiːθ], [vjõʀiɸ] + br),
morphemes: (high[grain#sc[.pl.pat]], [eat#sc[.prs.1s]]),
translation: [I eat #high[grains]],
lbl: "ex-case-pat-trans"
)
#ex(
caption: [Patient case in an intransitive clause],
txt: [#high[] ],
translit: (high[rent], [oítas.]),
phono: (bl + high[ʀɛ̃ð], [ytas] + br),
morphemes: (high[house#sc[.pat]], [be\_tall#sc[gno.3si]]),
translation: [#high[The house] is tall],
lbl: "ex-case-pat-intrans"
)
==== Genitive <case-genitive>
The genitive case #low(sc[gen]) indicates poessesion or qualification. Note that in the case of qualification, the formation of a compound is also possible. Note: Compounding is not subject to coalescence.
#ex(
caption: [Possessive genitive case],
txt: [#high[] ],
translit: (high[ilc], [ren]),
phono: (bl + high[eẅɣ], [ʀɛ̃] + br),
morphemes: (high(sc[1s.gen]), [house#sc[.age]]),
translation: [#high[my] house],
lbl: "ex-case-gen-poss"
)
#ex(
caption: [Qualificative genitive case],
txt: [#high[] ],
translit: (high[áhioc], [isove]),
phono: (bl + high[øːx], [esovɛ] + br),
morphemes: (high[fiction.#sc[gen]], [job#sc[.age]]),
translation: [a #high[fictional] job],
lbl: "ex-case-gen-qual"
)
#ex(
caption: [Compounding],
txt: [],
translit: ([#sn[]\ áhio-], [#sn[]\ isove]),
phono: (bl + [øː-], [esovɛ] + br),
morphemes: ([fiction-], [job#sc[.age]]),
translation: [a fictional job],
lbl: "ex-case-gen-compound"
)
==== Dative <case-dative>
The dative case #low(sc[dat]) indicates the beneficiary of a ditransitive verbs, as well as indicating a direction faced. Note hover that it isn't used with verbs of movement towards something, for those use the patient case (see @case-patient) instead, the dative instead indicates the means of displacement.
#ex(
caption: [Dative in ditransitive clauses],
txt: [#high[]  ],
translit: (high[elsi], [rent], [siehíef]),
phono: (bl + high[ɛẅɕ], [ʀɛ̃ð], [ɕjɛːf] + br),
morphemes: (high(sc[2s.dat]), [house#sc[.pat]], [give.#sc[pres.1s]]),
translation: [I give #high[you] a house],
lbl: "ex-case-dat-ditrans"
)
#ex(
caption: [Dative as means of displacement for verbs of movement towards],
txt: [ #high[] ],
translit: ([an·sialmosécet], high[foítztselasi], [oisailin.]),
phono: (bl + [ãɕaẅmosicɛθ], high[fydztsɛʟaɕ], [øsɛʎẽ] + br),
morphemes: ([#sc[ppn-]Chalmosique#sc[.dat]], high[train.#sc[dat]], [go#sc[.pst.2s]]),
translation: [You#ann[sg] went to Chalmosique #high[by train]],
lbl: "ex-case-dat-mot"
)
#ex(
caption: [Dative as an allative substitute for other verbs],
txt: [#high[]   ],
translit: (high[an·cairniasialsi], [an·sialmosécet], [foítztselavia], [vilinef.]),
phono: (bl + high[ãkɛɐ̯nɛɕaẅɕ], [ãɕaẅmosicɛθ], [fydztsɛʟavja], [veʎẽnɛf] + br),
morphemes: (high[#sc[ppn-]Quernechal#sc[.dat]], [#sc[ppn-]Chalmosique#sc[.pat]], [train.#sc[abl]], [leave.#sc[prs.1s]]),
translation: [I leave Chalmosique by train #high[to Quernechal].],
lbl: "ex-case-dat-all"
)
==== Ablative <case-abl>
The ablative case #low(sc[abl]) indicates the provenance of the action. It is also used as an instrumental, indicating the means by which the action is done.
For verbs of movement away from something, it only has it's instrumental meaning, the source is indicated by the patient case (see @case-patient). For verbs of movement towards something, the ablative doesn't have the instrumental meaning, for that role use the dative instead.
#ex(caption: [Ablative case in an ablative meaning],
txt: [#high[]  ],
translit: (high[an·sialmosécevia], [nriiht], [vionreef.]),
phono: (bl + [ãɕaẅmosicɛvja], [nʁiːθ], [vjõʀiɸ] + br),
morphemes: (high[#sc[ppn]-Chalmosique.#sc[abl]], [grain#sc[.pl.pat]], [eat#sc[.prs.1s]]),
translation: [I eat grains #high[from Chalmosique]],
lbl: "ex-case-abl-abl"
)
#ex(caption: [Ablative case as instrumental],
txt: [#high[] ],
translit: (high[hoévenvia], [cirtív.]),
phono: (bl + high[yvɛ̃vja], [ceɐ̯div] + br),
morphemes: (high[pen#sc[.abl]], [write#sc[.past.1pe]]),
translation: [We#ann[excl] wrote #high[with a pen].],
lbl: "ex-case-abl-inst"
)
==== Spatial Locative <case-spaloc>
The spatial locative cases #low(sc[sploc]) is used to indicate a spacial location. it is marked by expressing the noun in the genitive case, followed by the #sn[] ⟨la⟩ particle.
#ex(caption: [Spatial Locative],
txt: [ #high[ ]],
translit: ([il], high[an·vansterilc‿la] + [.]),
phono: (bl + [eẅ], [ãvãstɛʀeẅʝ ʟa] + br),
morphemes: (sc[1s.age], [#sc[ppn-]Vansterel#sc[.gen‿sploc]]),
translation: [We#ann[excl] wrote #high[with a pen].],
lbl: "ex-case-sploc"
)
==== Temporal Locative <case-temploc>
There are four temporal locatives: past, present, future and gnomic #low(sc[tmploc.pst, tmploc.prs, tmploc.fut] + [ and ] + sc[tmploc.gno]). The past case (resp. present and future) is used, as its name indicates, to locate events that happened in the past (resp. present and future). The gnomic case locates events that are either generally happeneing, happening at an unknown point in time, or happening repeatedly.
The past case (resp. present, future, gnomic) are indicated by expressing the noun in the oblique followed by the particle #sn[] ⟨anip⟩ (resp #sn[] ⟨anep⟩, #sn[] ⟨anop⟩ and #sn[] ⟨anap⟩)
However the present case is rarely used outside of set phrases like #sn[ ] “today” or #sn[ ] “now”
#ex(caption: [Present temporal locative],
txt: [#high[ ] ],
translit: (high[vint‿anep], [mareves.]),
phono: (bl + high[vẽð ãneɸ], [maʀɛvɛs] + br),
morphemes: (high[day.#sc[pat‿tmploc.prs]], [be_cold.#sc[prs.3si]]),
translation: [Its cold #high[today]],
lbl: "ex-case-tmploc-prs"
)
#ex(
caption: [Past temporal locative],
txt: [ #high[ ] ],
translit: ([il], high[vionreipt‿anip], [,], [cirtin.]),
phono: (bl + [eẅ], high[vjõʀipθ ãneɸ], [|], [ceɐ̯dẽ]+ br),
morphemes: (sc[1s.act], high[eat#sc[.pst.pcp.pat‿tmploc.pst]], [|], [write] + sc[.pst.2s]),
translation: [#high[When] I #high[ate], you#ann[sg] wrote],
lbl: "ex-case-tmploc-pst"
)
#ex(
caption: [Future temporal locative],
txt: [ #high[ ] ],
translit: ([il], high[vionreapt‿anop], [,], [cirton.]),
phono: (bl + [eẅ], high[vjõʀøpθ ãnoɸ], [|], [ceɐ̯dõ]+ br),
morphemes: (sc[1s.act], high[eat#sc[.fut.pcp.pat‿tmploc.fut]], [|], [write] + sc[.fut.2s]),
translation: [#high[When] I#high[ll eat], you#ann[sg]ll write],
lbl: "ex-case-tmploc-fut"
)
#ex(
caption: [Gnomic temporal locative],
txt: [ #high[ ] ],
translit: ([il], high[vionreapt‿anap], [,], [cirtan.]),
phono: (bl + [eẅ], high[vjõʀɛpθ ãnaɸ], [|], [ceɐ̯dã]+ br),
morphemes: (sc[1s.act], high[eat#sc[.gno.pcp.pat‿tmploc.gno]], [|], [write] + sc[.gno.2s]),
translation: [#high[Whenever] I #high[eat], you#ann[sg] write],
lbl: "ex-case-tmploc-gno"
)
=== Proper noun clitic <morpho-ppn>
== Pronouns <morpho-pronouns>
== Verbs <morpho-verbs>

View File

@ -20,7 +20,7 @@
show figure.where(kind: "gloss"): set figure(supplement: [Example]) show figure.where(kind: "gloss"): set figure(supplement: [Example])
show figure.where(kind: "gloss"): set figure.caption(position: top) show figure.where(kind: "gloss"): set figure.caption(position: top)
show figure: it => { show figure: it => {
html.details({ html.details(open: it.kind == image or it.kind == table, {
html.summary[*#it.caption.supplement #it.caption.counter.display(it.numbering)*: #it.caption.body] html.summary[*#it.caption.supplement #it.caption.counter.display(it.numbering)*: #it.caption.body]
it.body it.body
}) })