commit 576fb84ce17b730938092a7eece5b487ce624e74 Author: Annwan Date: Sun Jan 18 16:45:36 2026 +0100 typst rewrite part 1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08ffe2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# dependencies for build.ts and other related nonsense +node_modules +bun.lock + + +# VSCode configuration +.vscode + +# Generated output +public_html diff --git a/assets/scripts/fuse.min.js b/assets/scripts/fuse.min.js new file mode 100644 index 0000000..0d509f3 --- /dev/null +++ b/assets/scripts/fuse.min.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v7.1.0 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2025 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:{},n=t.getFn,i=void 0===n?O.getFn:n,u=t.fieldNormWeight,o=void 0===u?O.fieldNormWeight:u;r(this,e),this.norm=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:3,n=new Map,r=Math.pow(10,t);return{get:function(t){var i=t.match(j).length;if(n.has(i))return n.get(i);var u=1/Math.pow(i,.5*e),o=parseFloat(Math.round(u*r)/r);return n.set(i,o),o},clear:function(){n.clear()}}}(o,3),this.getFn=i,this.isCreated=!1,this.setIndexRecords()}return u(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,A(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();A(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,i=void 0===r?O.getFn:r,u=n.fieldNormWeight,o=void 0===u?O.fieldNormWeight:u,c=new I({getFn:i,fieldNormWeight:o});return c.setKeys(e.map(w)),c.setSources(t),c.create(),c}function R(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.errors,r=void 0===n?0:n,i=t.currentLocation,u=void 0===i?0:i,o=t.expectedLocation,c=void 0===o?0:o,a=t.distance,s=void 0===a?O.distance:a,h=t.ignoreLocation,l=void 0===h?O.ignoreLocation:h,f=r/e.length;if(l)return f;var d=Math.abs(c-u);return s?f+d/s:d?1:f}var N=32;function P(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},i=r.location,u=void 0===i?O.location:i,o=r.distance,c=void 0===o?O.distance:o,a=r.threshold,s=void 0===a?O.threshold:a,h=r.findAllMatches,l=void 0===h?O.findAllMatches:h,f=r.minMatchCharLength,d=void 0===f?O.minMatchCharLength:f,v=r.includeMatches,g=void 0===v?O.includeMatches:v,y=r.ignoreLocation,p=void 0===y?O.ignoreLocation:y;if(t.length>N)throw new Error("Pattern length exceeds max of ".concat(N,"."));for(var A,m=t.length,C=e.length,k=Math.max(0,Math.min(u,C)),E=s,F=k,M=d>1||g,b=M?Array(C):[];(A=e.indexOf(t,F))>-1;){var D=R(t,{currentLocation:A,expectedLocation:k,distance:c,ignoreLocation:p});if(E=Math.min(D,E),F=A+m,M)for(var B=0;B=$;z-=1){var T=z-1,K=n[e.charAt(T)];if(M&&(b[T]=+!!K),W[z]=(W[z+1]<<1|1)&K,_&&(W[z]|=(x[z+1]|x[z])<<1|1|x[z+1]),W[z]&L&&(w=R(t,{errors:_,currentLocation:T,expectedLocation:k,distance:c,ignoreLocation:p}))<=E){if(E=w,(F=T)<=k)break;$=Math.max(1,2*k-F)}}if(R(t,{errors:_+1,currentLocation:k,expectedLocation:k,distance:c,ignoreLocation:p})>E)break;x=W}var q={isMatch:F>=0,score:Math.max(.001,w)};if(M){var J=function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:O.minMatchCharLength,n=[],r=-1,i=-1,u=0,o=e.length;u=t&&n.push([r,i]),r=-1)}return e[u-1]&&u-r>=t&&n.push([r,u-1]),n}(b,d);J.length?g&&(q.indices=J):q.isMatch=!1}return q}function W(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},u=i.location,o=void 0===u?O.location:u,c=i.threshold,a=void 0===c?O.threshold:c,s=i.distance,h=void 0===s?O.distance:s,l=i.includeMatches,f=void 0===l?O.includeMatches:l,d=i.findAllMatches,v=void 0===d?O.findAllMatches:d,g=i.minMatchCharLength,y=void 0===g?O.minMatchCharLength:g,p=i.isCaseSensitive,A=void 0===p?O.isCaseSensitive:p,m=i.ignoreDiacritics,C=void 0===m?O.ignoreDiacritics:m,k=i.ignoreLocation,E=void 0===k?O.ignoreLocation:k;if(r(this,e),this.options={location:o,threshold:a,distance:h,includeMatches:f,findAllMatches:v,minMatchCharLength:y,isCaseSensitive:A,ignoreDiacritics:C,ignoreLocation:E},t=A?t:t.toLowerCase(),t=C?z(t):t,this.pattern=t,this.chunks=[],this.pattern.length){var F=function(e,t){n.chunks.push({pattern:e,alphabet:W(e),startIndex:t})},M=this.pattern.length;if(M>N){for(var b=0,D=M%N,B=M-D;b1&&void 0!==arguments[1]?arguments[1]:{},o=u.location,c=void 0===o?O.location:o,a=u.threshold,s=void 0===a?O.threshold:a,h=u.distance,l=void 0===h?O.distance:h,f=u.includeMatches,d=void 0===f?O.includeMatches:f,v=u.findAllMatches,g=void 0===v?O.findAllMatches:v,y=u.minMatchCharLength,p=void 0===y?O.minMatchCharLength:y,A=u.isCaseSensitive,m=void 0===A?O.isCaseSensitive:A,C=u.ignoreDiacritics,k=void 0===C?O.ignoreDiacritics:C,E=u.ignoreLocation,F=void 0===E?O.ignoreLocation:E;return r(this,n),(i=t.call(this,e))._bitapSearch=new T(e,{location:c,threshold:s,distance:l,includeMatches:d,findAllMatches:g,minMatchCharLength:p,isCaseSensitive:m,ignoreDiacritics:k,ignoreLocation:F}),i}return u(n,[{key:"search",value:function(e){return this._bitapSearch.searchIn(e)}}],[{key:"type",get:function(){return"fuzzy"}},{key:"multiRegex",get:function(){return/^"(.*)"$/}},{key:"singleRegex",get:function(){return/^(.*)$/}}]),n}(K),Y=function(e){c(n,e);var t=l(n);function n(e){return r(this,n),t.call(this,e)}return u(n,[{key:"search",value:function(e){for(var t,n=0,r=[],i=this.pattern.length;(t=e.indexOf(this.pattern,n))>-1;)n=t+i,r.push([t,n-1]);var u=!!r.length;return{isMatch:u,score:u?0:1,indices:r}}}],[{key:"type",get:function(){return"include"}},{key:"multiRegex",get:function(){return/^'"(.*)"$/}},{key:"singleRegex",get:function(){return/^'(.*)$/}}]),n}(K),Z=[J,Y,V,G,Q,H,U,X],ee=Z.length,te=/ +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,ne=new Set([X.type,Y.type]),re=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=n.isCaseSensitive,u=void 0===i?O.isCaseSensitive:i,o=n.ignoreDiacritics,c=void 0===o?O.ignoreDiacritics:o,a=n.includeMatches,s=void 0===a?O.includeMatches:a,h=n.minMatchCharLength,l=void 0===h?O.minMatchCharLength:h,f=n.ignoreLocation,d=void 0===f?O.ignoreLocation:f,v=n.findAllMatches,g=void 0===v?O.findAllMatches:v,y=n.location,p=void 0===y?O.location:y,A=n.threshold,m=void 0===A?O.threshold:A,C=n.distance,k=void 0===C?O.distance:C;r(this,e),this.query=null,this.options={isCaseSensitive:u,ignoreDiacritics:c,includeMatches:s,minMatchCharLength:l,findAllMatches:g,ignoreLocation:d,location:p,threshold:m,distance:k},t=u?t:t.toLowerCase(),t=c?z(t):t,this.pattern=t,this.query=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.split("|").map((function(e){for(var n=e.trim().split(te).filter((function(e){return e&&!!e.trim()})),r=[],i=0,u=n.length;i2&&void 0!==arguments[2]?arguments[2]:{}).auto,r=void 0===n||n;return he(e)||(e=le(e)),function e(n){var i=Object.keys(n),u=function(e){return!!e[ae]}(n);if(!u&&i.length>1&&!he(n))return e(le(n));if(function(e){return!g(e)&&k(e)&&!he(e)}(n)){var o=u?n[ae]:i[0],c=u?n[se]:n[o];if(!A(c))throw new Error(function(e){return"Invalid value for key ".concat(e)}(o));var a={keyId:L(o),pattern:c};return r&&(a.searcher=ue(c,t)),a}var s={children:[],operator:i[0]};return i.forEach((function(t){var r=n[t];g(r)&&r.forEach((function(t){s.children.push(e(t))}))})),s}(e)}function de(e,t){var n=e.matches;t.matches=[],E(n)&&n.forEach((function(e){if(E(e.indices)&&e.indices.length){var n={indices:e.indices,value:e.value};e.key&&(n.key=e.key.src),e.idx>-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function ve(e,t){t.score=e.score}var ge=function(){function e(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},u=arguments.length>2?arguments[2]:void 0;r(this,e),this.options=t(t({},O),i),this.options.useExtendedSearch,this._keyStore=new x(this.options.keys),this.setCollection(n,u)}return u(e,[{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof I))throw new Error("Incorrect 'index' type");this._myIndex=t||$(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}},{key:"add",value:function(e){E(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=0,r=this._docs.length;n1&&void 0!==arguments[1]?arguments[1]:{}).limit,n=void 0===t?-1:t,r=this.options,i=r.includeMatches,u=r.includeScore,o=r.shouldSort,c=r.sortFn,a=r.ignoreFieldNorm,s=A(e)?A(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e);return function(e,t){var n=t.ignoreFieldNorm,r=void 0===n?O.ignoreFieldNorm:n;e.forEach((function(e){var t=1;e.matches.forEach((function(e){var n=e.key,i=e.norm,u=e.score,o=n?n.weight:null;t*=Math.pow(0===u&&o?Number.EPSILON:u,(o||1)*(r?1:i))})),e.score=t}))}(s,{ignoreFieldNorm:a}),o&&s.sort(c),m(n)&&n>-1&&(s=s.slice(0,n)),function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?O.includeMatches:r,u=n.includeScore,o=void 0===u?O.includeScore:u,c=[];return i&&c.push(de),o&&c.push(ve),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return c.length&&c.forEach((function(t){t(e,r)})),r}))}(s,this._docs,{includeMatches:i,includeScore:u})}},{key:"_searchStringList",value:function(e){var t=ue(e,this.options),n=this._myIndex.records,r=[];return n.forEach((function(e){var n=e.v,i=e.i,u=e.n;if(E(n)){var o=t.searchIn(n),c=o.isMatch,a=o.score,s=o.indices;c&&r.push({item:n,idx:i,matches:[{score:a,value:n,norm:u,indices:s}]})}})),r}},{key:"_searchLogical",value:function(e){var t=this,n=fe(e,this.options),r=function e(n,r,i){if(!n.children){var u=n.keyId,o=n.searcher,c=t._findMatches({key:t._keyStore.get(u),value:t._myIndex.getValueForItemAtKeyId(r,u),searcher:o});return c&&c.length?[{idx:i,item:r,matches:c}]:[]}for(var a=[],s=0,h=n.children.length;s1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?O.getFn:n,i=t.fieldNormWeight,u=void 0===i?O.fieldNormWeight:i,o=e.keys,c=e.records,a=new I({getFn:r,fieldNormWeight:u});return a.setKeys(o),a.setIndexRecords(c),a},ge.config=O,function(){ie.push.apply(ie,arguments)}(re),ge},"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Fuse=t(); \ No newline at end of file diff --git a/assets/scripts/search.js b/assets/scripts/search.js new file mode 100644 index 0000000..b4c5eaf --- /dev/null +++ b/assets/scripts/search.js @@ -0,0 +1,47 @@ + +window.onload = async function (ev) { + let data_req = await fetch("/index.json") + let data = await data_req.json() + let fuse = new Fuse(data, {keys: [ + "tags", + "id", + "title", + "body" + ] + }) + async function search(args) { + try { + let x = fuse.search(args) + return x + } catch { + return null + } + } + document.getElementById('searchbox').addEventListener("input", + async function(e) { + let results = document.getElementById('results') // .innerHTML = idx.search(e.target.value)) + if (e.target.value == "") { + results.innerHTML = "" + } else { + Promise.race([ + search(e.target.value), + new Promise((resolve, reject) => { + setTimeout(() => { + resolve(-1) + }, 3000); + }) + ]).then((r) => { + if (r == null) { + results.innerHTML = "
  • Search results
  • An error occured
