Compare commits

..

15 Commits

Author SHA1 Message Date
3f0d36015b update readme 2026-03-31 23:27:55 +08:00
Codex
3a3fd97055 Split TUI from default watch mode 2026-03-30 11:05:44 +08:00
aa05d73c9c Clean up CSS and update default styles
Normalize formatting across CSS files and fix selector whitespace
and quote usage. Change default link color to browser blue and
enable smooth scrolling; hover underlines links. Revamp table
styles (borders, caption, zebra rows, padding, footer alignment).
Adjust proof/theorem header spacing and minor spacing/padding fixes.
2026-03-30 10:42:25 +08:00
3652459503 Add Unix and enable stdin-driven quit in watch TUI 2026-03-29 13:48:43 +08:00
fc4cac00d5 format code 2026-03-29 13:38:58 +08:00
55719f3444 fix a tui bug 2026-03-26 15:57:32 +08:00
d4629ec8e7 update readme 2026-03-26 15:57:02 +08:00
e419366615 better tui for watch 2026-03-24 21:05:23 +08:00
6c59abb9cc Introduce CLI, watch server, and bundled assets
Add data-files and bundled templates/fonts/css to the package and
rename the executable to hakysidian (autogen Paths_hakysidian).
Refactor site.hs to parse CLI commands (build/clean/rebuild/watch),
start a preview server, snapshot inputs and run an incremental watch
loop, and move rules into a siteRules function. Update ChaoDoc and
filters to accept math-macros, and add favicon links to templates.
2026-03-24 20:47:46 +08:00
6a3b4c5f88 Remove hakyll-blog.cabal and update .Proof CSS 2026-03-24 20:06:48 +08:00
720a19e24d Remove .notes-list custom styles 2026-03-24 20:01:35 +08:00
1789c75f18 Revert "Rename package to hakysidian; add CLI and assets"
This reverts commit 3d2c5a8852.
2026-03-24 19:58:26 +08:00
3d2c5a8852 Rename package to hakysidian; add CLI and assets 2026-03-24 18:59:06 +08:00
614de591ba Add notes list and integrate into templates
New partial templates/notes-list.html to render the notes list.
Add .notes-list CSS for styling and ensure contents areas use it.
Refactor site.hs: add loadNoteLinks and provide a "notes" listField
used by index and note pages.
2026-03-24 14:56:45 +08:00
71611b0641 Increase body line-height and add theorem padding 2026-03-23 14:54:09 +08:00
15 changed files with 1341 additions and 946 deletions

View File

