@bytesbybrandon/living-docs 0.2.0 → 0.2.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/README.md CHANGED
@@ -96,6 +96,7 @@ Detailed references:
96
96
  * [Guarantees](docs/guide/guarantees.md)
97
97
  * [Architecture](ARCHITECTURE.md)
98
98
  * [Decisions](docs/decisions/0001-one-model-three-themes.md)
99
+ * [Website](https://bytesbybrandon.github.io/living-docs/), built from this repository with `npm run site`
99
100
 
100
101
  ---
101
102
 
@@ -122,7 +123,8 @@ living-docs/
122
123
  render/ block renderers, SVG generators, style resolution, themes
123
124
  server/ local preview server
124
125
  samples/ sample models and one Markdown source
125
- scripts/ sample build script
126
+ scripts/ sample and website build scripts
127
+ site/ landing page template and stylesheet for the website
126
128
  tests/ Node test runner suites
127
129
  docs/
128
130
  decisions/ architecture decision records
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytesbybrandon/living-docs",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Turn a source document into a designed, self-contained living doc: academic paper, dark reading notes, or telemetry console. Ships an AXI CLI and an MCP server.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -47,8 +47,9 @@
47
47
  "access": "public"
48
48
  },
49
49
  "scripts": {
50
- "test": "node --test tests/cli.test.js tests/ingest.test.js tests/mcp.test.js tests/motion.test.js tests/render.test.js tests/style.test.js tests/validate.test.js",
51
- "samples": "node scripts/build-samples.js"
50
+ "test": "node --test tests/cli.test.js tests/ingest.test.js tests/mcp.test.js tests/motion.test.js tests/render.test.js tests/site.test.js tests/style.test.js tests/validate.test.js",
51
+ "samples": "node scripts/build-samples.js",
52
+ "site": "node scripts/build-site.js"
52
53
  },
