stack-site-builder 1.24.0 → 1.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/README.md +1 -1
- package/index.d.ts +8 -0
- package/index.mjs +6 -2
- package/markdown.mjs +61 -6
- package/package.json +4 -1
- package/src/components/DeckView.astro +6 -0
- package/src/layouts/BaseLayout.astro +11 -0
- package/src/styles/global.css +42 -0
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,72 @@ content schema, while a consuming site supplies only content, taxonomy data and
|
|
|
11
11
|
config. Sites track the theme with `pnpm up stack-site-builder`, so each release
|
|
12
12
|
here is a plain version bump they pull in.
|
|
13
13
|
|
|
14
|
+
## [1.25.1] - 2026-08-31
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Math in a slide deck rendered twice** — a deck is the one route that does
|
|
19
|
+
not go through `BaseLayout`; `DeckView.astro` renders the whole `<html>`
|
|
20
|
+
itself, so it never picked up the layout's `katex.min.css` import. Without
|
|
21
|
+
that stylesheet the MathML copy rehype-katex emits for screen readers loses
|
|
22
|
+
the rule that clips it out of view, and every formula showed up twice: once
|
|
23
|
+
as unstyled MathML source and once as the real KaTeX drawing beside it. The
|
|
24
|
+
deck now imports the stylesheet too, and `sample-layouts` gained a Math
|
|
25
|
+
section so the deck path is covered the way the article path already was.
|
|
26
|
+
- **Inline math in a slide was oversized** — the `font-size: 1.1em` that trims
|
|
27
|
+
KaTeX's 1.21em default was scoped to `.prose`, which a slide is not. It now
|
|
28
|
+
applies under `.aas-slide-inner` as well, so a formula mid-sentence matches
|
|
29
|
+
the text around it on a slide the same as in an article.
|
|
30
|
+
|
|
31
|
+
## [1.25.0] - 2026-08-31
|
|
32
|
+
|
|
33
|
+
Papers and courses routinely carry formulas, and until now a site's only
|
|
34
|
+
option was to drop in a screenshot or fake it with Unicode. This release adds
|
|
35
|
+
LaTeX math to the markdown pipeline, rendered by KaTeX at build time, with the
|
|
36
|
+
theme's existing dollar-sign prices left alone by default.
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- **LaTeX math in the markdown pipeline** — `$$…$$` renders as math: inline
|
|
41
|
+
when it sits mid-line, a centered display block when the fence stands alone
|
|
42
|
+
on its own lines. A longer run of dollars (`$$$…$$$`) opens an equivalent
|
|
43
|
+
fence for a formula that itself contains `$$`. Single `$…$` is deliberately
|
|
44
|
+
left as plain text — these are catalog sites full of prices, and "plans
|
|
45
|
+
start at $8 and go to $20" would otherwise silently become an equation — so
|
|
46
|
+
a site opts in explicitly with `math: { singleDollar: true }`, now
|
|
47
|
+
available on both `aasMarkdown()` and, newly, `aasTheme()`, which previously
|
|
48
|
+
had no way to forward the option at all. KaTeX runs at build time via
|
|
49
|
+
rehype-katex, so pages ship plain HTML + MathML with no math runtime on the
|
|
50
|
+
client, and `\text{}` renders unicode text (`\text{처리량}`) without
|
|
51
|
+
KaTeX's usual strict-mode warning, since that's the normal case in
|
|
52
|
+
bilingual content rather than a mistake.
|
|
53
|
+
- **A formula KaTeX can't parse fails the build** — rehype-katex's own answer
|
|
54
|
+
to unparsable LaTeX is a vfile warning nobody reads plus red error text
|
|
55
|
+
baked into the published page, the same silent degradation this pipeline
|
|
56
|
+
already refuses for an unknown wikilink. `rehypeMathErrors` turns each such
|
|
57
|
+
warning into a thrown build error naming the file and the `line:column`
|
|
58
|
+
alongside KaTeX's own diagnostic.
|
|
59
|
+
- **KaTeX ships themed and scrollable** — its stylesheet and fonts load
|
|
60
|
+
site-wide (~7 kB gzipped added to the shared bundle; the 20 font faces are
|
|
61
|
+
only fetched by a page that actually renders math) and draw in
|
|
62
|
+
`currentColor`, so math follows the light/dark theme with no palette work
|
|
63
|
+
needed. A display block wider than its column scrolls horizontally instead
|
|
64
|
+
of squeezing, matching how wide tables already behave, and inline math is
|
|
65
|
+
sized to 1.1em to match body text. KaTeX's Hangul/kana/Han fallback
|
|
66
|
+
classes, which ship with no font by design, now point at the site's own
|
|
67
|
+
sans, so `\text{처리량}` renders in the site font instead of falling
|
|
68
|
+
through to a mismatched system pick.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- **The playground's Attention paper demonstrates the math syntax**, in both
|
|
73
|
+
locales — inline math, a `$$` display fence, a `$$$` fence, prices left
|
|
74
|
+
untouched, and `\text{처리량}` rendering warning-free.
|
|
75
|
+
`docs/content-authoring.md` gains a matching Math section covering both
|
|
76
|
+
fence forms, the single-dollar opt-in, the build-fails-on-error behavior,
|
|
77
|
+
and the one MDX escaping rule that does not apply inside math (braces need
|
|
78
|
+
no `\{`).
|
|
79
|
+
|
|
14
80
|
## [1.24.0] - 2026-08-25
|
|
15
81
|
|
|
16
82
|
Adding a locale was already meant to be site config only — the README said so —
|
|
@@ -520,6 +586,7 @@ catalog sites from a thin content-only repository.
|
|
|
520
586
|
- **Standalone development setup** — a devcontainer and a minimal `playground/`
|
|
521
587
|
consuming site for developing and previewing the theme on its own.
|
|
522
588
|
|
|
589
|
+
[1.25.0]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.24.0...v1.25.0
|
|
523
590
|
[1.24.0]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.23.2...v1.24.0
|
|
524
591
|
[1.23.2]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.23.1...v1.23.2
|
|
525
592
|
[1.23.1]: https://github.com/CodeComposeStudio/stack-site-builder/compare/v1.23.0...v1.23.1
|
package/README.md
CHANGED
|
@@ -218,7 +218,7 @@ import Bookmark from 'stack-site-builder/components/Bookmark.astro';
|
|
|
218
218
|
```
|
|
219
219
|
|
|
220
220
|
Markdown/MDX authoring gotchas (nested-list indentation, blockquotes, hard
|
|
221
|
-
breaks, wikilinks): `docs/content-authoring.md`.
|
|
221
|
+
breaks, wikilinks, math): `docs/content-authoring.md`.
|
|
222
222
|
|
|
223
223
|
## RSS
|
|
224
224
|
|
package/index.d.ts
CHANGED
|
@@ -25,6 +25,14 @@ export interface AasThemeOptions {
|
|
|
25
25
|
* `sections` so its header-nav item is hidden too.
|
|
26
26
|
*/
|
|
27
27
|
sections?: Partial<Record<SectionKey, boolean>>;
|
|
28
|
+
/**
|
|
29
|
+
* Math syntax. `$$…$$` (inline, or a fence on its own lines for a display
|
|
30
|
+
* block) is always on. `singleDollar` additionally reads `$…$` as inline
|
|
31
|
+
* math; it is off by default because these are catalog sites whose prose is
|
|
32
|
+
* full of prices, and "plans start at $8 and go to $20" would otherwise
|
|
33
|
+
* silently render as an equation.
|
|
34
|
+
*/
|
|
35
|
+
math?: { singleDollar?: boolean };
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
/**
|
package/index.mjs
CHANGED
|
@@ -100,9 +100,13 @@ const OPT_IN_SECTIONS = new Set(['courses', 'products', 'papers']);
|
|
|
100
100
|
* toggles (`{ slides: false }`); a disabled section's routes are not injected.
|
|
101
101
|
* `courses` is opt-IN (`{ courses: true }`) — it needs site-side course data.
|
|
102
102
|
* Keep it in sync with `src/data/site.ts` `sections` (which hides the nav item).
|
|
103
|
+
* @param {{ singleDollar?: boolean }} [opts.math] — math syntax. `$$…$$` is
|
|
104
|
+
* always on; `{ singleDollar: true }` additionally reads `$…$` as inline math.
|
|
105
|
+
* Off by default because a catalog site's prose is full of prices — see
|
|
106
|
+
* MATH_SYNTAX in markdown.mjs.
|
|
103
107
|
* @returns {import('astro').AstroIntegration[]}
|
|
104
108
|
*/
|
|
105
|
-
export default function aasTheme({ glossary, sections = {} }) {
|
|
109
|
+
export default function aasTheme({ glossary, sections = {}, math = {} }) {
|
|
106
110
|
/** @type {import('astro').AstroIntegration} */
|
|
107
111
|
const core = {
|
|
108
112
|
name: 'stack-site-builder',
|
|
@@ -138,7 +142,7 @@ export default function aasTheme({ glossary, sections = {} }) {
|
|
|
138
142
|
});
|
|
139
143
|
|
|
140
144
|
updateConfig({
|
|
141
|
-
markdown: aasMarkdown({ glossary, locales, defaultLocale }),
|
|
145
|
+
markdown: aasMarkdown({ glossary, locales, defaultLocale, math }),
|
|
142
146
|
|
|
143
147
|
// Bind the dev server to 0.0.0.0 so it's reachable from a browser on
|
|
144
148
|
// the host (outside the Docker container).
|
package/markdown.mjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
// The shared Markdown/MDX pipeline for awesome-*-stack sites: heading ids +
|
|
3
|
-
// copy-link anchors, mermaid fences, slide directives, and
|
|
4
|
-
// resolution against the site's glossary. Sites get the whole
|
|
5
|
-
// the theme integration (index.mjs); `aasMarkdown({ glossary })`
|
|
6
|
-
// exported for direct use.
|
|
3
|
+
// copy-link anchors, mermaid fences, slide directives, LaTeX math, and
|
|
4
|
+
// [[wikilink]] resolution against the site's glossary. Sites get the whole
|
|
5
|
+
// pipeline from the theme integration (index.mjs); `aasMarkdown({ glossary })`
|
|
6
|
+
// is also exported for direct use.
|
|
7
7
|
import rehypeExternalLinks from 'rehype-external-links';
|
|
8
|
+
import rehypeKatex from 'rehype-katex';
|
|
8
9
|
import rehypeSlug from 'rehype-slug';
|
|
9
10
|
import remarkDirective from 'remark-directive';
|
|
11
|
+
import remarkMath from 'remark-math';
|
|
10
12
|
|
|
11
13
|
// Prepend a "#" copy-link anchor to h2/h3/h4 headings (a global click handler
|
|
12
14
|
// in BaseLayout copies the section URL). The "#" count per level is drawn via
|
|
@@ -412,25 +414,78 @@ function remarkGlossary({ glossary, locales = ['en', 'ko'], defaultLocale = 'en'
|
|
|
412
414
|
};
|
|
413
415
|
}
|
|
414
416
|
|
|
417
|
+
// KaTeX cannot parse everything, and rehype-katex's answer to a formula it
|
|
418
|
+
// chokes on is a warning nobody reads plus red text in the published page. That
|
|
419
|
+
// is the silent-degradation failure mode this pipeline already refuses for
|
|
420
|
+
// wikilinks, so promote it the same way: fail the build. Runs immediately after
|
|
421
|
+
// rehypeKatex, which records each failure on the vfile.
|
|
422
|
+
function rehypeMathErrors() {
|
|
423
|
+
return (/** @type {any} */ _tree, /** @type {any} */ file) => {
|
|
424
|
+
/** @type {any[]} */
|
|
425
|
+
const messages = file.messages || [];
|
|
426
|
+
const bad = messages.filter((m) => m.source === 'rehype-katex');
|
|
427
|
+
if (!bad.length) return;
|
|
428
|
+
// Drop them from the vfile so the thrown error is the only report.
|
|
429
|
+
file.messages = messages.filter((m) => m.source !== 'rehype-katex');
|
|
430
|
+
const where = file.path || (file.history && file.history[0]) || 'unknown file';
|
|
431
|
+
const detail = bad
|
|
432
|
+
.map((m) => {
|
|
433
|
+
const at = m.place && m.place.start ? `${m.place.start.line}:${m.place.start.column} ` : '';
|
|
434
|
+
return ` - ${at}${(m.cause && m.cause.message) || m.reason}`;
|
|
435
|
+
})
|
|
436
|
+
.join('\n');
|
|
437
|
+
throw new Error(`[math] KaTeX could not parse ${bad.length} formula(s) in ${where}:\n${detail}`);
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Math is written with DOUBLE dollars — `$$E = mc^2$$` inline, or a `$$` fence
|
|
442
|
+
// on its own lines for a display block (three or more dollars work the same, so
|
|
443
|
+
// `$$$ … $$$` is a valid fence too, as long as the closing run is at least as
|
|
444
|
+
// long as the opening one). Single `$…$` is deliberately OFF: these are catalog
|
|
445
|
+
// sites full of prices, and "plans start at $8 and go to $20" would otherwise
|
|
446
|
+
// silently turn into math. A site that wants single-dollar math can opt in with
|
|
447
|
+
// `aasMarkdown({ math: { singleDollar: true } })`.
|
|
448
|
+
const MATH_SYNTAX = { singleDollarTextMath: false };
|
|
449
|
+
|
|
450
|
+
// KaTeX renders at BUILD time (rehype-katex), so pages ship plain HTML + MathML
|
|
451
|
+
// with no client-side math runtime; the stylesheet is pulled in by BaseLayout.
|
|
452
|
+
const KATEX_OPTIONS = {
|
|
453
|
+
// KaTeX's `strict` mode warns about non-Latin characters inside `\text{…}`,
|
|
454
|
+
// which for bilingual ko/en content is the normal case, not a mistake
|
|
455
|
+
// (`\text{처리량}`). Silence just that rule and keep the rest of the warnings.
|
|
456
|
+
/** @param {string} code */
|
|
457
|
+
strict: (/** @type {string} */ code) => (code === 'unicodeTextInMathMode' ? 'ignore' : 'warn'),
|
|
458
|
+
};
|
|
459
|
+
|
|
415
460
|
/**
|
|
416
461
|
* The full markdown config for `defineConfig({ markdown })`.
|
|
417
|
-
* @param {{ glossary: Record<string, any>, locales?: string[], defaultLocale?: string
|
|
462
|
+
* @param {{ glossary: Record<string, any>, locales?: string[], defaultLocale?: string,
|
|
463
|
+
* math?: { singleDollar?: boolean } }} opts
|
|
418
464
|
* `glossary` — the site's wikilink targets (pass `{}` for none). `locales` /
|
|
419
465
|
* `defaultLocale` come from the site's astro.config `i18n` so wikilink locale
|
|
420
466
|
* detection matches whatever locales the site ships (defaults to en/ko).
|
|
467
|
+
* `math.singleDollar` opts into `$…$` inline math (see MATH_SYNTAX).
|
|
421
468
|
*/
|
|
422
|
-
export function aasMarkdown({ glossary, locales = ['en', 'ko'], defaultLocale = 'en' }) {
|
|
469
|
+
export function aasMarkdown({ glossary, locales = ['en', 'ko'], defaultLocale = 'en', math = {} }) {
|
|
423
470
|
return {
|
|
424
471
|
remarkPlugins: [
|
|
425
472
|
remarkHeadingIds,
|
|
473
|
+
// Before remarkMermaid/remarkDirective only for readability — all three
|
|
474
|
+
// are micromark syntax extensions, so the parser applies them together.
|
|
475
|
+
[remarkMath, { ...MATH_SYNTAX, singleDollarTextMath: math.singleDollar === true }],
|
|
426
476
|
remarkMermaid,
|
|
427
477
|
remarkDirective,
|
|
428
478
|
remarkSlideDirectives,
|
|
429
479
|
[remarkGlossary, { glossary, locales, defaultLocale }],
|
|
430
480
|
],
|
|
431
481
|
rehypePlugins: [
|
|
482
|
+
// rehypeSlug runs first so a heading with math slugs from the SOURCE
|
|
483
|
+
// (`## $$x^2$$` → "x2"); after KaTeX the heading's text content is the
|
|
484
|
+
// whole rendered glyph soup and the id would be unusable.
|
|
432
485
|
rehypeSlug,
|
|
433
486
|
rehypeHeadingAnchors,
|
|
487
|
+
[rehypeKatex, KATEX_OPTIONS],
|
|
488
|
+
rehypeMathErrors,
|
|
434
489
|
rehypeTableScroll,
|
|
435
490
|
[rehypeExternalLinks, { target: '_blank', rel: ['noopener', 'noreferrer'] }],
|
|
436
491
|
],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stack-site-builder",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.25.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "The engine behind the awesome-*-stack catalog sites: an Astro theme with the catalog/concepts/articles/slides/samples routes, components, styles and markdown pipeline. Sites provide content, taxonomy data and config.",
|
|
7
7
|
"repository": {
|
|
@@ -38,11 +38,14 @@
|
|
|
38
38
|
"@astrojs/rss": "^4.0.0",
|
|
39
39
|
"@astrojs/sitemap": "^3.3.0",
|
|
40
40
|
"@tailwindcss/vite": "^4.1.0",
|
|
41
|
+
"katex": "^0.16.47",
|
|
41
42
|
"markdown-it": "^14.2.0",
|
|
42
43
|
"mermaid": "^11.15.0",
|
|
43
44
|
"rehype-external-links": "^3.0.0",
|
|
45
|
+
"rehype-katex": "^7.0.1",
|
|
44
46
|
"rehype-slug": "^6.0.0",
|
|
45
47
|
"remark-directive": "^4.0.0",
|
|
48
|
+
"remark-math": "^6.0.0",
|
|
46
49
|
"shiki": "^4.2.0",
|
|
47
50
|
"tailwindcss": "^4.1.0"
|
|
48
51
|
},
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
import '../styles/global.css';
|
|
3
|
+
// A deck is its own document root (this component renders the whole <html>), so
|
|
4
|
+
// it does not inherit BaseLayout's stylesheet imports and needs KaTeX's own
|
|
5
|
+
// stylesheet here as well. Without it the MathML copy that rehype-katex emits
|
|
6
|
+
// for screen readers loses the rule that hides it and renders as a second,
|
|
7
|
+
// unstyled formula beside the real one.
|
|
8
|
+
import 'katex/dist/katex.min.css';
|
|
3
9
|
import { getRelativeLocaleUrl } from 'astro:i18n';
|
|
4
10
|
import { render, type CollectionEntry } from 'astro:content';
|
|
5
11
|
import MermaidLoader from './MermaidLoader.astro';
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
import '../styles/global.css';
|
|
3
|
+
// KaTeX's stylesheet, loaded for every page under this layout rather than
|
|
4
|
+
// per-page. An Astro `import`
|
|
5
|
+
// is resolved statically, so a runtime `{hasMath && …}` guard (the trick that
|
|
6
|
+
// keeps the Mermaid loader off pages without diagrams) would not actually keep
|
|
7
|
+
// it out of the bundle. Site-wide is also the cheaper end of the trade: it
|
|
8
|
+
// merges into the one global stylesheet every page already downloads and
|
|
9
|
+
// caches, ~7 kB gzipped, and the font files are only fetched by a browser that
|
|
10
|
+
// has math to draw. DeckView.astro is the one route that bypasses this layout
|
|
11
|
+
// (a deck renders its own <html>), so it repeats the import.
|
|
12
|
+
// See markdown.mjs for the pipeline that produces the markup.
|
|
13
|
+
import 'katex/dist/katex.min.css';
|
|
3
14
|
import { site } from '@aas-data/site';
|
|
4
15
|
import { getRelativeLocaleUrl } from 'astro:i18n';
|
|
5
16
|
import { useTranslations, languages, siteName, defaultLang, type Lang } from '../i18n/ui';
|
package/src/styles/global.css
CHANGED
|
@@ -866,6 +866,48 @@ summary {
|
|
|
866
866
|
background: color-mix(in srgb, var(--aas-panel) 45%, transparent);
|
|
867
867
|
}
|
|
868
868
|
|
|
869
|
+
/* Math (`$$…$$`, rendered to HTML + MathML at build time by rehype-katex).
|
|
870
|
+
KaTeX draws everything in `currentColor`, so it picks up the theme — and the
|
|
871
|
+
dark/light swap — with no palette work here. What it does NOT do is stay
|
|
872
|
+
inside its column: a long derivation is one unbreakable line, so the same
|
|
873
|
+
argument as `.aas-table-scroll` applies and the display block takes the
|
|
874
|
+
scroll. The vertical padding is what makes that safe — `overflow-x: auto`
|
|
875
|
+
promotes `overflow-y` to `auto` unless it is pinned, and pinning it to
|
|
876
|
+
`hidden` would shave the top of a tall fraction or the tail of a descender. */
|
|
877
|
+
.katex-display {
|
|
878
|
+
max-width: 100%;
|
|
879
|
+
overflow-x: auto;
|
|
880
|
+
overflow-y: hidden;
|
|
881
|
+
overscroll-behavior-x: contain;
|
|
882
|
+
padding: 0.5em 0;
|
|
883
|
+
}
|
|
884
|
+
/* Inline math sits in running text, so it must not introduce its own scrollbar
|
|
885
|
+
or stretch the line box; KaTeX's 1.21em default is a touch large next to our
|
|
886
|
+
body size. A slide is not `.prose` — it carries its own type scale — but the
|
|
887
|
+
same mismatch shows up there, so both roots get the rule. */
|
|
888
|
+
.prose .katex,
|
|
889
|
+
.aas-slide-inner .katex {
|
|
890
|
+
font-size: 1.1em;
|
|
891
|
+
}
|
|
892
|
+
/* KaTeX's fonts carry no Hangul, kana or Han glyphs, so for `\text{처리량}` it
|
|
893
|
+
tags the run with `.hangul_fallback` (or `.cjk_fallback` / `.brahmic_fallback`)
|
|
894
|
+
and ships NO rule for them — the font is left to the site on purpose. Without
|
|
895
|
+
one the run falls through KaTeX_Main to whatever the browser picks next, which
|
|
896
|
+
renders Korean loose and letterform-mismatched beside the prose it sits in.
|
|
897
|
+
Hand it the site stack (Pretendard first) so math text matches body text. */
|
|
898
|
+
.katex .hangul_fallback,
|
|
899
|
+
.katex .cjk_fallback,
|
|
900
|
+
.katex .brahmic_fallback {
|
|
901
|
+
font-family: var(--aas-sans);
|
|
902
|
+
}
|
|
903
|
+
/* A formula KaTeX could not parse. A production build fails on one (see
|
|
904
|
+
`rehypeMathErrors` in markdown.mjs), so this only ever shows in a dev server,
|
|
905
|
+
where being loud is exactly the point. */
|
|
906
|
+
.katex-error {
|
|
907
|
+
color: var(--aas-role-guard);
|
|
908
|
+
border-bottom: 1px dotted currentColor;
|
|
909
|
+
}
|
|
910
|
+
|
|
869
911
|
/* ── Listing density: gallery (compact mini-cards) ───────────────────────────
|
|
870
912
|
Toggled by [data-view='gallery'] on <html> (persisted in localStorage). A
|
|
871
913
|
denser grid plus a stripped-down card — logo, name, stars, pricing, and a
|