@@ -1,25 +1,12 @@
.theorem-environment {
position: relative;
margin: 1.5rem 0;
padding: 1rem 1.25rem;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 1rem;
box-shadow: var(--color-shadow-soft);
font-style: italic;
}
.theorem-environment > :first-child {
margin-top: 0;
}
.theorem-environment > :last-child {
margin-bottom: 0;
margin-top: 1em;
padding: 0.5em;
background-color: whitesmoke;
}
.theorem-header {
color: var(--color-text);
font-weight: 700;
font-weight: bold;
font-style: normal;
}
@@ -43,63 +30,52 @@
display: inline;
}
.Proof {
padding-right: 2.5rem;
font-style: normal;
.Proof .type {
font-style: italic;
font-weight: normal;
}
.Proof .type {
color: var(--color-linkh);
font-weight: 400;
font-style: italic;
.Proof {
background: none;
font-style: normal;
position: relative;
}
.Proof:after {
content: "\220e";
content: "";
position: absolute;
right: 1rem;
bottom: 0.85rem;
color: var(--color-tag1);
right: 0px;
bottom: 0px;
}
.Proof span.theorem-header span.name {
font-weight: normal;
font-style: italic;
}
.Proof span.theorem-header span.name:before {
content: " ";
}
.Proof span.theorem-header span.name:after {
content: " ";
}
table.postindex {
width: 100%;
border: 0;
border-collapse: separate;
border-spacing: 0;
background: transparent;
box-shadow: none;
}
table.postindex cite {
font-style: normal;
}
table.postindex td {
padding: 0.65rem 0;
border: 0;
border-bottom: 1px solid var(--color-border);
background: transparent;
}
table.postindex tr:nth-child(even) td {
background: transparent;
}
table.postindex tr:last-child td {
border-bottom: 0;
}
table.postindex td.right {
width: 11ex;
color: var(--color-tag1);
text-align: right;
white-space: nowrap;
width: 11ex;
}
.header-section-number {
margin-right: 0.55rem;
color: var(--color-tag1);
margin-right: 10px;
}
.header-section-number:after {
@@ -109,22 +85,19 @@ table.postindex td.right {
.csl-entry {
display: table;
width: 100%;
margin-bottom: 0.75rem;
table-layout: auto;
}
.csl-left-margin {
display: table-cell;
width: 1px;
padding-right: 0.75em;
color: var(--color-tag1);
padding-right: 0.5em;
white-space: nowrap;
width: 1px;
}
.csl-right-inline {
display: table-cell;
}
.csl-right-inline a {
word-break: break-all;
}

View File

@@ -1,61 +1,19 @@
:root {
color-scheme: light;
--color-text: #162031;
--color-tag1: #6c788c;
--color-tag2: #547157;
--color-bg: #f3f6fb;
--color-surface: rgba(255, 255, 255, 0.84);
--color-surface-strong: #ffffff;
--color-surface-muted: #edf3fb;
--color-border: rgba(22, 32, 49, 0.12);
--color-border-strong: rgba(22, 32, 49, 0.2);
--color-link: #225b9c;
--color-link-line: rgba(34, 91, 156, 0.28);
--color-linkhbg: rgba(89, 143, 211, 0.16);
--color-linkh: #163a63;
--color-bq: #628b72;
--color-notice: #d0455a;
--color-shadow: 0 20px 50px rgba(15, 23, 42, 0.08);
--color-shadow-soft: 0 10px 30px rgba(15, 23, 42, 0.06);
--code-bg: #f6f8fc;
--code-border: rgba(55, 65, 81, 0.14);
--page-glow-1: rgba(144, 187, 241, 0.18);
--page-glow-2: rgba(99, 141, 118, 0.14);
--color-text: black;
--color-tag1: gray;
--color-tag2: darkolivegreen;
--color-bg: white;
--color-link: #0000ee;
--color-notice: #fb4f4f;
}
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
--color-text: #e7edf7;
--color-tag1: #9aa7bb;
--color-tag2: #9abf9c;
--color-bg: #0b1020;
--color-surface: rgba(15, 23, 42, 0.78);
--color-surface-strong: #101827;
--color-surface-muted: #162238;
--color-border: rgba(148, 163, 184, 0.18);
--color-border-strong: rgba(148, 163, 184, 0.26);
--color-link: #8bc3ff;
--color-link-line: rgba(139, 195, 255, 0.35);
--color-linkhbg: rgba(139, 195, 255, 0.16);
--color-linkh: #d9ecff;
--color-bq: #8db59a;
--color-notice: #ff8b8b;
--color-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
--color-shadow-soft: 0 12px 32px rgba(0, 0, 0, 0.3);
--code-bg: #0f172a;
--code-border: rgba(148, 163, 184, 0.22);
--page-glow-1: rgba(71, 119, 183, 0.22);
--page-glow-2: rgba(74, 118, 90, 0.18);
--color-text: white;
--color-bg: black;
}
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
scrollbar-gutter: stable;
scroll-behavior: smooth;
@@ -63,102 +21,45 @@ html {
}
body {
box-sizing: content-box;
margin: 0;
font-family: "Lato", -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
font-family:
"Lato",
-apple-system,
BlinkMacSystemFont,
"PingFang SC",
"Microsoft YaHei",
sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
font-size: 1rem;
line-height: 1.5;
line-height: 140%;
color: var(--color-text);
background:
radial-gradient(circle at top, var(--page-glow-1), transparent 38%),
radial-gradient(circle at right top, var(--page-glow-2), transparent 30%),
var(--color-bg);
background-attachment: fixed;
background-color: var(--color-bg);
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
body.lang-zh {
text-align: left;
}
::selection {
background-color: var(--color-linkhbg);
color: var(--color-linkh);
}
main,
.text-space {
min-width: 0;
text-autospace: no-autospace; /*using pangu.hs*/
}
body a {
color: var(--color-link);
text-decoration: none;
}
body a:hover {
text-decoration: underline;
text-decoration-thickness: 0.08em;
text-underline-offset: 0.18em;
text-decoration-color: var(--color-link-line);
transition:
color 0.18s ease,
background-color 0.18s ease,
text-decoration-color 0.18s ease;
-webkit-box-decoration-break: clone;
box-decoration-break: clone;
}
body a:visited {
color: var(--color-link);
}
.text-space a:hover {
background-color: var(--color-linkhbg);
color: var(--color-linkh);
text-decoration-color: transparent;
border-radius: 0.2rem;
}
body a:focus-visible {
outline: 2px solid var(--color-link);
outline-offset: 3px;
border-radius: 0.25rem;
}
details {
margin: 1.25rem 0;
padding: 0.85rem 1rem;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 1rem;
box-shadow: var(--color-shadow-soft);
padding-left: 1em;
border: 2px solid var(--color-text);
}
details[open] summary {
margin-bottom: 0.75rem;
}
details > :last-child {
margin-bottom: 0;
}
summary {
font-weight: 700;
}
summary:hover {
cursor: pointer;
color: var(--color-linkh);
}
/* mathML */
/*mathML*/
.htmlmathparagraph,
mtext,
math {
font-family: "Lete Sans Math";
font-family: Lete Sans Math;
}
.math-container,
#math-container {
display: block;
@@ -205,145 +106,77 @@ math {
font-variant-caps: small-caps;
}
p {
margin: 0 0 1rem;
hyphens: auto;
}
a.url {
word-break: break-all;
}
html body div.text-space main ul.post-list {
list-style-type: none;
padding-left: 1em;
}
/* top bar */
header {
font-weight: 400;
font-family: "IosevkaC", sans-serif;
}
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
.navright {
display: flex;
flex-wrap: wrap;
}
.navright a {
margin: 0 0 0 1em;
}
.navbar a {
color: var(--color-text);
text-decoration: none;
}
.navbar a:visited {
color: var(--color-text);
}
nav {
margin-bottom: 1.5rem;
padding-bottom: 0.75rem;
text-align: right;
border-bottom: 1px solid var(--color-border-strong);
}
nav a {
display: inline-flex;
align-items: center;
vertical-align: middle;
font-size: 1.05rem;
display: inline-block;
text-decoration: none;
}
.uri {
overflow-wrap: anywhere;
word-break: break-word;
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-all;
white-space: normal;
}
footer {
margin-top: 3rem;
padding-right: 0.2rem;
color: var(--color-tag1);
font-size: 0.82rem;
color: var(--color-text);
font-size: 0.8rem;
margin-top: 2em;
text-align: right;
}
h1,
h2,
h3,
h4,
h5,
h6 {
text-align: left;
text-wrap: balance;
padding-right: 1em;
}
.pagetitle {
margin: 0;
font-size: 2rem;
font-weight: 400;
font-weight: normal;
font-style: normal;
text-align: left;
line-height: 1;
letter-spacing: -0.03em;
line-height: 100%;
}
h1 {
margin-top: 1.5rem;
margin-bottom: 0.75rem;
margin-top: 1em;
font-size: 1.44rem;
font-weight: 700;
font-weight: bold;
font-style: normal;
line-height: 1.1;
letter-spacing: -0.02em;
}
h2 {
margin-top: 1.75rem;
margin-bottom: 0.7rem;
margin-top: 1em;
font-size: 1.2rem;
font-weight: 700;
font-weight: bold;
font-style: normal;
line-height: 1.2;
}
h3 {
margin-top: 1.5rem;
margin-bottom: 0.6rem;
margin-top: 1em;
font-size: 1rem;
font-weight: 700;
font-weight: bold;
font-style: normal;
line-height: 1.25;
}
h4,
h5,
h6 {
margin-top: 1.35rem;
margin-bottom: 0.55rem;
line-height: 1.3;
}
article .header {
font-size: 1rem;
font-style: normal;
color: var(--color-tag1);
font-size: 1rem;
font-style: normal;
text-align: left;
letter-spacing: 0.02em;
}
.info {
color: var(--color-tag2);
font-size: 1rem;
font-style: normal;
text-align: left;
}
.info,
.info a {
color: var(--color-tag2);
font-size: 1rem;
@@ -356,351 +189,200 @@ article .header {
}
section.body {
margin-top: 1.75rem;
}
section.body > :first-child {
margin-top: 0;
margin-top: 2rem;
}
.ascii-art {
overflow-x: auto;
font-family: monospace;
line-height: 1.2;
}
blockquote {
margin: 1.5rem 0;
padding: 1rem 1.25rem;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-left: 4px solid var(--color-bq);
border-radius: 0 1rem 1rem 0;
box-shadow: var(--color-shadow-soft);
}
blockquote p {
margin: 0;
}
ol,
ul {
margin: 0 0 1.1rem;
padding-left: 2em;
}
ul {
list-style-type: square;
}
li {
margin-bottom: 0.3em;
padding-left: 0.15em;
}
li::marker {
color: var(--color-tag2);
line-height: normal;
}
/* table. copied from https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/table */
table {
margin: 1.5rem 0;
border: 1px solid var(--color-border-strong);
border-collapse: collapse;
background-color: var(--color-surface-strong);
box-shadow: var(--color-shadow-soft);
border: 2px solid rgb(140 140 140);
font-size: 0.8rem;
letter-spacing: 1px;
}
caption {
caption-side: bottom;
padding: 10px;
font-weight: bold;
}
thead,
tfoot {
background-color: rgb(228 240 245);
}
th,
td {
padding: 0.65rem 0.8rem;
border: 1px solid var(--color-border);
text-align: left;
vertical-align: top;
border: 1px solid rgb(160 160 160);
padding: 8px 10px;
}
th {
background-color: var(--color-surface-muted);
td:last-of-type {
text-align: center;
}
tr:nth-child(even) td {
background-color: var(--color-surface);
tbody > tr:nth-of-type(even) {
background-color: rgb(237 238 242);
}
tfoot th {
text-align: right;
}
tfoot td {
font-weight: bold;
}
figure {
display: flex;
flex-flow: column;
gap: 0.75rem;
padding: 0.9rem;
margin: 1.5rem auto;
padding: 5px;
margin: auto;
max-width: 80%;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 1.25rem;
box-shadow: var(--color-shadow-soft);
}
figure img {
border-radius: 0.9rem;
}
figcaption {
padding: 0 0.25rem;
color: var(--color-tag1);
font-size: 0.92rem;
text-align: center;
}
.caption {
display: none;
}
.centerimg img {
margin: 0 auto 0 auto;
display: block;
margin: 0 auto;
}
pre,
div.highlight {
margin: 1.5rem 0;
padding: 1rem 1.1rem;
div.highlight,
pre code {
margin: auto;
padding: 10px;
overflow: auto;
background-color: var(--code-bg);
border: 1px solid var(--code-border);
border-radius: 1rem;
box-shadow: var(--color-shadow-soft);
}
pre code,
div.highlight pre,
div.highlight pre code {
display: block;
margin: 0;
padding: 0;
overflow: visible;
background: transparent;
border: 0;
box-shadow: none;
}
code {
display: inline;
margin: 0;
padding: 0.12em 0.35em;
background-color: var(--code-bg);
border: 1px solid var(--code-border);
border-radius: 0.45rem;
font-family: "IosevkaC", monospace;
font-size: 0.92em;
margin: 0 auto;
display: inline-block;
padding: 0px 2px;
border-radius: 2px;
font-variant-ligatures: none;
font-kerning: none;
text-rendering: optimizeSpeed;
}
.draft-notice {
display: table;
margin: 1.5rem auto;
padding: 0.85rem 1rem;
color: var(--color-notice);
margin: 1em auto;
text-align: center;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-left: 4px solid var(--color-notice);
border-radius: 1rem;
box-shadow: var(--color-shadow-soft);
}
.subtitle {
margin-top: 0.5rem;
color: var(--color-tag1);
font-size: 1.2rem;
line-height: 1.4;
text-align: left;
font-size: 1.2rem;
margin-top: 0;
}
.subtitle p {
margin: 0.2rem 0;
}
.gallery {
margin-top: 2em;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin-top: 2em;
gap: 12px;
}
.gallery img {
display: block;
width: 100%;
max-width: 320px;
border-radius: 1rem;
box-shadow: var(--color-shadow-soft);
display: block;
}
hr {
margin: 2rem 0;
border: 0;
border-top: 1px solid var(--color-border-strong);
/* phones -- no sidebar no sidenotes*/
@media (max-width: 768px) {
body {
/* width: 90%; */
margin: auto;
padding: 0 5%;
text-align: left;
max-width: 876px;
}
mjx-container[display="true"]
/*, .katex-display */ {
overflow-x: auto;
overflow-y: hidden;
}
/* .katex-display>.katex>.katex-html>.tag {
display: inline-block;
position: relative;
padding-left: 10pt;
} */
}
.toc {
display: none;
}
/* phones: no sidebar, no sidenotes */
@media (max-width: 768px) {
body {
max-width: 876px;
margin: auto;
padding: 1rem 5% 3rem;
text-align: left;
background-attachment: scroll;
}
mjx-container[display="true"] {
overflow-x: auto;
overflow-y: hidden;
}
figure {
max-width: 100%;
}
}
/* sidebar, no sidenotes */
/* sidebar. no sidenotes */
@media (min-width: 769px) {
body {
display: flex;
max-width: 1350px;
display: -webkit-flex;
-webkit-flex-flow: row wrap;
display: -ms-flexbox;
-ms-flex-flow: row wrap;
flex-flow: row wrap;
width: 95%;
max-width: 1350px;
padding-right: 5%;
margin: auto;
padding: 1.5rem 5% 3rem 0;
}
.toc {
display: inline-block;
width: 33%;
margin-top: 4.5rem;
margin-top: 5rem;
margin-left: 0;
margin-right: 0;
width: 33%;
display: inline-block;
}
div#contents ul,
div#contents-big ul {
margin-top: 0.3em;
margin-bottom: 0.3em;
margin-left: 0.1rem;
padding-left: 1.45rem;
line-height: 1.25;
margin-top: 0.5em;
margin-bottom: 0.5em;
padding-left: 1em;
line-height: 1.2;
list-style-type: decimal;
margin-left: 0;
}
div#contents ul.notes-list,
div#contents-big ul.notes-list {
list-style: none;
padding-left: 0;
}
div#contents-big ul ul {
list-style-type: none;
margin-top: 0.15rem;
margin-left: 0.2rem;
padding-left: 0.8rem;
border-left: 1px solid var(--color-border);
}
div#contents-big li + li {
margin-top: 0.18rem;
}
div#contents-big li::marker {
color: var(--color-tag1);
font-family: "IosevkaC", sans-serif;
margin-top: 0.5em;
}
div#contents-big {
position: sticky;
top: 6rem;
left: 100%;
clear: both;
max-width: 68%;
margin-right: 4em;
padding: 0.8rem 1.15rem 0.9rem 1.45rem;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 1.25rem;
box-shadow: var(--color-shadow-soft);
backdrop-filter: blur(14px);
font-size: 80%;
padding-top: 0;
padding-left: 1rem;
text-align: left;
max-width: 60%;
clear: both;
margin-right: 4em;
position: sticky;
top: 5rem;
left: 100%;
}
div#contents-big .mini-header {
margin: 0 0 0.45rem;
color: var(--color-tag1);
font-weight: 700;
font-weight: bold;
margin: 0;
font-variant: small-caps;
letter-spacing: 0.08em;
}
div#contents-big a {
color: var(--color-text);
text-decoration: none;
}
div#contents-big li > a {
display: block;
margin-left: 0;
padding: 0.14rem 0.32rem;
border-radius: 0.55rem;
border: 1px solid transparent;
transition:
color 0.18s ease,
background-color 0.18s ease,
border-color 0.18s ease,
transform 0.18s ease;
}
div#contents-big a:visited {
color: var(--color-text);
}
div#contents-big a:hover {
background-color: var(--color-linkhbg);
color: var(--color-linkh);
}
div#contents-big li > a:hover,
div#contents-big li > a:focus-visible {
border-color: var(--color-border);
transform: translateX(2px);
}
div#contents-big li > a:focus-visible,
div#contents-big .mini-header a:focus-visible {
outline: 2px solid var(--color-link);
outline-offset: 2px;
}
div#contents-big .mini-header a {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.25rem;
height: 1.25rem;
margin-left: 0.25rem;
background-color: var(--color-surface-muted);
border: 1px solid var(--color-border);
border-radius: 999px;
color: var(--color-link);
font-size: 0.78rem;
line-height: 1;
vertical-align: middle;
transition:
color 0.18s ease,
background-color 0.18s ease,
border-color 0.18s ease;
}
div#contents-big .mini-header a:hover {
background-color: var(--color-linkhbg);
border-color: var(--color-border-strong);
color: var(--color-linkh);
transform: none;
}
.text-space {
@@ -709,8 +391,7 @@ hr {
max-width: 875px;
}
}
/* sidebar + sidenotes */
/* sidebar+sidenotes */
@media (min-width: 1200px) {
body {
width: 75%;
@@ -726,16 +407,5 @@ hr {
body {
margin: auto;
background: white;
}
blockquote,
details,
div#contents-big,
figure,
pre,
div.highlight,
table {
box-shadow: none;
}
}

View File

@@ -1,52 +1,51 @@
/* fonts */
@font-face {
font-family: "Lato";
src: url("/fonts/Lato-Regular.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-family: "Lato";
src: url("/fonts/Lato-Regular.woff2") format("woff2");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "Lato";
src: url("/fonts/Lato-Bold.woff2") format("woff2");
font-weight: bold;
font-style: normal;
font-family: "Lato";
src: url("/fonts/Lato-Bold.woff2") format("woff2");
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: "Lato";
src: url("/fonts/Lato-Italic.woff2") format("woff2");
font-weight: normal;
font-style: italic;
font-family: "Lato";
src: url("/fonts/Lato-Italic.woff2") format("woff2");
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: "Lato";
src: url("/fonts/Lato-BoldItalic.woff2") format("woff2");
font-weight: bold;
font-style: italic;
font-family: "Lato";
src: url("/fonts/Lato-BoldItalic.woff2") format("woff2");
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: "Lete Sans Math";
src: url("/fonts/LeteSansMath.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-family: "Lete Sans Math";
src: url("/fonts/LeteSansMath.woff2") format("woff2");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "Lete Sans Math";
src: url("/fonts/LeteSansMath-Bold.woff2") format("woff2");
font-weight: bold;
font-style: normal;
font-family: "Lete Sans Math";
src: url("/fonts/LeteSansMath-Bold.woff2") format("woff2");
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: "IosevkaC";
src: url("/fonts/IosevkaCustom-Regular.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-family: "IosevkaC";
src: url("/fonts/IosevkaCustom-Regular.woff2") format("woff2");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "IosevkaC";
src: url("/fonts/IosevkaCustom-Bold.woff2") format("woff2");
font-weight: bold;
font-style: normal;
font-family: "IosevkaC";
src: url("/fonts/IosevkaCustom-Bold.woff2") format("woff2");
font-weight: bold;
font-style: normal;
}

View File

@@ -1,79 +1,35 @@
:root {
--syntax-alert: #c2410c;
--syntax-annotation: #0f766e;
--syntax-attribute: #2563eb;
--syntax-number: #7c3aed;
--syntax-control: #4d7c0f;
--syntax-char: #0f766e;
--syntax-constant: #9f1239;
--syntax-comment: #7b8794;
--syntax-doc: #b45309;
--syntax-type-bg: #efe7ff;
--syntax-error: #dc2626;
--syntax-function: #0f4c81;
--syntax-import: #0d9488;
--syntax-preprocessor: #92400e;
--syntax-special: #0369a1;
--syntax-string: #15803d;
--syntax-variable: #1d4ed8;
}
@media (prefers-color-scheme: dark) {
:root {
--syntax-alert: #fdba74;
--syntax-annotation: #99f6e4;
--syntax-attribute: #93c5fd;
--syntax-number: #d8b4fe;
--syntax-control: #bef264;
--syntax-char: #86efac;
--syntax-constant: #fda4af;
--syntax-comment: #94a3b8;
--syntax-doc: #fcd34d;
--syntax-type-bg: rgba(216, 180, 254, 0.16);
--syntax-error: #f87171;
--syntax-function: #bfdbfe;
--syntax-import: #5eead4;
--syntax-preprocessor: #fdba74;
--syntax-special: #7dd3fc;
--syntax-string: #86efac;
--syntax-variable: #c4b5fd;
}
code.sourceCode
{
background: inherit
}
code.sourceCode {
background: inherit;
}
pre > code.sourceCode > span > a:first-child::before {
text-decoration: underline;
}
code span.al { color: var(--syntax-alert); font-weight: 700; }
code span.an { color: var(--syntax-annotation); font-weight: 700; font-style: italic; }
code span.at { color: var(--syntax-attribute); }
code span.bn { color: var(--syntax-number); }
code span.bu { color: var(--syntax-function); }
code span.cf { color: var(--syntax-control); font-weight: 700; }
code span.ch { color: var(--syntax-char); }
code span.cn { color: var(--syntax-constant); }
code span.co { color: var(--syntax-comment); font-style: italic; }
code span.cv { color: var(--syntax-annotation); font-weight: 700; font-style: italic; }
code span.do { color: var(--syntax-doc); font-style: italic; }
code span.dt { background-color: var(--syntax-type-bg); }
code span.dv { color: var(--syntax-number); }
code span.er { color: var(--syntax-error); font-weight: 700; }
code span.ex { color: var(--syntax-function); }
code span.fl { color: var(--syntax-number); }
code span.fu { color: var(--syntax-function); }
code span.im { color: var(--syntax-import); }
code span.in { color: var(--syntax-annotation); font-weight: 700; font-style: italic; }
code span.kw { color: var(--syntax-control); font-weight: 700; }
code span.op { color: var(--syntax-function); font-weight: 700; }
code span.ot { color: var(--syntax-special); font-weight: 700; }
code span.pp { color: var(--syntax-preprocessor); }
code span.sc { color: var(--syntax-special); }
code span.ss { color: var(--syntax-number); }
code span.st { color: var(--syntax-string); }
code span.va { color: var(--syntax-variable); }
code span.vs { color: var(--syntax-special); }
code span.wa { color: var(--syntax-annotation); font-weight: 700; font-style: italic; }
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
code span.al { color: #CB4B16; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #D33682; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #5F8700; font-weight: bold; } /* ControlFlow */
code span.ch { color: #16801a; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #93A1A1; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { background-color: #f8edff; } /* DataType */
code span.dv { color: #D33682; } /* DecVal */
code span.er { color: #D30102; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #D33682; } /* Float */
code span.fu { } /* Function */
code span.im { color: #D70000} /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { font-weight: bold; } /* Keyword */
code span.op { font-weight: bold; } /* Operator */
code span.ot { font-weight: bold; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #16801a; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */

View File

@@ -19,148 +19,127 @@ body {
.sidenote,
.marginnote,
.marginnote-left {
position: relative;
float: right;
clear: right;
width: 36%;
margin-top: 0.45rem;
margin-right: -42%;
width: 36%;
margin-top: 0.3rem;
margin-bottom: 0;
padding: 0.85rem 1rem;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 1rem;
box-shadow: var(--color-shadow-soft);
backdrop-filter: blur(14px);
font-size: 0.8em;
line-height: 1.2;
vertical-align: baseline;
font-size: 0.82em;
line-height: 1.45;
position: relative;
text-align: left;
}
@media (max-width: 1200px) {
.sidenote,
.marginnote,
.marginnote-left {
width: 33%;
margin-right: -40%;
width: 33%;
}
}
.marginnote-left {
position: relative;
float: left;
clear: left;
width: 25%;
margin-left: -32%;
width: 25%;
position: relative;
text-align: right;
}
/* The first condition is for the case of a left-aligned layout
(on a smaller screen), and the second condition for a more centered
layout on a larger screen. */
@media (max-width: 1349px), (min-width: 1367px) and (max-width: 1620px) {
/* The first condition is for the case of a left-aligned layout (on a
smaller screen), and the second condition for a more centered layout
on a larger screen. It's a bit awkward, sadly :/ */
@media (max-width: 1349px) or ((min-width: 1367px) and (max-width: 1620px)) {
.marginnote-left {
width: 30%;
margin-left: -33%;
width: 30%;
}
}
.sidenote > :first-child,
.marginnote > :first-child,
.marginnote-left > :first-child {
margin-top: 0;
}
.sidenote > :last-child,
.marginnote > :last-child,
.marginnote-left > :last-child {
margin-bottom: 0;
}
.sidenote code {
font-size: 0.92em;
font-size: 0.94em;
}
/* Block code in side and margin notes gets a vertical scrollbar on some
browsers; disable that. */
/* For some reason, although only `overflow-x` is set in `default.css`,
block code in side and marginnotes gets a vertical (!) scrollbar no
matter what; disable that.
*/
div .marginnote pre,
div .sidenote pre {
margin: 0.75rem 0;
overflow-y: hidden;
}
.sidenote-number {
color: var(--color-link);
counter-increment: sidenote-counter;
font-family: "IosevkaC", sans-serif;
color: var(--color-link);
}
.sidenote-number:after,
.sidenote:before {
position: relative;
vertical-align: baseline;
color: var(--color-link);
font-family: "IosevkaC", sans-serif;
}
.sidenote-number:after {
content: counter(sidenote-counter);
top: -0.5rem;
font-size: 0.8rem;
font-weight: 700;
top: -0.5rem;
}
/* Properly position sidenote number and adjust position of sidenote
/* Properly position siednote number and adjust position of sidenote
paragraphs:
https://github.com/edwardtufte/tufte-css/issues/93#issuecomment-670695382
*/
.sidenote::before {
content: counter(sidenote-counter) " ";
position: absolute;
top: -0.55rem;
right: calc(100% + 0.5em);
font-size: 0.8rem;
font-weight: 700;
top: -0.55rem;
position: absolute;
right: calc(100% + 0.5em);
}
.sidenote p {
margin: 0.85em 0;
margin: 1em 0;
}
.sidenote p:first-child {
margin-top: 0;
}
.sidenote p:last-child {
margin-bottom: 0;
}
/* */
input.margin-toggle {
display: none;
}
label.sidenote-number {
display: inline-block;
max-height: 2rem;
max-height: 2rem; /* should be less than or equal to paragraph line-height */
}
label.margin-toggle:not(.sidenote-number) {
display: none;
color: var(--color-link);
font-family: "IosevkaC", sans-serif;
}
.iframe-wrapper {
position: relative;
height: 0;
padding-bottom: 56.25%; /* 16:9 */
padding-top: 25px;
padding-bottom: 56.25%;
overflow: hidden;
background-color: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 1rem;
box-shadow: var(--color-shadow-soft);
height: 0;
}
.iframe-wrapper iframe {
position: absolute;
inset: 0;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
}
@media (max-width: 1200px) {
@@ -174,24 +153,23 @@ label.margin-toggle:not(.sidenote-number) {
display: none;
}
/* Linkify sidenotes iff they are clickable. */
/* Linkify sidenotes iff they are clickable */
.margin-toggle,
.sidenote-number:after {
color: var(--color-link);
text-decoration: underline;
text-decoration-color: var(--color-link-line);
text-decoration: none;
}
.margin-toggle:checked + .sidenote,
.margin-toggle:checked + .marginnote,
.margin-toggle:checked + .marginnote-left {
position: relative;
display: block;
float: left;
left: 1rem;
clear: both;
display: block;
width: 95%;
margin: 1rem 2.5%;
position: relative;
text-align: left;
}

View File

@@ -1,13 +1,22 @@
name: hakyll-blog
cabal-version: 2.4
name: hakysidian
version: 0.1.0.0
build-type: Simple
cabal-version: >= 1.10
data-files:
bib_style.csl
favicon.ico
css/*.css
fonts/*.otf
fonts/*.ttf
fonts/*.woff2
templates/*.html
executable site
executable hakysidian
hs-source-dirs: src
main-is: site.hs
other-modules: ChaoDoc, SideNoteHTML, Pangu
other-modules: ChaoDoc, SideNoteHTML, Pangu, Paths_hakysidian
autogen-modules: Paths_hakysidian
build-depends: base >= 4.18
, hakyll >= 4.15
, mtl >= 2.2.2
@@ -20,7 +29,13 @@ executable site
-- , process
-- , regex-compat
, array
, directory
, filepath
, process
, time
, unix
, wai-app-static
, warp
-- , ghc-syntax-highlighter
-- , blaze-html >= 0.9
, megaparsec

View File

@@ -1,5 +1,6 @@
COMMANDS := build watch rebuild clean
BIN := hakysidian
.PHONY: $(COMMANDS), publish
# Set the default goal, so running 'make' without arguments will run 'make build'.
@@ -7,18 +8,18 @@ COMMANDS := build watch rebuild clean
# ---
$(COMMANDS): site
@echo "Running command: ./site $@"
-@./site $@
$(COMMANDS): $(BIN)
@echo "Running command: ./$(BIN) $@"
-@./$(BIN) $@
# --- Rules ---
# using relative symlinks should be fine since everything only works at ./
site: src/site.hs src/ChaoDoc.hs
cabal build
ln -sf "$(shell cabal list-bin exe:site)" site
$(BIN): src/site.hs src/ChaoDoc.hs
cabal build exe:hakysidian
ln -sf "$(shell cabal list-bin exe:hakysidian)" $(BIN)
# move from katex to mathjax
# katex_cli:

134
readme.md
View File

@@ -1,6 +1,130 @@
things don't work:
1. equation labels & paragraph labels
2. pandoc does not support mathtools: <https://github.com/jgm/texmath/issues/249>
3. cross document refs
4.
# Drawbacks
- currently all shared files (css, templates, csl files...) are stored in `~/.cabal/store/`. there will be a copy for every compile
- web preview needs a port. if you don't set port manually, you cannot preview two projects at the same time.
--------
# hakysidian
`hakysidian` is a static site generator for note projects.
It is built on Hakyll, but packaged as a reusable CLI so you can run the same site generator across multiple note repositories without copying shared assets around. The executable bundles its shared `css/`, `fonts/`, `templates/`, `favicon.ico`, and `bib_style.csl` files with Cabal, then reads project-specific content from the current working directory.
## What It Expects
Run `hakysidian` inside a project directory with this layout:
```text
your-project/
├── notes/
│ ├── first-note.md
│ └── another-note.md
├── reference.bib
├── math-macros.md
└── images/ # optional
```
Required inputs:
- `notes/`: markdown notes to compile.
- `reference.bib`: bibliography used by Pandoc citeproc.
- `math-macros.md`: math macro definitions prepended before note parsing.
Optional inputs:
- `images/`: copied into the generated site as-is.
Shared assets are not required in each project. They come from the installed `hakysidian` package.
## Output
By default, `hakysidian` writes:
- `_site/`: generated site output.
- `_cache/`: Hakyll cache and temporary files.
Note pages use clean URLs. For example:
```text
notes/graph.md -> _site/notes/graph/index.html
```
## Install
From this repository:
```bash
cabal build exe:hakysidian
cabal install exe:hakysidian
```
## Commands
The default CLI mirrors the common Hakyll workflow:
```bash
hakysidian build
hakysidian clean
hakysidian rebuild
hakysidian watch
```
`watch` also supports:
```bash
hakysidian watch --host 127.0.0.1 --port 8000
hakysidian watch --no-server
```
The dashboard is now an explicit TUI mode:
```bash
hakysidian -tui
hakysidian -tui --host 127.0.0.1 --port 8000
hakysidian -tui --no-server
```
What each command does:
- `build`: incremental site build.
- `clean`: removes generated output and cache.
- `rebuild`: clears output/cache and builds from scratch.
- `watch`: runs Hakyll's normal watch workflow, prints build logs directly to the terminal, and rebuilds automatically on change.
- `-tui`: starts the interactive dashboard with explicit controls for watching and cleaning.
## Watch And TUI
Both `watch` and `-tui` work against the same project inputs:
- `notes/**`
- `reference.bib`
- `math-macros.md`
- `images/**`
Normal `watch` behaves like a standard Hakyll watch command: it stays in the terminal, rebuilds when inputs change, and can start a preview server unless `--no-server` is passed.
`-tui` uses an alternate-screen dashboard that:
- uses the terminals current size to keep the dashboard within the visible screen,
- keeps recent build output in a bounded activity pane,
- can start a local preview server unless `--no-server` is passed,
- supports `w` to start watching, `s` to stop watching, `c` to clean, and `q` to quit.
The TUI requires an interactive terminal.
## Notes Format
This generator is opinionated toward the current note pipeline in this repository:
- Markdown is parsed with Pandoc and custom theorem/callout handling.
- math is rendered with MathML. looks good in firefox
- sidenotes are supported
- spacing between CJK chars and ascii is automatically handled by a filter.
- Citations are processed through `reference.bib` and the bundled `bib_style.csl`.
- `math-macros.md` is injected before parsing so note content and theorem titles can use the same macros.
- Notes are rendered with the bundled templates and stylesheet set.

View File

@@ -124,9 +124,9 @@ preprocessTheorems (Div attr xs)
attr' = addAttr attr "type" theoremType
preprocessTheorems x = return x
theoremFilter :: Pandoc -> Pandoc
theoremFilter doc =
walk makeTheorem $
theoremFilter :: Text -> Pandoc -> Pandoc
theoremFilter mathMacros doc =
walk (makeTheorem mathMacros) $
autorefFilter $
evalState (walkM preprocessTheorems normalizedDoc) 1
where
@@ -171,24 +171,13 @@ autorefFilter x = walk (autoref links) x
where
links = query theoremIndex x
-- processCitations works on AST. If you want to use citations in theorem name,
-- then you need to convert citations there to AST as well and then use processCitations\
-- Thus one need to apply the theorem filter first.
-- autoref still does not work.
mathMacros :: Text
mathMacros = unsafePerformIO (pack <$> readFile "math-macros.md")
{-# NOINLINE mathMacros #-}
prependMacros :: Text -> Text -> Text
prependMacros macros body = macros <> "\n\n" <> body
prependMathMacros :: Text -> Text
prependMathMacros = prependMacros mathMacros
thmNamePandoc :: Text -> Pandoc
thmNamePandoc x =
thmNamePandoc :: Text -> Text -> Pandoc
thmNamePandoc mathMacros x =
fromRight (Pandoc nullMeta []) . runPure $
readMarkdown chaoDocRead (prependMathMacros x)
readMarkdown chaoDocRead (prependMacros mathMacros x)
obsidianTheoremFilter :: Pandoc -> Pandoc
obsidianTheoremFilter = attachStandaloneLabels . walk rewriteObsidianBlockQuote
@@ -390,8 +379,8 @@ unsnoc (x : xs) = do
(prefix, lastElem) <- unsnoc xs
return (x : prefix, lastElem)
makeTheorem :: Block -> Block
makeTheorem (Div attr xs)
makeTheorem :: Text -> Block -> Block
makeTheorem mathMacros (Div attr xs)
| isNothing t = Div attr xs
| otherwise = Div (addClass attr "theorem-environment") (Plain [header] : xs)
where
@@ -408,26 +397,23 @@ makeTheorem (Div attr xs)
nametext =
if isNothing name
then Str ""
else Span (addClass nullAttr "name") (pandocToInline $ thmNamePandoc $ fromJust name)
makeTheorem x = x
else Span (addClass nullAttr "name") (pandocToInline $ thmNamePandoc mathMacros $ fromJust name)
makeTheorem _ x = x
-- bib from https://github.com/chaoxu/chaoxu.github.io/tree/develop
cslFile :: String
cslFile = "bib_style.csl"
bibFile :: String
bibFile :: T.Text
bibFile = "reference.bib"
chaoDocPandocCompiler :: Compiler (Item Pandoc)
chaoDocPandocCompiler = do
chaoDocPandocCompiler :: FilePath -> Compiler (Item Pandoc)
chaoDocPandocCompiler cslPath = do
macros <- T.pack <$> loadBody "math-macros.md"
void (loadBody "reference.bib" :: Compiler String)
body <- getResourceBody
let bodyWithMacros =
fmap (T.unpack . prependMacros macros . T.pack) body
myReadPandocBiblio chaoDocRead (T.pack cslFile) (T.pack bibFile) myFilter bodyWithMacros
myReadPandocBiblio chaoDocRead (T.pack cslPath) bibFile (myFilter macros) bodyWithMacros
chaoDocCompiler :: Compiler (Item String)
chaoDocCompiler = chaoDocPandocCompiler <&> writePandocWith chaoDocWrite
chaoDocCompiler :: FilePath -> Compiler (Item String)
chaoDocCompiler cslPath = chaoDocPandocCompiler cslPath <&> writePandocWith chaoDocWrite
addMeta :: T.Text -> MetaValue -> Pandoc -> Pandoc
addMeta name value (Pandoc meta a) =
@@ -465,8 +451,9 @@ myReadPandocBiblio ropt csl biblio pdfilter item = do
-- let a x = itemSetBody (pandoc' x)
return $ fmap (const pandoc') item
myFilter :: Pandoc -> Pandoc
myFilter = usingSideNotesHTML chaoDocWrite . theoremFilter . panguFilter . displayMathFilter
myFilter :: Text -> Pandoc -> Pandoc
myFilter mathMacros =
usingSideNotesHTML chaoDocWrite . theoremFilter mathMacros . panguFilter . displayMathFilter
-- pangu filter
lastChar :: Inline -> Maybe Char

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="googlebot" content="noindex" />
<title>$title$</title>
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/css/fonts.css" />
<link rel="stylesheet" href="/css/default.css" />
<link rel="stylesheet" href="/css/pygentize.css" />

View File

@@ -7,6 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="googlebot" content="noindex">
<title></title>
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/css/fonts.css" />
<link rel="stylesheet" href="/css/default.css" />
<link rel="stylesheet" href="/css/pygentize.css" />

View File

@@ -11,6 +11,7 @@ $partial("templates/head.html")$
that are big enough -->
<div id="contents-big">
<p class="mini-header">Notes <a id="up-arrow" href="/"></a></p>
$partial("templates/notes-list.html")$
<p class="mini-header">Contents <a id="up-arrow" href="#"></a></p>
$toc$
</div>

View File

@@ -0,0 +1,7 @@
<ul class="notes-list">
$for(notes)$
<li>
<a href="$url$">$title$</a>
</li>
$endfor$
</ul>

View File

@@ -1,8 +1,2 @@
<h1 class="pagetitle">$title$</h1>
<ul>
$for(posts)$
<li>
<a href="$url$">$title$</a>
</li>
$endfor$
</ul>
$partial("templates/notes-list.html")$