53
54
  "dependencies": {
54
55
  "@modelcontextprotocol/sdk": "^1.32.2",
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { parseMarkdown, slug, wordCount, plainText } from '../model/markdown.js';
3
+ import { parseMarkdown, slug, wordCount } from '../model/markdown.js';
4
4
  import { htmlToMarkdown, htmlTitle } from './html.js';
5
5
  import { textToMarkdown } from './text.js';
6
6
 
@@ -79,8 +79,8 @@ function normalizeBlocks(blocks) {
79
79
  if (block.type === 'table') {
80
80
  return {
81
81
  type: 'table',
82
- columns: block.columns.map(c => plainText(c)),
83
- rows: block.rows.map(row => row.map(cell => plainText(cell)))
82
+ columns: block.columns.map(c => c.trim()),
83
+ rows: block.rows.map(row => row.map(cell => cell.trim()))
84
84
  };
85
85
  }
86
86
  if (block.type === 'list') {
@@ -200,16 +200,24 @@ export function blockToModelBlock(block) {
200
200
  export function draftModel(ingested, { theme = 'paper-dark', includePreamble = true, maxBlocks = 400 } = {}) {
201
201
  const { meta, sections, preamble } = ingested;
202
202
  const blocks = [];
203
+ let abstractPending = Boolean(meta.abstract);
204
+ const takeBlock = block => {
205
+ if (abstractPending && block.type === 'paragraph' && block.text === meta.abstract) {
206
+ abstractPending = false;
207
+ return null;
208
+ }
209
+ return blockToModelBlock(block);
210
+ };
203
211
  if (includePreamble && preamble.length) {
204
212
  for (const block of preamble) {
205
- const mapped = blockToModelBlock(block);
213
+ const mapped = takeBlock(block);
206
214
  if (mapped) blocks.push(mapped);
207
215
  }
208
216
  }
209
217
  for (const section of sections) {
210
218
  blocks.push({ type: 'heading', title: section.title, level: 2 });
211
219
  for (const block of section.blocks) {
212
- const mapped = blockToModelBlock(block);
220
+ const mapped = takeBlock(block);
213
221
  if (mapped) blocks.push(mapped);
214
222
  if (blocks.length >= maxBlocks) break;
215
223
  }
@@ -56,7 +56,8 @@ const olRe = /^\s*\d+[.)]\s+(.*)$/;
56
56
  const hrRe = /^\s*([-*_])(?:\s*\1){2,}\s*$/;
57
57
 
58
58
  function splitRow(line) {
59
- return line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map(cell => cell.trim());
59
+ const body = line.trim().replace(/^\|/, '').replace(/(?<!\\)\|$/, '');
60
+ return body.split(/(?<!\\)\|/).map(cell => cell.trim().replace(/\\\|/g, '|'));
60
61
  }
61
62
 
62
63
  /**
@@ -300,7 +300,8 @@ export const STYLE = {
300
300
  seed: str('hex color that derives the accent family, e.g. #2f6f8f', { optional: true }),
301
301
  type: oneOf(['classic', 'editorial', 'technical', 'mono', 'hybrid', 'terminal'], 'curated type pairing', { optional: true }),
302
302
  density: oneOf(['compact', 'normal', 'roomy'], 'vertical rhythm', { optional: true }),
303
- figure: oneOf(['outline', 'solid', 'minimal'], 'diagram and chart treatment', { optional: true })
303
+ figure: oneOf(['outline', 'solid', 'minimal'], 'diagram and chart treatment', { optional: true }),
304
+ layout: oneOf(['columns', 'single'], 'paper themes only: two columns (default) or one reading column for code and wide tables', { optional: true })
304
305
  };
305
306
 
306
307
  export const META = {
@@ -173,13 +173,15 @@ export function resolveStyle(theme, basePalette, style = {}, accentOverride) {
173
173
 
174
174
  const density = DENSITIES.has(style.density) ? style.density : 'normal';
175
175
  const figure = FIGURES.has(style.figure) ? style.figure : 'outline';
176
+ const layout = !isConsole && style.layout === 'single' ? 'single' : 'columns';
176
177
 
177
178
  return {
178
179
  palette,
179
180
  type,
180
181
  density,
181
182
  figure,
182
- className: `density-${density} type-${type} fig-${figure}`
183
+ layout,
184
+ className: `density-${density} type-${type} fig-${figure} layout-${layout}`
183
185
  };
184
186
  }
185
187
 
@@ -287,5 +289,8 @@ export function styleCss(theme, palette, resolved) {
287
289
  const overrides = theme === 'hud' ? CONSOLE_OVERRIDES : PAPER_OVERRIDES;
288
290
  const density = DENSITY[theme]?.[resolved.density] || '';
289
291
  const type = TYPE[theme]?.[resolved.type] || '';
290
- return `${palettes(palette)}${overrides}${density}${type}`;
292
+ const layout = resolved.layout === 'single'
293
+ ? '.columns{column-count:1;max-width:880px;margin-left:auto;margin-right:auto}.span-page{column-span:none}.footnotes{column-count:1}'
294
+ : '';
295
+ return `${palettes(palette)}${overrides}${density}${type}${layout}`;
291
296
  }
@@ -43,7 +43,7 @@ body{margin:0;background:#020409;color:var(--ink);font-family:var(--mono);font-s
43
43
  .scene-head{padding:14px 16px 0}
44
44
  .scene-head .brand-row{padding-bottom:6px}
45
45
  .scene-top{display:flex;justify-content:space-between;padding:12px 16px 6px;font-size:9px;letter-spacing:.12em;color:#5f8090;text-transform:uppercase}
46
- .scene-stage{position:relative;aspect-ratio:16/8.6;min-height:300px;margin-top:2px;background:radial-gradient(ellipse at 55% 45%,rgba(30,90,120,.35),rgba(4,8,14,0) 66%),#03060b}
46
+ .scene-stage{position:relative;width:100%;min-width:0;aspect-ratio:16/8.6;min-height:300px;margin-top:2px;background:radial-gradient(ellipse at 55% 45%,rgba(30,90,120,.35),rgba(4,8,14,0) 66%),#03060b}
47
47
  .scene-canvas{position:absolute;inset:0;width:100%;height:100%}
48
48
  .scene-label{position:absolute;transform:translate(-50%,-50%);font-size:9.5px;letter-spacing:.04em;color:#7fa6b8;white-space:nowrap;text-shadow:0 0 6px #03060b;pointer-events:none}
49
49
  .scene-crosshair{position:absolute;transform:translate(-50%,-50%);width:190px;height:190px;border:1px solid rgba(94,200,229,.22);border-radius:50%;pointer-events:none}
@@ -217,7 +217,7 @@ tbody td{padding:5px 6px;color:#b7d4e1;border-bottom:1px solid #0a1822}
217
217
  .hud-head{flex-direction:column;gap:8px}
218
218
  .live{font-size:8px}
219
219
  .scene-stage{min-height:240px}
220
- .scene-card{transform:translate(12px,18px);min-width:200px;max-width:230px}
220
+ .scene-card{left:12px!important;top:auto!important;bottom:10px;transform:none;min-width:0;max-width:none;width:calc(100% - 24px)}
221
221
  .scene-label{font-size:8px}
222
222
  .sc-tail{margin-left:0}
223
223
  .footnotes{column-count:1}