" + } else if (r == -1) { + results.innerHTML = "
  • Search results
  • Query timed out
" + } else if (r.length == 0) { + results.innerHTML = "
  • Search results
  • No result
" + } else { + results.innerHTML = "" + } + }) + } + } + ) +} diff --git a/assets/style/common.css b/assets/style/common.css new file mode 100644 index 0000000..3c313ba --- /dev/null +++ b/assets/style/common.css @@ -0,0 +1,147 @@ +@import url(/assets/style/fonts.css); +:root { + --bg: #282828; + --bg-accent: #504945; + --fg: #ebdbb2; + --fg-disabled: #bdae93; + --accent: #fe8019; + --note: #b8bb26; + --info: #83a598; + --warn: #fabd2f; + --error: #fb4934; + --font-main: Andika, sans-serif; + --font-mono: Iosevka, monospace; +} +* { + font-family: var(--font-main); + font-feature-settings: "ss13" on; + padding: 0pt; + margin: 0pt; +} +body { + background-color: var(--bg); + color: var(--fg); +} + +header { + width: 100%; + background-color: var(--bg-accent); + text-align: center; +} + +header h1 a { + text-decoration: none; +} + +header nav { + width: 100%; + padding: 0pt, 10pt; + background-color: var(--bg-accent); + justify-items: stretch; + justify-content: space-evenly; + align-items: stretch; + display: flex; +} + +nav input { + width: 25vw; + background-color: var(--bg); + margin: 10pt; + color: var(--fg); +} + +aside, aside * { + background-color: var(--bg-accent); +} +a { + display: inline; + color: var(--accent) +} +ul.sresult { + width: 50vw; + height: 50vw; + overflow: scroll; + justify-content: center; + position: absolute; + top: 25%; + left: 50%; + transform: translate(-50%, 0%); + background-color: var(--bg-accent); + padding: 10pt +} +li.sresult { + list-style: none; + font-size: 1.5em; +} + +aside { + padding: 10pt; + height: min-content; +} +main { + padding: 25pt 10pt; +} + +@media (min-width: 800px) { + #main-body {display: flex;} + main { + width: calc(100% - 10em); + } + aside { + margin: 10pt 10pt 10pt 0pt; + width: 10em; + } +} +@media (max-width: 800px) { + main { + width: 100% - 20pt + } + aside { width: 100% - 20pt } +} +/****** Main body styling ******/ +main h1 { + font-size: 2em; + font-weight: bold; +} + +main li { + list-style-position: inside; + padding: 0 0 0 10pt; +} +main table { + margin: 1em; +} +main table, main td, main tr { + text-align: center; + border-collapse: collapse; + border: 2px solid var(--fg); +} +main td { + padding: 0em 1em; + margin: 0; +} + +main .low { + color: var(--fg-disabled); + font-style: italic; +} +main .high { + color: var(--accent); + font-family: inherit; +} +main em { + color: var(--accent) +} + +/****** GLOSSES ******/ +.gloss, .gloss tr, .gloss td { + stroke: none; + border: none; + text-align: left; + padding: 0 .25em; +} + +/****** SCRIPTS ******/ +.scr-nahan { + font-family: AndikaAmbNaran; +} diff --git a/assets/style/fonts.css b/assets/style/fonts.css new file mode 100644 index 0000000..147ed88 --- /dev/null +++ b/assets/style/fonts.css @@ -0,0 +1,55 @@ +/* Andika with the nahan script */ +@font-face { + font-family: AndikaAmbNaran; + src: url("../fonts/AndikaAmbNaran.ttf") format("truetype"); +} + +/* Andika for the rest */ +@font-face { + font-family: Andika; + src: url("../fonts/Andika-Bold.ttf") format("truetype"); + font-weight: 700; + font-style: normal; +} +@font-face { + font-family: Andika; + src: url("../fonts/Andika-BoldItalic.ttf") format("truetype"); + font-weight: 700; + font-style: italic; +} +@font-face { + font-family: Andika; + src: url("../fonts/Andika-Italic.ttf") format("truetype"); + font-weight: 400; + font-style: italic; +} +@font-face { + font-family: Andika; + src: url("../fonts/Andika-Medium.ttf") format("truetype"); + font-weight: 500; + font-style: normal; +} +@font-face { + font-family: Andika; + src: url("../fonts/Andika-MediumItalic.ttf") format("truetype"); + font-weight: 500; + font-style: italic; +} +@font-face { + font-family: Andika; + src: url("../fonts/Andika-Regular.ttf") format("truetype"); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: Andika; + src: url("../fonts/Andika-SemiBold.ttf") format("truetype"); + font-weight: 600; + font-style: normal; +} +@font-face { + font-family: Andika; + src: url("../fonts/Andika-SemiBoldItalic.ttf") format("truetype"); + font-weight: 600; + font-style: italic; +} diff --git a/build.ts b/build.ts new file mode 100755 index 0000000..87cddec --- /dev/null +++ b/build.ts @@ -0,0 +1,96 @@ +#!/usr/bin/env -S bun run +import { $ } from "bun"; +import { readdir } from "node:fs/promises" +import { parseArgs } from "util"; +import { JSDOM } from "jsdom"; +import { Dirent } from "node:fs"; +import { mkdir, stat } from "node:fs/promises"; + +function out_path(path: string) { +} + +type LogLevel = "INFO"|"WARN"|"ERROR"|"FATAL" +function log(level: LogLevel, message: any) { + const p = Bun.argv[1]?.split("/").slice(-1)[0] ?? "build script"; + switch (level) { + case "INFO": console.log(`\x1b[90m${p}: \x1b[1;42;97m [INFO] \x1b[0m ${message}`); break; + case "WARN": console.log(`\x1b[90m${p}: \x1b[1;48;5;208;97m [WARN] \x1b[0m ${message}`); break; + case "ERROR": console.log(`\x1b[90m${p}: \x1b[1;41;97m [ERROR] \x1b[0m ${message}`); break; + case "FATAL": console.log(`\x1b[90m${p}: \x1b[1;41;97m [FATAL] ${message} \x1b[0m`); process.exit(); break; + } +} + +async function build_typst_file(file: Dirent) { + let path = file.parentPath + "/" + file.name + let dir = "public_html" + file.parentPath.slice(3) + let slug = path.slice(4, -4) + let outfile = `public_html/${slug}.html` + log("INFO", `Building ${path}`) + await mkdir(dir, {recursive: true}) + await $`typst c --features html --format html --root . ${path} ${outfile}` + log("INFO", `Built ${path}`) +} + + +async function build_all_typst() { + let files = await readdir("src", {withFileTypes: true, recursive: true}); + let promises: Promise[] = []; + for (let f of files) { + if (f.isFile() && f.name.endsWith(".typ")) + promises.push(build_typst_file(f)) + } + await Promise.all(promises) +} + +async function copy_assets() { + log("INFO", "Updating assets") + await $`rm -rf public_html/assets/` + await $`cp -r assets/ public_html/` +} + +type IndexEntry = { id: string; title: string; body: string; tags: string;} +async function collect_data(f:string) : Promise { + let id = f.slice(12, -5) + log("INFO", `collecting metadata for ${id}`) + let dom = await JSDOM.fromFile(f) + let body = "" + let things = dom.window.document.getElementsByTagName("main") + for (let thing of things) body += thing.textContent; + let title = dom.window.document.getElementById("search-title")?.getAttribute("content") ?? "" + let tags = dom.window.document.getElementById("search-tags")?.getAttribute("content") ?? "" + return {id, title, body, tags} +} +async function gen_index() { + let files = await readdir("public_html", {withFileTypes: true, recursive: true}) + + let promises : Promise[] = []; + for (let f of files) { + if (f.isFile() && f.name.endsWith(".html")) { + promises.push(collect_data(f.parentPath + "/" + f.name)); + } + } + let data : IndexEntry[] = await Promise.all(promises) + await Bun.write("public_html/index.json", JSON.stringify(data), {mode: 0o644, createPath: true}) +} + + +const args = parseArgs({ + args: Bun.argv, + options: { + "build": {type: "boolean", short: "b"}, + "assets": {type: "boolean", short: "a"}, + "index": { + short: "i", type: "boolean" + } + }, + allowPositionals: true +}).values + +if (!args.build && !args.assets && !args.index) { + await build_all_typst() + await copy_assets() + await gen_index() +} +if (args.build) await build_all_typst(); +if (args.assets) await copy_assets(); +if (args.index) await gen_index(); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..83c3c08 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "wiki-build-tools", + "private": true, + "devDependencies": { + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "@types/jsdom": "^27.0.0", + "jsdom": "^27.4.0", + "@types/bun": "latest" + } +} diff --git a/src/dict/-ala.typ b/src/dict/-ala.typ new file mode 100644 index 0000000..8b430ff --- /dev/null +++ b/src/dict/-ala.typ @@ -0,0 +1,14 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "-ala") +%dict %word += Mosici +%mos +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/ala", mos-cit(""),"cln") +=== Pronunciation +#mos-pro[\*aʟa] +=== Affix +%mos/affix +#mos-cit("") +1. Noun #low[x] arrow Verb "causing the existence of #low[x]" diff --git a/src/dict/ain.typ b/src/dict/ain.typ new file mode 100644 index 0000000..5703eea --- /dev/null +++ b/src/dict/ain.typ @@ -0,0 +1,20 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ain") +%dict %word += Mosici +%mos +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/ainy", [#sn[]], "cln") #low[ainy] +=== Pronunciation +#mos-pro[ɛ̃] +=== Pronoun +%mos/pronoun +#mos-cit("") +1. Third person inannimate pronoun +#mos-pron("") +=== Noun +%mos/noun +#mos-cit("") +1. Thing, object +#mos-n("") diff --git a/src/dict/ainilapil.typ b/src/dict/ainilapil.typ new file mode 100644 index 0000000..e3187e3 --- /dev/null +++ b/src/dict/ainilapil.typ @@ -0,0 +1,15 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ainilapil") +%dict %word += Mosici +%mos +From #wl("/dict/ain", mos-cit(""), "mos-1-noun") + #wl("/dict/ilapil", mos-cit(""), "mos") +=== Pronunciation +#mos-pro[ɛ̃neʎapeẅ] +=== Noun +%mos/noun +#mos-cit("") (plural #wl("/dict/ainilapiltee", mos-cit(""), "mos")) +1. Noun, substantive +#mos-n("", pl: "") diff --git a/src/dict/ainilapiltee.typ b/src/dict/ainilapiltee.typ new file mode 100644 index 0000000..371dab7 --- /dev/null +++ b/src/dict/ainilapiltee.typ @@ -0,0 +1,13 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ainilapiltee") +%dict += Mosici +%mos +=== Pronunciation +#mos-pro[ɛ̃neʎapeẅdi] +=== Moun +%mos/noun +#mos-cit("") +1. plural form of #wl("/dict/ainilapil", mos-cit(""), "mos")) diff --git a/src/dict/ainy.typ b/src/dict/ainy.typ new file mode 100644 index 0000000..1d52dba --- /dev/null +++ b/src/dict/ainy.typ @@ -0,0 +1,13 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ainy") +%dict %word += Classical Nyelaf +%cln +=== Pronounciation +- /a.inə/ +=== Noun +%cln/noun +#sn[] #low[ainy] +1. Thing, object diff --git a/src/dict/ala.typ b/src/dict/ala.typ new file mode 100644 index 0000000..9f6b0bf --- /dev/null +++ b/src/dict/ala.typ @@ -0,0 +1,8 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ala") +%dict %word += Classical Nyelaf +%cln +#sn[] #low[ala] [aɫa] diff --git a/src/dict/an·oreaiác ila.typ b/src/dict/an·oreaiác ila.typ new file mode 100644 index 0000000..1b62426 --- /dev/null +++ b/src/dict/an·oreaiác ila.typ @@ -0,0 +1,13 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "an·oreaiác ila") +%word %dict += Mosici +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/oheaiā", sn[], "cln") #low[oheaiā] and the regular formation for language names +=== Pronunciation +#mos-pro[ãdoʀeç eʎa] +=== Proper noun +#mos-cit(" ") +1. #reg[Out of Universe] #link("https://www.laghariportals.com/78h")[O’eaiā] +#mos-n(" ", pl: false) diff --git a/src/dict/cihyty.typ b/src/dict/cihyty.typ new file mode 100644 index 0000000..0ace125 --- /dev/null +++ b/src/dict/cihyty.typ @@ -0,0 +1,8 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "cihyty") +%dict %word += Classical Nyelaf +%cln +#sn[] #low[cihyty] [kixətə] diff --git a/src/dict/cirts.typ b/src/dict/cirts.typ new file mode 100644 index 0000000..f01d084 --- /dev/null +++ b/src/dict/cirts.typ @@ -0,0 +1,16 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "cirts") +%word %dict += Mosici +%mos +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/cihyty", sn[], "cln") #low[cihyty] +=== Pronunciation +#mos-pro[ceɐ̯ts] +=== Noun +%mos/noun +#mos-cit("") +1. Writing, text +2. Document, book +#mos-n("") diff --git a/src/dict/contse.typ b/src/dict/contse.typ new file mode 100644 index 0000000..3784097 --- /dev/null +++ b/src/dict/contse.typ @@ -0,0 +1,16 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "contse") +%word %dict += Mosici +%mos +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/contysse", sn[], "cln") #low[contysse] +=== Pronunciation +#mos-pro[qõtsɛ] +=== Noun +%mos/noun +#mos-cit("") +1. Novelty, innovation +#mos-n("") + diff --git a/src/dict/contseila.typ b/src/dict/contseila.typ new file mode 100644 index 0000000..7bfd694 --- /dev/null +++ b/src/dict/contseila.typ @@ -0,0 +1,15 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "contseila") +%word %dict += Mosici +%mos +From #wl("/dict/contse", sn[], "mos-1-noun") #low[contse] + #wl("/dict/ila", sn[], "mos-1-verb") #low[ila] +=== Pronunciation +#mos-pro[qõtsiʎa] +=== Verb +%mos/verb +#mos-cit("") +1. To present, to announce +#mos-v("") diff --git a/src/dict/contysse.typ b/src/dict/contysse.typ new file mode 100644 index 0000000..02e023a --- /dev/null +++ b/src/dict/contysse.typ @@ -0,0 +1,8 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "contysse") +%word %dict += Classical Nyelaf +%cln +#sn[] #low[contysse] [kontəsːə] diff --git a/src/dict/effyha.typ b/src/dict/effyha.typ new file mode 100644 index 0000000..7727549 --- /dev/null +++ b/src/dict/effyha.typ @@ -0,0 +1,8 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "effyha") +%word %dict += Classical Nyelaf +%cln +#sn[] #low[effyha] [efːəxa] diff --git a/src/dict/entec r. tziets.typ b/src/dict/entec r. tziets.typ new file mode 100644 index 0000000..c65af6b --- /dev/null +++ b/src/dict/entec r. tziets.typ @@ -0,0 +1,15 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "Entec R. Tzietz") +%dict %word + += Mosici +%mos +=== Pronunciation +#mos-pro[ɛ̃dɛx aɐ̯ dʑɛdz] +=== Proper Noun +%mos/proper +#mos-cit("  ") +1. Enderjed, the man, the myth, the legend +#mos-n("  ", pl: false) diff --git a/src/dict/i.typ b/src/dict/i.typ new file mode 100644 index 0000000..7112719 --- /dev/null +++ b/src/dict/i.typ @@ -0,0 +1,21 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "i") +%dict +%word += Mosici +%mos +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/i", sn[], "cln") #low[i] +=== Pronunciation +#mos-pro[e] +=== Particle +%mos/particle +#mos-cit("") +1. and +2. also + += Classical Nyelaf +%cln +#mos-cit("") [i] + diff --git a/src/dict/ila.typ b/src/dict/ila.typ new file mode 100644 index 0000000..90db2c4 --- /dev/null +++ b/src/dict/ila.typ @@ -0,0 +1,28 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ila") +%dict +%word += Mosici +%mos + +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/ila", mos-cit(""), "cln") +=== Prononciation +#mos-pro[eʎa] +=== Noun +%mos/noun +#mos-cit("") +1. Language, especially spoken +2. Speech, announcement +3. #poet word +#mos-n("") +=== Verb +%mos/verb +#mos-cit("") +1. #arch to speak, to tell +#mos-v("") + += Classical Nyelaf +%cln +#mos-cit("") [iɫa] diff --git a/src/dict/ilaala.typ b/src/dict/ilaala.typ new file mode 100644 index 0000000..1e9bb05 --- /dev/null +++ b/src/dict/ilaala.typ @@ -0,0 +1,17 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ilaala") +%dict +%word += Mosici +%mos +From #wl("/dict/ila", sn[], "mos") #low[ila] + #wl("/dict/-ala", sn[], "mos") #low[-ala] +=== Pronunciation +#mos-pro[eʎɔʟa] +=== Verb +%mos/verb +#mos-cit("") +1. To speak +#mos-v("") + diff --git a/src/dict/ilamócirts.typ b/src/dict/ilamócirts.typ new file mode 100644 index 0000000..4b4a070 --- /dev/null +++ b/src/dict/ilamócirts.typ @@ -0,0 +1,16 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ilamócirts") +%dict +%word += Mosici +%mos +From #wl("/dict/ilamós", sn[], "mos") #low[ilamós] + #wl("/dict/cirts", sn[], "mos") #low[cirts] +=== Pronunciation +#mos-pro[eʎamuceɐ̯ts] +=== Noun +%mos/noun +#mos-cit("") +1. Grammar (book) +#mos-n("") diff --git a/src/dict/ilamós.typ b/src/dict/ilamós.typ new file mode 100644 index 0000000..f04c5d6 --- /dev/null +++ b/src/dict/ilamós.typ @@ -0,0 +1,16 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ilamós") +%dict +%word += Mosici +%mos +From #wl("/dict/ila", sn[], "mos") #low[ila] + [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/mōssy", sn[], "cln") #low[mōssy] (see also #wl("/dict/mósnier", sn[], "mos") #low[mósnier]) +=== Pronunciation +#mos-pro[eʎamus] +=== Noun +%mos/noun +#mos-cit("") +1. Grammar, language rules +#mos-n("") diff --git a/src/dict/ilapil.typ b/src/dict/ilapil.typ new file mode 100644 index 0000000..f4735d3 --- /dev/null +++ b/src/dict/ilapil.typ @@ -0,0 +1,16 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ilapil") +%dict +%word += Mosici +%mos +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/ila", sn[], "cln") #low[ila] #wl("/dict/pilyteny", sn[], "cln") #low[pily(teny)] +=== Pronunciation +#mos-pro[eʎapeẅ] +=== Noun +%mos/noun +#mos-cit("") (plural #wl("/dict/ilapiltee", sn[], "mos") #low[ilapiltee]) +1. Word +#mos-n("", pl: "") diff --git a/src/dict/ilapiltee.typ b/src/dict/ilapiltee.typ new file mode 100644 index 0000000..ca537ad --- /dev/null +++ b/src/dict/ilapiltee.typ @@ -0,0 +1,13 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ilapiltee") +%dict += Mosici +%mos +=== Pronunciation +#mos-pro[eʎapeẅdi] +=== Moun +%mos/noun +#mos-cit("") +1. plural form of #wl("/dict/ilapil", mos-cit(""), "mos")) diff --git a/src/dict/ipianlei.typ b/src/dict/ipianlei.typ new file mode 100644 index 0000000..b526fc5 --- /dev/null +++ b/src/dict/ipianlei.typ @@ -0,0 +1,18 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ipianlei") +%dict + += Mosici +%word %mos + +From the [[/worlds/Asteron/Classical Nyelaf]] phrase #sn[]#wl("/dict/i", sn[], "cln") #low[i] #wl("/dict/piany", sn[], "cln") #low[piany] #wl("/dict/lei", sn[], "cln") #low[lei] #sn[] (and afterwards, two) +=== Pronunciation +#mos-pro[epjãʎi] +=== Noun +%mos/noun +#mos-cit("") (plural #wl("/dict/ipianalei", mos-cit(""), "mos")) +1. Sequence, ordering + +#mos-n("", pl: "") diff --git a/src/dict/lei.typ b/src/dict/lei.typ new file mode 100644 index 0000000..da19c4f --- /dev/null +++ b/src/dict/lei.typ @@ -0,0 +1,18 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "lei") +%dict +%word += Mosici +%mos +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/lei", sn[], "cln") #low[lei] +=== Pronunciation +#mos-pro[ʎi] +=== Numeral +%mos/numeral +#mos-cit("") +1. two += Classical Nyelaf +%cln +#mos-cit("") [ɫe.i] diff --git a/src/dict/mósnier.typ b/src/dict/mósnier.typ new file mode 100644 index 0000000..d9a14f4 --- /dev/null +++ b/src/dict/mósnier.typ @@ -0,0 +1,15 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "mósnier") +%dict %word += Mosici +%mos +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/mōssy", sn[], "cln") #low[mōssy] + #wl("/dict/niehy", sn[], "cln") #low[niehy] +=== Pronunciation +#mos-pro[musɲiɐ̯] +=== Noun +#mos-cit("") +1. Convention, custom +2. Rule (of a game) +#mos-n("") diff --git a/src/dict/mōssy.typ b/src/dict/mōssy.typ new file mode 100644 index 0000000..88624a3 --- /dev/null +++ b/src/dict/mōssy.typ @@ -0,0 +1,9 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "mōssy") +%dict +%word += Classical Nyelaf +%cln +#sn[] #low[mōssy] [moːsːə] diff --git a/src/dict/niehy.typ b/src/dict/niehy.typ new file mode 100644 index 0000000..f536ba1 --- /dev/null +++ b/src/dict/niehy.typ @@ -0,0 +1,9 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "niehy") +%dict +%word += Classical Nyelaf +%cln +#sn[] #low[niehy] [ni.exə] diff --git a/src/dict/o.typ b/src/dict/o.typ new file mode 100644 index 0000000..22b078c --- /dev/null +++ b/src/dict/o.typ @@ -0,0 +1,15 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "o") +%dict %word += Mosici +%mos +From onomatopeic origin +=== Pronunciation +#mos-pro[o] +=== Particle +#mos-cit("") +1. #reg[After nouns] Vocative marker +2. #reg[After verbs] Imperative marker + diff --git a/src/dict/ohaioa.typ b/src/dict/ohaioa.typ new file mode 100644 index 0000000..6b9d1f2 --- /dev/null +++ b/src/dict/ohaioa.typ @@ -0,0 +1,8 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ohaioa") +%word %dict += Classical Nyelaf +%cln +#sn[] #low[ohaioa] [oxajo.a] diff --git a/src/dict/oheaiā.typ b/src/dict/oheaiā.typ new file mode 100644 index 0000000..d60bab6 --- /dev/null +++ b/src/dict/oheaiā.typ @@ -0,0 +1,9 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "oheaiā") +%word %dict += Classical Neyelaf +%cln +#sn[] #low[oheaiā] [oxe.a.i.aː] +1. #reg[Out of Universe] #link("https://www.laghariportals.com/78h")[O’eaiā] diff --git a/src/dict/oiaō.typ b/src/dict/oiaō.typ new file mode 100644 index 0000000..974f03b --- /dev/null +++ b/src/dict/oiaō.typ @@ -0,0 +1,9 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "oiaō") +%word %dict += Classical Nyelaf +%cln +#sn[] #low[oiaō] [o.i.a.oː] + diff --git a/src/dict/óasonela.typ b/src/dict/óasonela.typ new file mode 100644 index 0000000..8c7be5f --- /dev/null +++ b/src/dict/óasonela.typ @@ -0,0 +1,15 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "óasonela") +%word %dict += Mosici +%mos +From [[/worlds/Asteron/Classical Nyelaf]] #wl("/dict/ōas", sn[], "cln") #low[ōas] + #wl("/dict/sonela", sn[], "cln") #low[sonela] +=== Pronunciation +#mos-pro[wasõnɛʟa] +=== Verb +%mos/verb +#mos-cit("") +1. To discover +#mos-v("") diff --git a/src/dict/īly.typ b/src/dict/īly.typ new file mode 100644 index 0000000..9ab12de --- /dev/null +++ b/src/dict/īly.typ @@ -0,0 +1,9 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "īly") +%dict +%word += Classical Nyelaf +%cln +#sn[] #low[īly] [iːɫə] diff --git a/src/dict/īny.typ b/src/dict/īny.typ new file mode 100644 index 0000000..54ca0e3 --- /dev/null +++ b/src/dict/īny.typ @@ -0,0 +1,9 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "īny") +%dict +%word += Classical Nyelaf +%cln +#sn[] #low[īny] [iːnə] diff --git a/src/dict/ōas.typ b/src/dict/ōas.typ new file mode 100644 index 0000000..4b73c06 --- /dev/null +++ b/src/dict/ōas.typ @@ -0,0 +1,8 @@ +#import "/templates/base.typ": * +#import "/templates/utils/dict.typ": * +#import "/templates/utils/lang-mos.typ": * +#show: conf.with(page-title: "ōas") +%word %dict += Classical Nyelaf +%cln +#sn[] #low[ōas] [oːas] diff --git a/src/index.typ b/src/index.typ new file mode 100644 index 0000000..e3ed0da --- /dev/null +++ b/src/index.typ @@ -0,0 +1,7 @@ +#import "../templates/base.typ": * +#show: conf.with(page-title: sitename, title-override: "Index") + += Welcome +foo bar baz +== H2 +=== H3 diff --git a/src/worlds/Asteron/Classical Nyelaf.typ b/src/worlds/Asteron/Classical Nyelaf.typ new file mode 100644 index 0000000..910fab7 --- /dev/null +++ b/src/worlds/Asteron/Classical Nyelaf.typ @@ -0,0 +1,4 @@ +#import "/templates/base.typ": * +#show: conf.with(page-title: "Classical Nyelaf") +#tag("lang") #tag("cln") +#set heading(numbering: "1.") diff --git a/src/worlds/Asteron/Mosici.typ b/src/worlds/Asteron/Mosici.typ new file mode 100644 index 0000000..fa50ae2 --- /dev/null +++ b/src/worlds/Asteron/Mosici.typ @@ -0,0 +1,121 @@ +#import "/templates/base.typ": * +#import "/templates/utils/gloss.typ": example, gloss +#import "/templates/utils/lang-mos.typ": * +#let high = html.span.with(class: "high") +#show: conf.with(page-title: "Mosici") +%lang %mos +#set heading(numbering: "1.") +#let gloss-opts = ( + txt-style: sn, + translation-style: html.span.with(class: "low"), +) +#let g = gloss.with(..gloss-opts) +#let ex = example.with(..gloss-opts) += Sound ⁊ Letters +== Phonolgy +Mosici has the following phonemes +#table( + columns: 5, + [], [*Labial*], [*Coronal*], [*Palatal*], [*Dorsal*], + [*Nasal*], [m], [n], [], [], + [*Stop*], [p], [t], [], [k], + [*Fricative*], [f v], [s z], [ɕ ʑ], [ʀ], + [*Approximants*], [w], [], [j], [ʟ], +) +#table( + columns: 3, + [], [*Front*], [*Back*], + [*Close*], [i y], [u], + [*Close-Mid*], [e ø], [o], + [*Open-Mid*], [ɛ], [ɔ], + [*Open*], table.cell(colspan: 2)[a], +) + +All vowels can also all be long, + +There is also the following allophony rules: + +- The dorsal approximant is realised as a [ẅ] off-glide in coda positions. +- The dorsal fricative is realised as a [ɐ̯] off-glide in coda positions. +- The dorsal fricative is realised as a true fricative [ʁ] in consonant clusters. +- /n/ nasalises a preceding vowel. +- /n/ itself is not pronounced in coda positions.#footnote[still applies nasalisation] +- Nasalised close vowels are realised as mid-centralised: /ĩ ĩː ỹ ỹː ũ ũː/ [ɪ̃ ɪ̃ː ʏ̃ ʏ̃ː ʊ̃ ʊ̃ː] +- The dorsal plosive ⁊ approximant are realised as palatal before /i y e j/ #footnote[or their or their long and/or + nasalised variants] , +- The dorsal plosive ⁊ approximant are realised as uvular before ther /u o w/@fn-dorsal-assimilation +- The dorsal plosive ⁊ approximant are realised as palatal after /e i j/@fn-dorsal-assimilation or in the coda of a + syllable with /e i/@fn-dorsal-assimilation as the nucleus. +- The dorsal plosive ⁊ approximant are realised as velar otherwise +- 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. + +== Coalsecence + +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 +to understand to read the written language, as the spelling was fixed before that sound change occurred. + +The process goes thusly (before applying the allophony): + +1. Group all consecutive vowels by pairs, starting at near the start of the word +2. Combine all pairs of vowels according to the table below (the first vowel indexes the row, and the second vowel + indexes the column) +3. If any vowel is long, the resulting vowel is long; +4. Repeat from #context link(query().first().location())[step 1] until all hiatus has been + resolved. +#table( + columns: 10, + [ ], [*a*], [*ɛ*], [*ɔ*], [*e*], [*ø*], [*o*], [*i*], [*y*], [*u*], + [*a*], [ɔ], [a], [ɔ], [ɛ], [ɛ], [ɔ], [e], [ø], [o], + [*ɛ*], [ɛ], [i], [ø], [i], [e], [ø], [i], [ø], [ø], + [*ɔ*], [ɔ], [ø], [ɔ], [ø], [ø], [o], [ø], [ø], [o], + [*e*], [ɛ], [i], [ø], [i], [e], [ø], [i], [ø], [ø], + [*ø*], [ø], [e], [ø], [e], [y], [ø], [y], [y], [y], + [*o*], [ɔ], [ø], [o], [ø], [ø], [u], [ø], [ø], [u], + [*i*], [ja], [jɛ], [jɔ], [je], [jø], [jo], [i], [jy], [ju], + [*y*], [ø], [ø], [ø], [ø], [y], [ø], [i], [i], [y], + [*u*], [wa], [wɛ], [wɔ], [we], [wø], [wo], [wi], [y], [u], +) + +== The Nahan Script +Mosici is written in the [[/worlds/Asteron/Nahan Script]] (also named the Polia(h)r alphabet), which is am alphabet +which in Mosici is considered to have the following letters, digraphs and diacritic’d letters. The sole diacritic is +called the #sn[] 〈sitrapaóha〉. + +#table( + columns: 5, + [Letter], [Transliteration], [Value (IPA)], [Name], [Name (IPA)], + sn[], [p], [/p/], sn[], [[pe]], + sn[], [o], [/o/], sn[], [[us]], + sn[], [l], [/ʟ/], sn[], [[ʟɔ̃]], + sn[], [i], [/e/], sn[], [[iɐ̯nɛ]], + sn[], [a], [/a/], sn[], [[ɔ̃ɸ]], + sn[], [h], [/∅/#footnote[Lengthens a preceeding vowel] ], sn[], [[apfɛ]], + sn[], + [r], + [/ʀ/], + sn[ #footnote[Literally "sounded 〈base letter〉"] ], + [[fasteɕɛx apfɛ]], + + sn[], [c], [/k/], sn[], [[kaẅ]], + sn[], [n], [/n/], sn[], [[nɔẅ]], + sn[], [e], [/e/], sn[], [[istaẅ]], + sn[], [s], [/s/], sn[], [[ɕpaẅ]], + sn[], [z], [/z/], sn[  @fn-script-sounded], [[fasteɕɛx ɕpaẅ]], + sn[], [f], [/f/], sn[], [[fasoː]], + sn[], [v], [/v/], sn[  @fn-script-sounded], [[fasteɕex fasoː]], + sn[], [m], [/m/], sn[], [[miʎɔ]], + sn[], [t], [/t/], sn[], [[tɛɟjo]], +) + += Morphology +#ex( + caption: [foo], + txt: [ #high[ ] ], + translit: ([il], high[vionreapt], high[anap], [cirtan]), + 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]), + translation: [#high[Whenever] I #high[eat], you write], + lbl: "example" +) diff --git a/templates/base.typ b/templates/base.typ new file mode 100644 index 0000000..c10debe --- /dev/null +++ b/templates/base.typ @@ -0,0 +1,66 @@ +#let tag = tagname => [ #metadata(tagname) ] +#let wl(path, txt, frag) = { + let fragtext = if frag == none {""} else {"#" + frag} + let texttext = if txt == none { + path.split("/").at(-1) + } else {txt} + link(path + ".html" + fragtext, texttext) +} +#let conf( + page-title: "", + title-override: none, + subtitle: none, + doc, +) = { + let sitename = [Annwan's Wiki] + // matches [[path|text!fragments]] with opt texts and fragment + let link-re = regex( + "\\[\\[([^\\]\\|!]+)(?:\\|([^\\]\\|!]+))?(?:!([^\\]\\|!]+))?\\]\\]" + ) + show figure.where(kind: "gloss"): set figure(supplement: [Example]) + show figure.where(kind: "gloss"): set figure.caption(position: top) + show figure: it => { + html.details({ + html.summary[*#it.caption.supplement #it.caption.counter.display(it.numbering)*: #it.caption.body] + it.body + }) + } + show regex("%[a-z0-9/]+"): it => tag(it.text.slice(1)) + show link-re: it => wl(..it.text.match(link-re).captures) + set table(stroke: none) + html.head({ + html.link(rel: "stylesheet", href: "/assets/style/common.css") + html.meta(charset: "utf-8") + html.meta(name: "viewport", content: "width=device-width, initial-scale=1") + html.meta(name: "search-title", content: if title-override != none {title-override} else {page-title}, id: "search-title") + context html.meta(content: { let t = query().map(it => it.value).join(" "); if type(t) != str {""} else {t}}, name: "search-tags", id: "search-tags") + + if title-override != none { + html.title(sitename + " — " + title-override) + } else { + html.title(sitename + " — " + page-title) + } + html.script(src:"/assets/scripts/fuse.min.js") + html.script(src: "/assets/scripts/search.js") + }) + + html.header({ + html.h1(html.a(href: "/", sitename)) + html.nav(html.input(id: "searchbox", type: "text", placeholder: "Search...")) + }) + html.div(id: "results")[] + html.div(id: "main-body", { + html.main({ + html.h1(page-title) + html.hr() + doc + + }) + html.aside(outline()) + }) +} +#let sitename = [Annwan's Wiki] +#let s(script, body) = { + html.span(class: "scr-"+script, body) +} +#let sc = smallcaps diff --git a/templates/utils/dict.typ b/templates/utils/dict.typ new file mode 100644 index 0000000..2f67f2c --- /dev/null +++ b/templates/utils/dict.typ @@ -0,0 +1,6 @@ +#let reg = (it) => [(#html.span(style: "font-style: italic", it))] +#let poet = reg[poetic] +#let arch = reg[archaic] +#let coll = reg[colloquial] +#let slang = reg[slang] +#let low = html.span.with(class: "low") diff --git a/templates/utils/gloss.typ b/templates/utils/gloss.typ new file mode 100644 index 0000000..2e2064c --- /dev/null +++ b/templates/utils/gloss.typ @@ -0,0 +1,39 @@ +#let gloss( + txt: none, + translit: none, + phono: none, + morphemes: none, + translation: none, + txt-style: it => it, + translit-style: it => it, + phono-style: it => it, + morphemes-style: it => it, + translation-style: it => it, + +) = { + assert(type(morphemes) == array) + assert(translation == none or type(translation) == content) + assert(txt == none or type(txt) == content or (type(txt) == array and txt.len() == morphemes.len())) + assert(translit == none or (type(translit) == array and translit.len() == morphemes.len())) + assert(translit == none or (type(phono) == array and phono.len() == morphemes.len())) + html.table(class: "gloss", + { + if type(txt) == content { + html.tr(html.td(colspan: morphemes.len(), txt-style(txt))) + } else if type(txt) == array { + html.tr(txt.map(txt-style).map(html.td).join()) + } + if translit != none { + html.tr(translit.map(translit-style).map(html.td).join()) + } + if phono != none { + html.tr(phono.map(phono-style).map(html.td).join()) + } + html.tr(morphemes.map(morphemes-style).map(html.td).join()) + if translation != none { html.tr(html.td(colspan: morphemes.len(),translation-style(translation))) } + } + ) + } +#let example(..g, lbl: none, caption: none) = [ + #figure(kind: "gloss", caption: caption, gloss(..g)) #if lbl != none {label(lbl)} +] diff --git a/templates/utils/lang-mos.typ b/templates/utils/lang-mos.typ new file mode 100644 index 0000000..7aaea4d --- /dev/null +++ b/templates/utils/lang-mos.typ @@ -0,0 +1,129 @@ +#import "/templates/base.typ" +#let mos-sec = [ += Mosici +#base.tag("word") #base.tag("mos") +] +#let sn = base.s.with("nahan") +#let mos-pro(ipa) = [ +- #base.wl("/worlds/Asteron/Mosici", [Standard], none) [#ipa] +] +#let mos-translit(s) = { + s .replace("", "a") + .replace("", "á") + .replace("", "c") + .replace("", "e") + .replace("", "é") + .replace("", "f") + .replace("", "h") + .replace("", "i") + .replace("", "í") + .replace("", "l") + .replace("", "m") + .replace("", "n") + .replace("", "o") + .replace("", "ó") + .replace("", "p") + .replace("", "r") + .replace("", "s") + .replace("", "t") + .replace("", "u") + .replace("", "v") + .replace("", "z") + .replace("", ",") + .replace("", "·") + .replace("", "’") +} +#let mos-cit(t) = [#sn(t) #html.span(class: "low", mos-translit(t))] +#let mos-nstem(w) = { + if w.ends-with("") {(w.clusters().slice(0, -2).join(), "")} + else if w.ends-with("") {(w.clusters().slice(0, -2).join(), "")} + else if w.ends-with("") {(w.clusters().slice(0, -2).join(), "")} + else if w.ends-with("") {(w.clusters().slice(0, -2).join(), "")} + else if w.ends-with("") {(w.clusters().slice(0, -1).join(), "")} + else if w.ends-with("") {(w.clusters().slice(0, -1).join(), "")} + else {(w, "")} +} +#let mos-vstem(w) = w.clusters().slice(0, -1).join() + +#let mos-pluralise(w) = { + // find index of last vowel + let revw = w.rev() + let pos = revw.position(regex("[]")) + let v = revw.at(pos) + if v == "" {v = ""} + if v == "" {v = ""} + if v == "" {v = ""} + if v == "" {v = ""} + revw = revw.slice(0, pos) + v + revw.slice(pos) + revw.rev() +} + + +#let mos-n(sg, pl: none) = { + let pr = if pl == false {""} else if pl != none {pl} else {mos-pluralise(sg)} + let (ps, pv) = mos-nstem(pr) + let (ss, sv) = mos-nstem(sg) + + let c(it) = [#base.s("nahan", it) \ #html.span(class: "low", mos-translit(it))] + html.details({ + html.summary[ *Declension for* _#mos-cit(sg)_ #if pl != none and pl != false [*Irr. pl. stem* _#mos-cit(pl)_] else if pl == false [*(Uncountable)*]] + table(columns: if pl != false {3} else {2}, + ..if pl != false { ([], + base.sc[*sg*], base.sc[*pl*],)} else {()}, + base.sc[*age*], c(sg), ..if pl != false {(c(pr),)} else {()}, + base.sc[*pat*], c(ss + ""), ..if pl != false {(c(ps + ""),)} else {()}, + base.sc[*gen*], c(ss + ""), ..if pl != false {(c(ps + ""),)} else {()}, + base.sc[*dat*], c(ss + sv + ""), ..if pl != false {(c(ps + pv + ""),)} else {()}, + base.sc[*abl*], c(ss + ""), ..if pl != false {(c(ps + ""),)} else {()}, + ) + }) +} +#let mos-v(w) = { + let s = mos-vstem(w) + let c(it) = [#base.s("nahan", it) \ #html.span(class: "low", mos-translit(it))] + html.details({ + html.summary[*Conjugation for* _#mos-cit(w)_] + table(columns: 5, + [], base.sc[*prs*], base.sc[*pst*], base.sc[*fut*], base.sc[*gno*], + base.sc[*1s*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*2s*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*3sa*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*3si*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*1pi*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*1pe*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*2p*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*3pa*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*3pi*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*inf*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*pcp.sg*], c(s+""), c(s+""), c(s+""), c(s+""), + base.sc[*pcp.pl*], c(s+""), c(s+""), c(s+""), c(s+"") + ) + }) +} + +#let mos-pron(this) = { + let c(k, h: false) = if h and k != this [ + #base.wl("dict/"+mos-translit(k), base.s("nahan", k), "mos") \ #html.span(class: "low", mos-translit(k)) + ] else [ + #base.s("nahan", k) \ #html.span(class: "low", mos-translit(k)) + ] + + html.details({ + html.summary[Mosici Pronouns] + table(columns: 6, + [], base.sc[*agt*], base.sc[*pat*], base.sc[*gen*], base.sc[*dat*], base.sc[*abl*], + base.sc[*1s*], c(h: true, ""), c(""), c(""), c(""), c(""), + base.sc[*1p*], c( ""), c(""), c(""), c(""), c(""), + base.sc[*2s*], c(h: true, ""), c(""), c(""), c(""), c(""), + base.sc[*2p*], c( ""), c(""), c(""), c(""), c(""), + base.sc[*3sa*], c(h: true, ""), c(""), c(""), c(""), c(""), + base.sc[*3pa*], c( ""), c(""), c(""), c(""), c(""), + base.sc[*3si*], c(h: true, ""), c(""), c(""), c(""), c(""), + base.sc[*3pi*], c( ""), c(""), c(""), c(""), c(""), + base.sc[*dem.sg*], c(h: true, ""), c(""), c(""), c(""), c(""), + base.sc[*dem.pl*], c( ""), c(""), c(""), c(""), c(""), + base.sc[*q*], c(h: true, ""), c(""), c(""), c(""), c(""), + ) + }) + +} diff --git a/templates/utils/notes.typ b/templates/utils/notes.typ new file mode 100644 index 0000000..976f05f --- /dev/null +++ b/templates/utils/notes.typ @@ -0,0 +1,186 @@ +/* + Keep track of a running note counter, and associated notes. +*/ + +#let note_state_prefix = "notes-" +#let note_default_group = "default" + +#let note_default_display_fn(note) = { + h(0pt, weak: true) + super([[#note.index]]) +} + +#let add_note( + // The location of the note. This is used to derive + // what the note counter should be for this note. + loc, + // The note itself. + text, + // The offset which will be added to the 0-based counter index + // when the index stored in the state. + offset: 1, + // the display function that creates the returned content from put. + // Put can't return a pure index number because the counter + // and state updates need to output content. + display: note_default_display_fn, + // The state group to track notes in. + // A group acts independent (both counter and set of notes) + // from other groups. + group: note_default_group +) = { + let s = state(note_state_prefix + group, ()) + let c = counter(note_state_prefix + group) + // find any existing note that hasn't been printed yet, + // containing the exact same text: + let existing = s.at(loc).filter((n) => n.text == text) + // If we found an existing note use that, + // otherwise the note is the current location's counter + offset + // and the given text (the counter is 0-based, we want 1-based indices) + let note = if existing.len() > 0 { + existing.first() + } else { + (text: text, index: c.at(loc).first() + offset, page: loc.page()) + } + + // If we didn't find an existing index, increment the counter + // and add the note to the "notes" state. + if existing.len() == 0 { + c.step() + s.update(notes => notes + (note,)) + } + + // Output the note marker + display(note) +} + +// get notes at specific location +#let get_notes(loc, group: note_default_group) = { + state(note_state_prefix + group, ()).at(loc) +} + +// Reset the note group to empty. +// Note: The counter does not reset by default. +#let reset_notes(group: note_default_group, reset_counter: false) = { + if reset_counter { + counter(note_state_prefix + group).update(0) + } + state(note_state_prefix + group, ()).update(()) +} + +// +// Helpers for nicer in-document ergonomics +// + +#let render_notes(fn, group: note_default_group, reset: true, reset_counter: false) = { + locate(loc => fn(get_notes(loc, group: group))) + if reset { + reset_notes(group: group, reset_counter: reset_counter) + } +} + +// Create a new note at the current location +#let note(note, ..args) = { + locate((loc) => add_note(loc, note, ..args)) +} + +// The quick-start option that outputs something useful by default. +// This is a sane-defaults call to `render_notes`. +#let notes( + size: 8pt, + font: "Roboto", + line: line(length: 100%, stroke: 1pt + gray), + padding: (top: 3mm), + alignment: bottom, + numberings: "1", + group: note_default_group, + reset: true, + reset_counter: false +) = { + let render(notes) = { + if notes.len() > 0 { + set align(alignment) + block(breakable: false, pad(..padding, { + if line != none { line } + set text(size: size, font: font) + for note in notes { + [/ #text(font: "Roboto Mono")[[#numbering(numberings, note.index)]]: #note.text] + } + })) + } + } + render_notes(group: group, reset: reset, reset_counter: reset_counter, render) +} + +// +// Examples +// + +#set page(height: 5cm, width: 15cm) + += Example + +- Having a flexible note-keeping system is important #note[Citation needed] +- It's pretty easy to implement with Typst #note[Fact] +- Everyone really likes the name "Typst" #note[Citation needed] + + + +// Print notes since last print: +#notes() +#pagebreak() + +These notes won't be printed on this page, so they accumulate onto next page. + +- Hello World #note[Your first program] +- Foo Bar #note[Does not serve beverages] +#pagebreak() + +- Orange #note[A Color] +- Blue #note[A Color] +// Notice that the "Citation needed" gets a new index +// because we've re-used it since we printed the initial "Citation needed" +- All colors are great #note[Citation needed] +#notes() +#pagebreak() + +#set page( + height: 8cm, + footer: notes(padding: (bottom: 5mm)), + margin: (rest: 5mm, bottom: 3cm) +) + += Footnotes + +It is of course also possible to place the notes _in_ the page footer. +This is the way to implement footnotes. + +- Black#note[Debateably a color] +- White#note[Debateably a color] +#pagebreak() + +- Orange#note[A Color] +- Blue#note[A Color] +- Purple#note[Also a color] + +#pagebreak() + += Note groups + +This page still has footnotes (using the default note group) +but we can also name a new group for custom notes. + +#let mynote = note.with(group: "custom") +#let mynotes = notes.with( + group: "custom", + font: "Comic Neue", + size: 12pt, + numberings: "a.", + line: none, + alignment: right + top +) + +- This is in it's own group#mynote[Custom] +- Regular footnote here#note[Regular footnote] +- Same custom note#mynote[Custom] + +#mynotes() \ No newline at end of file