@dogsbay/format-astro 0.2.0-beta.92 → 0.2.0-beta.98

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.
Files changed (44) hide show
  1. package/dist/base-path.d.ts +10 -0
  2. package/dist/base-path.d.ts.map +1 -1
  3. package/dist/base-path.js +11 -1
  4. package/dist/base-path.js.map +1 -1
  5. package/dist/blog.d.ts +134 -0
  6. package/dist/blog.d.ts.map +1 -0
  7. package/dist/blog.js +319 -0
  8. package/dist/blog.js.map +1 -0
  9. package/dist/diff-decoration.d.ts +79 -0
  10. package/dist/diff-decoration.d.ts.map +1 -0
  11. package/dist/diff-decoration.js +541 -0
  12. package/dist/diff-decoration.js.map +1 -0
  13. package/dist/granularity.d.ts +83 -0
  14. package/dist/granularity.d.ts.map +1 -0
  15. package/dist/granularity.js +247 -0
  16. package/dist/granularity.js.map +1 -0
  17. package/dist/index.d.ts +17 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +22 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/lead.d.ts.map +1 -1
  22. package/dist/lead.js +30 -6
  23. package/dist/lead.js.map +1 -1
  24. package/dist/llms-txt.d.ts +19 -0
  25. package/dist/llms-txt.d.ts.map +1 -1
  26. package/dist/llms-txt.js +40 -2
  27. package/dist/llms-txt.js.map +1 -1
  28. package/dist/project.d.ts +134 -2
  29. package/dist/project.d.ts.map +1 -1
  30. package/dist/project.js +1149 -137
  31. package/dist/project.js.map +1 -1
  32. package/dist/serialize.d.ts +1 -0
  33. package/dist/serialize.d.ts.map +1 -1
  34. package/dist/serialize.js +302 -138
  35. package/dist/serialize.js.map +1 -1
  36. package/dist/sitemap.d.ts +24 -0
  37. package/dist/sitemap.d.ts.map +1 -1
  38. package/dist/sitemap.js +32 -3
  39. package/dist/sitemap.js.map +1 -1
  40. package/dist/taxonomy.d.ts +24 -1
  41. package/dist/taxonomy.d.ts.map +1 -1
  42. package/dist/taxonomy.js +23 -2
  43. package/dist/taxonomy.js.map +1 -1
  44. package/package.json +8 -7
@@ -176,6 +176,16 @@ export declare function resolvePrefixes(siteUrl: string | undefined, basePath: s
176
176
  * - default basePath, empty slug → `"/docs"`
177
177
  * - empty basePath, slug "auth" → `"/auth"`
178
178
  * - empty basePath, empty slug → `"/"`
179
+ * - empty basePath, slug "index" → `"/"` (root home)
180
+ *
181
+ * A bare `index` slug means the directory root: `content/index.md` is
182
+ * deliberately loaded with slug `"index"` (so the root-home and `.md`-mirror
183
+ * special cases fire — see plans/dir-index-slug-nav-drop.md), but the page is
184
+ * served at the directory URL and its nav href drops `index` (via
185
+ * `fileToHref`'s `/index` strip). `currentPath` must match that nav href for
186
+ * `getPagination` to find the home page, so we drop a whole-slug `index` here
187
+ * too. Without this the home page's `currentPath` (`/base/index`) never matches
188
+ * its nav entry (`/base`), so prev/next silently falls back to the first item.
179
189
  */
180
190
  export declare function buildCurrentPath(basePath: string, section: string | undefined, slug: string): string;
181
191
  //# sourceMappingURL=base-path.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-path.d.ts","sourceRoot":"","sources":["../src/base-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAKnE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAE3D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,IAAI,EAAE,MAAM,GACX,MAAM,CAMR;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG;IACzD,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB,CAiBA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B;IACD,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CASA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,IAAI,EAAE,MAAM,GACX,MAAM,CAMR"}
1
+ {"version":3,"file":"base-path.d.ts","sourceRoot":"","sources":["../src/base-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAKnE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAE3D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,IAAI,EAAE,MAAM,GACX,MAAM,CAMR;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG;IACzD,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB,CAiBA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B;IACD,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CASA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,IAAI,EAAE,MAAM,GACX,MAAM,CAMR"}
package/dist/base-path.js CHANGED
@@ -225,6 +225,16 @@ export function resolvePrefixes(siteUrl, basePath) {
225
225
  * - default basePath, empty slug → `"/docs"`
226
226
  * - empty basePath, slug "auth" → `"/auth"`
227
227
  * - empty basePath, empty slug → `"/"`
228
+ * - empty basePath, slug "index" → `"/"` (root home)
229
+ *
230
+ * A bare `index` slug means the directory root: `content/index.md` is
231
+ * deliberately loaded with slug `"index"` (so the root-home and `.md`-mirror
232
+ * special cases fire — see plans/dir-index-slug-nav-drop.md), but the page is
233
+ * served at the directory URL and its nav href drops `index` (via
234
+ * `fileToHref`'s `/index` strip). `currentPath` must match that nav href for
235
+ * `getPagination` to find the home page, so we drop a whole-slug `index` here
236
+ * too. Without this the home page's `currentPath` (`/base/index`) never matches
237
+ * its nav entry (`/base`), so prev/next silently falls back to the first item.
228
238
  */
229
239
  export function buildCurrentPath(basePath, section, slug) {
230
240
  const parts = [];
@@ -232,7 +242,7 @@ export function buildCurrentPath(basePath, section, slug) {
232
242
  parts.push(seg);
233
243
  if (section)
234
244
  parts.push(section);
235
- if (slug)
245
+ if (slug && slug !== "index")
236
246
  parts.push(slug);
237
247
  return parts.length === 0 ? "/" : `/${parts.join("/")}`;
238
248
  }
@@ -1 +1 @@
1
- {"version":3,"file":"base-path.js","sourceRoot":"","sources":["../src/base-path.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-path.js","sourceRoot":"","sources":["../src/base-path.ts"],"names":[],"mappings":"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"}
package/dist/blog.d.ts ADDED
@@ -0,0 +1,134 @@
1
+ import type { ExportPage } from "@dogsbay/types";
2
+ /** Resolved `blog:` config (defaults filled). */
3
+ export interface BlogEmitConfig {
4
+ /**
5
+ * Where the reverse-chronological index lives, relative to the site.
6
+ * `"/"` (the default) means the site root IS the blog index — the
7
+ * shape a blog-only site wants.
8
+ */
9
+ indexPath: string;
10
+ /** Posts per index page. */
11
+ postsPerPage: number;
12
+ /** Emit an RSS feed route. Phase 4; accepted and ignored for now. */
13
+ rss: boolean;
14
+ /** Which `PageMeta` date orders the list. */
15
+ dateField: "created" | "updated";
16
+ }
17
+ /** Defaults for a `blog:` block that omits fields. */
18
+ export declare const BLOG_DEFAULTS: BlogEmitConfig;
19
+ /** One post in the emitted data file. */
20
+ export interface BlogPostRef {
21
+ slug: string;
22
+ title: string;
23
+ url: string;
24
+ description?: string;
25
+ /** ISO date from `meta[dateField]`. Absent when the post carries none. */
26
+ date?: string;
27
+ /** Only when it differs from `date` — the layout shows it as "updated". */
28
+ updated?: string;
29
+ author?: string[];
30
+ tags?: string[];
31
+ heroImage?: string;
32
+ /** Whole minutes, minimum 1. */
33
+ readingMinutes: number;
34
+ }
35
+ /** `src/data/blog.json`. */
36
+ export interface BlogData {
37
+ /** URL-form index path, prefixed with the COMBINED serving prefix. */
38
+ indexPath: string;
39
+ /**
40
+ * Filesystem-layout prefix (`site.basePath`), used to place route
41
+ * files under `src/pages/`. Deliberately NOT the same field as the URL
42
+ * prefix: Astro's `base` adds `urlBase` at request time, so baking it
43
+ * into file paths too would double it. Same split `taxonomy.ts` takes
44
+ * as `basePath` + `urlPrefix`.
45
+ */
46
+ basePath: string;
47
+ /**
48
+ * The COMBINED serving prefix (urlBase + basePath). What DocsLayout's
49
+ * `basePath` prop wants — it composes the llms.txt link, switcher
50
+ * hrefs and the alternate link from it, all of which are URLs. Kept
51
+ * separate from `basePath` above, which is a filesystem path.
52
+ */
53
+ urlPrefix: string;
54
+ /** `content.section`, when the build has one. Part of route placement. */
55
+ section?: string;
56
+ postsPerPage: number;
57
+ /** Newest first. */
58
+ posts: BlogPostRef[];
59
+ postCount: number;
60
+ }
61
+ /** Prev/next for one post, in date order. */
62
+ export interface BlogAdjacency {
63
+ /** The OLDER neighbour — back along the timeline, rendered on the left. */
64
+ prev?: {
65
+ title: string;
66
+ href: string;
67
+ };
68
+ /** The NEWER neighbour — forward, rendered on the right. */
69
+ next?: {
70
+ title: string;
71
+ href: string;
72
+ };
73
+ }
74
+ /**
75
+ * Count words in a page's body.
76
+ *
77
+ * Deliberately crude: whitespace-split on the extracted plain text.
78
+ * Reading time is a reading CUE, not a measurement, and a more precise
79
+ * count would not change the rendered minutes.
80
+ */
81
+ export declare function countWords(page: ExportPage): number;
82
+ /** Whole minutes at {@link WORDS_PER_MINUTE}, never less than 1. */
83
+ export declare function readingMinutes(page: ExportPage): number;
84
+ /**
85
+ * Build the post list from the page set.
86
+ *
87
+ * EVERY page is a post except the one that would occupy the index
88
+ * route itself. A site that declares `blog:` IS a blog; there is no
89
+ * second "is this a post?" axis to configure, and inventing one would
90
+ * mean a post silently vanishing because a field was missing.
91
+ *
92
+ * Undated posts sort last, in title order, rather than being dropped —
93
+ * a post with no date is an authoring mistake worth SEEING, not
94
+ * hiding.
95
+ */
96
+ export declare function buildBlogData(pages: ExportPage[], opts: {
97
+ basePath: string;
98
+ urlPrefix?: string;
99
+ /** `content.section`, threaded exactly as taxonomy.ts threads it. */
100
+ section?: string;
101
+ /** `excludeFromRoutes` predicate — see makeSlugExcluder. */
102
+ excludeSlug?: (slug: string) => boolean;
103
+ config: BlogEmitConfig;
104
+ }): BlogData;
105
+ /**
106
+ * Prev/next per post slug, in TIME order — `prev` older, `next` newer.
107
+ *
108
+ * The footer renders prev on the left and next on the right, so this is
109
+ * the direction a timeline runs: older behind you, newer ahead. It also
110
+ * matches how a series is read. `data.posts` is newest-first for the
111
+ * index listing, so the neighbours are deliberately taken the other way
112
+ * round from the array order.
113
+ *
114
+ * The first version had `prev` as the NEWER neighbour, reasoning that a
115
+ * blog archive is browsed backwards. That made the right-hand link walk
116
+ * a reader BACKWARDS through a series — part 1's "next" was nothing, and
117
+ * its left-hand link was part 2.
118
+ */
119
+ export declare function blogAdjacency(data: BlogData): Map<string, BlogAdjacency>;
120
+ /** Number of index pages at the configured page size. */
121
+ export declare function indexPageCount(data: BlogData): number;
122
+ /**
123
+ * Emit `src/data/blog.json` plus the index route(s).
124
+ *
125
+ * Page 1 lives at the index path; subsequent pages at
126
+ * `<indexPath>/page/<n>`. Data JSON is overwritten unconditionally —
127
+ * it is pure derived data — while route files respect the marker.
128
+ */
129
+ export declare function emitBlogFiles(outputDir: string, data: BlogData, config: BlogEmitConfig): {
130
+ routes: string[];
131
+ skipped: string[];
132
+ pruned: string[];
133
+ };
134
+ //# sourceMappingURL=blog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog.d.ts","sourceRoot":"","sources":["../src/blog.ts"],"names":[],"mappings":"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"}
package/dist/blog.js ADDED
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Blog emission for `dogsbay site build`.
3
+ *
4
+ * Dogsbay has no blog primitive; this adds one. See
5
+ * plans/blog-capability.md.
6
+ *
7
+ * The shape deliberately mirrors `taxonomy.ts`, which already solves
8
+ * the same problem — turn a page set into a generated list page:
9
+ *
10
+ * - `src/data/blog.json` — the post list, newest first
11
+ * - `src/pages<indexPath>/…` — the reverse-chron index route(s)
12
+ *
13
+ * A blog index is that emitter sorted by DATE rather than grouped by
14
+ * term. Route files carry the auto-generated marker and are left alone
15
+ * once a writer edits them, exactly as taxonomy routes are.
16
+ *
17
+ * Two things here are not obvious and are load-bearing:
18
+ *
19
+ * - **Adjacency is chronological, not navigational.** `getPagination`
20
+ * in `@dogsbay/docs-layout` walks `NavItem[]`, i.e. sidebar order. A
21
+ * blog has no sidebar and its prev/next is "the post before this
22
+ * one in time". Post pages get their links from {@link blogAdjacency}
23
+ * instead.
24
+ * - **Reading time is computed HERE, at emit time.** The word count
25
+ * never changes after the build, so doing it in the browser would
26
+ * re-walk the DOM on every page load for a constant.
27
+ */
28
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
29
+ import { dirname, join } from "node:path";
30
+ import { basePathSegments, joinBaseUrl, withBasePath } from "./base-path.js";
31
+ import { derivePageDescription, textOfNode } from "./taxonomy.js";
32
+ /** Words per minute used for reading-time estimates. */
33
+ const WORDS_PER_MINUTE = 200;
34
+ /** Marker that makes an emitted route file safe to regenerate. */
35
+ const AUTO_GENERATED_MARKER = "// AUTO-GENERATED by dogsbay";
36
+ /**
37
+ * The marker `emitAstroPages` puts on its generated ROUTE STUBS (the root
38
+ * redirect). Matched exactly, because content pages carry a different
39
+ * banner — `AUTO-GENERATED by \`dogsbay convert\`` — and treating that as
40
+ * regenerable meant a real `content/index.md` was silently overwritten by
41
+ * the blog index AND excluded from the post list. The page just vanished.
42
+ */
43
+ const STUB_MARKER = "// AUTO-GENERATED by dogsbay site build";
44
+ /** Defaults for a `blog:` block that omits fields. */
45
+ export const BLOG_DEFAULTS = {
46
+ indexPath: "/",
47
+ postsPerPage: 20,
48
+ rss: true,
49
+ dateField: "created",
50
+ };
51
+ /**
52
+ * Count words in a page's body.
53
+ *
54
+ * Deliberately crude: whitespace-split on the extracted plain text.
55
+ * Reading time is a reading CUE, not a measurement, and a more precise
56
+ * count would not change the rendered minutes.
57
+ */
58
+ export function countWords(page) {
59
+ const text = (page.tree ?? [])
60
+ .map((node) => textOfNode(node))
61
+ .join(" ")
62
+ .replace(/\s+/g, " ")
63
+ .trim();
64
+ if (text.length === 0)
65
+ return 0;
66
+ return text.split(" ").length;
67
+ }
68
+ /** Whole minutes at {@link WORDS_PER_MINUTE}, never less than 1. */
69
+ export function readingMinutes(page) {
70
+ const words = countWords(page);
71
+ if (words === 0)
72
+ return 1;
73
+ return Math.max(1, Math.round(words / WORDS_PER_MINUTE));
74
+ }
75
+ function firstString(value) {
76
+ return typeof value === "string" && value.length > 0 ? value : undefined;
77
+ }
78
+ /**
79
+ * Build the post list from the page set.
80
+ *
81
+ * EVERY page is a post except the one that would occupy the index
82
+ * route itself. A site that declares `blog:` IS a blog; there is no
83
+ * second "is this a post?" axis to configure, and inventing one would
84
+ * mean a post silently vanishing because a field was missing.
85
+ *
86
+ * Undated posts sort last, in title order, rather than being dropped —
87
+ * a post with no date is an authoring mistake worth SEEING, not
88
+ * hiding.
89
+ */
90
+ export function buildBlogData(pages, opts) {
91
+ const { basePath, section, config } = opts;
92
+ // URLs use the COMBINED prefix (urlBase + basePath) — the path the host
93
+ // actually serves under. Falling back to basePath keeps callers that
94
+ // only have the one value working, but a mounted site MUST pass
95
+ // urlPrefix or every post link drops the mount and lands on whatever
96
+ // owns the host root.
97
+ const urlPrefix = opts.urlPrefix ?? basePath;
98
+ const posts = [];
99
+ for (const page of pages) {
100
+ // The index route is generated from the post list; a page at that
101
+ // slug would be listing itself.
102
+ if (page.slug === "index" && config.indexPath === "/")
103
+ continue;
104
+ // Only pages that BECOME routes can be posts. emitAstroPages skips
105
+ // fragments and excludeFromRoutes matches, so listing them produced
106
+ // index cards and Newer/Older links to pages that never existed.
107
+ const fm = (page.frontmatter ?? {});
108
+ if (page.fragment === true || fm._fragment === true)
109
+ continue;
110
+ if (opts.excludeSlug?.(page.slug))
111
+ continue;
112
+ const meta = page.meta ?? {};
113
+ const date = meta[config.dateField];
114
+ const updated = meta.updated;
115
+ posts.push({
116
+ slug: page.slug,
117
+ title: page.title ?? page.slug,
118
+ // section threaded like taxonomy.ts's pageUrl — without it, a
119
+ // sectioned build linked /slug/ while the page lives at
120
+ // /section/slug/, so every blog link 404'd.
121
+ url: joinBaseUrl(urlPrefix, section, page.slug),
122
+ description: derivePageDescription(page),
123
+ ...(typeof date === "string" ? { date } : {}),
124
+ ...(typeof updated === "string" && updated !== date ? { updated } : {}),
125
+ ...(meta.author ? { author: meta.author } : {}),
126
+ ...(meta.tags ? { tags: meta.tags } : {}),
127
+ ...(firstString(fm.heroImage) ? { heroImage: firstString(fm.heroImage) } : {}),
128
+ readingMinutes: readingMinutes(page),
129
+ });
130
+ }
131
+ posts.sort((a, b) => {
132
+ if (a.date && b.date)
133
+ return a.date < b.date ? 1 : a.date > b.date ? -1 : 0;
134
+ if (a.date)
135
+ return -1;
136
+ if (b.date)
137
+ return 1;
138
+ return a.title.localeCompare(b.title);
139
+ });
140
+ return {
141
+ indexPath: config.indexPath === "/"
142
+ ? urlPrefix || "/"
143
+ : withBasePath(urlPrefix, config.indexPath),
144
+ basePath,
145
+ ...(section ? { section } : {}),
146
+ urlPrefix,
147
+ postsPerPage: config.postsPerPage,
148
+ posts,
149
+ postCount: posts.length,
150
+ };
151
+ }
152
+ /**
153
+ * Prev/next per post slug, in TIME order — `prev` older, `next` newer.
154
+ *
155
+ * The footer renders prev on the left and next on the right, so this is
156
+ * the direction a timeline runs: older behind you, newer ahead. It also
157
+ * matches how a series is read. `data.posts` is newest-first for the
158
+ * index listing, so the neighbours are deliberately taken the other way
159
+ * round from the array order.
160
+ *
161
+ * The first version had `prev` as the NEWER neighbour, reasoning that a
162
+ * blog archive is browsed backwards. That made the right-hand link walk
163
+ * a reader BACKWARDS through a series — part 1's "next" was nothing, and
164
+ * its left-hand link was part 2.
165
+ */
166
+ export function blogAdjacency(data) {
167
+ const out = new Map();
168
+ data.posts.forEach((post, i) => {
169
+ // posts[] is newest-first, so the OLDER neighbour is the next index.
170
+ const prev = data.posts[i + 1];
171
+ const next = data.posts[i - 1];
172
+ out.set(post.slug, {
173
+ ...(prev ? { prev: { title: prev.title, href: prev.url } } : {}),
174
+ ...(next ? { next: { title: next.title, href: next.url } } : {}),
175
+ });
176
+ });
177
+ return out;
178
+ }
179
+ /**
180
+ * Regenerate an emitted route unless a writer has taken it over.
181
+ *
182
+ * Same contract as `taxonomy.ts`: the marker means "ours, safe to
183
+ * overwrite"; its absence means someone edited the file and their work
184
+ * wins. A content-derived page also counts as ours — it carries a
185
+ * `dogsbay convert` banner — so a scaffold's placeholder index does not
186
+ * block the blog index from claiming the route.
187
+ */
188
+ function shouldRegenerate(path) {
189
+ if (!existsSync(path))
190
+ return true;
191
+ const contents = readFileSync(path, "utf-8");
192
+ // Ours (a previous blog index) or a generated route stub — replaceable.
193
+ // Anything else, INCLUDING a content page, belongs to the author.
194
+ return contents.includes(AUTO_GENERATED_MARKER) || contents.includes(STUB_MARKER);
195
+ }
196
+ /** Number of index pages at the configured page size. */
197
+ export function indexPageCount(data) {
198
+ return Math.max(1, Math.ceil(data.posts.length / data.postsPerPage));
199
+ }
200
+ /**
201
+ * Emit `src/data/blog.json` plus the index route(s).
202
+ *
203
+ * Page 1 lives at the index path; subsequent pages at
204
+ * `<indexPath>/page/<n>`. Data JSON is overwritten unconditionally —
205
+ * it is pure derived data — while route files respect the marker.
206
+ */
207
+ export function emitBlogFiles(outputDir, data, config) {
208
+ mkdirSync(join(outputDir, "src", "data"), { recursive: true });
209
+ writeFileSync(join(outputDir, "src", "data", "blog.json"), JSON.stringify(data, null, 2));
210
+ const routes = [];
211
+ const skipped = [];
212
+ // Routes live where the PAGES live: <basePath>/<section>/… . Missing
213
+ // the section put the index at the site root while every post sat one
214
+ // level down.
215
+ const segments = [
216
+ ...basePathSegments(data.basePath),
217
+ ...(data.section ? basePathSegments(`/${data.section}`) : []),
218
+ ];
219
+ const indexSegments = config.indexPath === "/" ? [] : basePathSegments(config.indexPath);
220
+ const total = indexPageCount(data);
221
+ for (let pageNo = 1; pageNo <= total; pageNo++) {
222
+ const parts = pageNo === 1
223
+ ? [...segments, ...indexSegments, "index.astro"]
224
+ : [...segments, ...indexSegments, "page", `${pageNo}.astro`];
225
+ const routePath = join(outputDir, "src", "pages", ...parts);
226
+ if (!shouldRegenerate(routePath)) {
227
+ skipped.push(routePath);
228
+ continue;
229
+ }
230
+ mkdirSync(dirname(routePath), { recursive: true });
231
+ writeFileSync(routePath, buildIndexRoute(pageNo, total));
232
+ routes.push(routePath);
233
+ }
234
+ // Prune pagination routes the current post count no longer needs.
235
+ // Without this, deleting posts (or raising postsPerPage) left the
236
+ // previous build's page/3.astro in place: it still compiles, slices
237
+ // past the end of the list, and serves a live "No posts yet. Page 3
238
+ // of 2".
239
+ const pruned = [];
240
+ for (let stale = total + 1;; stale++) {
241
+ const stalePath = join(outputDir, "src", "pages", ...segments, ...indexSegments, "page", `${stale}.astro`);
242
+ if (!existsSync(stalePath))
243
+ break;
244
+ if (!shouldRegenerate(stalePath))
245
+ break; // writer took it over
246
+ rmSync(stalePath, { force: true });
247
+ pruned.push(stalePath);
248
+ }
249
+ return { routes, skipped, pruned };
250
+ }
251
+ /**
252
+ * One index route file.
253
+ *
254
+ * Reads the emitted JSON at build time (not runtime) and hands a slice
255
+ * to `<BlogIndex>`. Kept deliberately thin: all layout lives in the
256
+ * component, so a template fix propagates on rebuild rather than being
257
+ * baked into every generated route.
258
+ */
259
+ function buildIndexRoute(pageNo, totalPages) {
260
+ // wideLayout: blog chrome centres prose at max-w-3xl, which is right for
261
+ // a post and far too narrow for a three-across card grid. The index is
262
+ // the one blog route that wants the full measure.
263
+ //
264
+ // `@/` alias throughout, not relative paths — page 1 and page N sit at
265
+ // different depths under src/pages, and a hardcoded `../../` is correct
266
+ // for exactly one of them. Matches the taxonomy route template.
267
+ return `---
268
+ ${AUTO_GENERATED_MARKER} site build — safe to delete; will regenerate next build.
269
+ // To customize this page, remove the marker line above (or modify the file
270
+ // in any way that drops it) and dogsbay will leave your edits alone.
271
+ import "@/styles/global.css";
272
+ import DocsLayout from "@dogsbay/docs-layout/DocsLayout.astro";
273
+ import BlogIndex from "@dogsbay/docs-layout/BlogIndex.astro";
274
+ import type { SiteConfig } from "@dogsbay/types";
275
+ import blog from "@/data/blog.json";
276
+ import navData from "@/data/nav.json";
277
+ import siteConfigData from "@/data/site.json";
278
+ const siteConfig = siteConfigData as SiteConfig;
279
+ // Read untyped: favicon is site.json data, not part of the published
280
+ // SiteConfig type, so this needs no @dogsbay/types release.
281
+ const favicon = (siteConfigData as { favicon?: string }).favicon;
282
+ const pageNo = ${pageNo};
283
+ const totalPages = ${totalPages};
284
+ const start = (pageNo - 1) * blog.postsPerPage;
285
+ const posts = blog.posts.slice(start, start + blog.postsPerPage);
286
+ const title = pageNo === 1 ? siteConfig.siteName : \`\${siteConfig.siteName} — page \${pageNo}\`;
287
+ ---
288
+ <DocsLayout
289
+ siteName={siteConfig.siteName}
290
+ title={title}
291
+ nav={navData as any[]}
292
+ headings={[]}
293
+ siteUrl={siteConfig.siteUrl || "/"}
294
+ description={siteConfig.description || undefined}
295
+ ogImage={siteConfig.ogImage || undefined}
296
+ twitterHandle={siteConfig.twitterHandle || undefined}
297
+ themeColor={siteConfig.themeColor || undefined}
298
+ repoUrl={siteConfig.repoUrl || undefined}
299
+ plausibleDomain={siteConfig.plausible?.domain}
300
+ plausibleScriptUrl={siteConfig.plausible?.scriptUrl}
301
+ tagPrefixes={siteConfig.tagPrefixes}
302
+ tagLabels={siteConfig.tagLabels}
303
+ taxonomyIndexPaths={siteConfig.taxonomyIndexPaths}
304
+ chrome="blog"
305
+ favicon={favicon}
306
+ basePath={blog.urlPrefix}
307
+ autoH1={false}
308
+ wideLayout={true}
309
+ >
310
+ <BlogIndex
311
+ posts={posts}
312
+ indexPath={blog.indexPath}
313
+ pageNo={pageNo}
314
+ totalPages={totalPages}
315
+ />
316
+ </DocsLayout>
317
+ `;
318
+ }
319
+ //# sourceMappingURL=blog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog.js","sourceRoot":"","sources":["../src/blog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Diff decorations — DiffTree marks rendered through the NORMAL
3
+ * serializer (plans/tree-diff-rendering.md §2).
4
+ *
5
+ * `@dogsbay/tree-diff` annotates nodes with structural fields
6
+ * (`diff`, `diffInline`, `diffPeer`, `diffMovedFrom`); this module
7
+ * reads them STRUCTURALLY (no dependency on tree-diff) and decorates
8
+ * the serializer's output:
9
+ *
10
+ * - block marks → a `data-diff` attribute injected into the
11
+ * rendered snippet's first opening tag (attribute injection works
12
+ * uniformly for components, table rows, and plain tags — no
13
+ * invalid wrapper elements inside tables/tabs), styled by the
14
+ * generated theme CSS;
15
+ * - changed prose → the word-level segments render as
16
+ * `<ins>/<del>` runs (the merged reading view);
17
+ * - changed code → a before/after pair: the old side renders
18
+ * through the same code renderer, labeled, above the new side.
19
+ *
20
+ * A tree with no marks renders byte-identically to before — the
21
+ * decoration layer is invisible until a DiffTree arrives.
22
+ */
23
+ import type { TreeNode } from "@dogsbay/types";
24
+ /** Structural mirror of @dogsbay/tree-diff's DiffNode fields. */
25
+ export interface DiffMarks {
26
+ diff?: "added" | "removed" | "changed" | "moved";
27
+ diffInline?: {
28
+ op: "eq" | "ins" | "del";
29
+ text: string;
30
+ }[];
31
+ diffPeer?: TreeNode;
32
+ diffMovedFrom?: string[];
33
+ }
34
+ export declare const diffMarksOf: (node: TreeNode) => DiffMarks;
35
+ export declare function hasDiffMarks(node: TreeNode): boolean;
36
+ /**
37
+ * Inject attributes into the FIRST opening tag of a rendered
38
+ * snippet. Handles components (`<TableRow`), plain tags (`<p`), and
39
+ * skips leading whitespace/JSX expressions.
40
+ */
41
+ export declare function injectAttrs(rendered: string, attrs: string): string;
42
+ /**
43
+ * Render the merged word-level segments of a changed prose node.
44
+ * Formatting inside changed prose flattens to text in this view —
45
+ * the marks are the content (the unchanged siblings keep their full
46
+ * inline rendering).
47
+ */
48
+ export declare function inlineSegmentsToHtml(segments: NonNullable<DiffMarks["diffInline"]>): string;
49
+ export interface MergedCodeDiff {
50
+ /** Unified code: unchanged lines, removed lines, added lines. */
51
+ code: string;
52
+ /** 1-based line numbers, for CodeRich's diffAdd/diffRemove props. */
53
+ addLines: number[];
54
+ removeLines: number[];
55
+ }
56
+ /**
57
+ * Merge two code texts into ONE unified block, reporting the changed
58
+ * lines BY NUMBER rather than by `[!code]` comment markers.
59
+ *
60
+ * Markers were the first design and they leak: Shiki can only strip a
61
+ * marker it recognizes as a comment in the grammar, so in `plaintext`
62
+ * — which every unknown language (`package-install`, doc-tool tokens)
63
+ * falls back to — the marker renders as literal text in the reader's
64
+ * code. Line numbers are language-independent and can't leak.
65
+ *
66
+ * Returns null over the line cap; callers fall back to before/after.
67
+ */
68
+ export declare function buildMergedCodeDiff(oldCode: string, newCode: string): MergedCodeDiff | null;
69
+ /**
70
+ * Decorate one rendered node. `renderPeer` renders the other side of
71
+ * a changed pair through the same serializer (code before/after).
72
+ */
73
+ export declare function decorateDiff(node: TreeNode, rendered: string, renderPeer: (peer: TreeNode) => string): string;
74
+ /**
75
+ * The diff stylesheet the generated site carries — theme-token based,
76
+ * both color modes, no hardcoded palette (key-decisions doctrine).
77
+ */
78
+ export declare const DIFF_CSS = "/* tree-diff decorations \u2014 one DiffTree, rendered by the normal components.\n Mark styling is GUARDED by html:not([data-diff-hidden]) rather than\n force-reset under hidden mode: a reset like \"border-left: none\n !important\" also clobbers a component's OWN border (an added warning\n callout lost its left edge \u2014 user find, adapters/mysql). Guarded\n rules simply stop applying, and component styling survives. */\n[data-diff] { position: relative; }\nhtml:not([data-diff-hidden]) [data-diff=\"added\"] {\n background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 12%, transparent);\n border-left: 3px solid var(--color-success, oklch(0.72 0.19 150));\n padding-left: 0.75rem;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"] {\n background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 10%, transparent);\n border-left: 3px solid var(--color-destructive, oklch(0.64 0.21 25));\n padding-left: 0.75rem;\n opacity: 0.75;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"] :is(p, li, code, pre) { text-decoration: line-through; text-decoration-thickness: 1px; }\nhtml:not([data-diff-hidden]) [data-diff=\"changed\"] {\n border-left: 3px solid var(--color-warning, oklch(0.8 0.16 85));\n padding-left: 0.75rem;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"moved\"] {\n border-left: 3px dashed var(--color-info, oklch(0.7 0.14 240));\n padding-left: 0.75rem;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"moved\"][data-diff-from]::after {\n content: \"moved from \" attr(data-diff-from);\n display: block;\n font-size: 0.75rem;\n color: var(--color-muted-foreground, inherit);\n opacity: 0.8;\n}\n\n/* Explicit \"added\"/\"removed\" label pills straddling the block's top\n edge. Components with their own chrome (callouts, code blocks)\n camouflage a tint-only mark \u2014 a green-tinted warning callout reads\n as a callout variant, not as new content (user feedback,\n better-auth adapters/mysql). Table rows are excluded (a pill can't\n straddle a tr); nested marks defer to the outermost label. */\nhtml:not([data-diff-hidden]) [data-diff=\"added\"]:not(tr, th, td)::before,\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"]:not(tr, th, td)::before {\n position: absolute;\n top: 0;\n right: 0.6rem;\n transform: translateY(-55%);\n font-size: 0.65rem;\n font-weight: 600;\n letter-spacing: 0.04em;\n line-height: 1.4;\n padding: 0 0.5rem;\n border-radius: 99px;\n z-index: 1;\n text-decoration: none;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"added\"]:not(tr, th, td)::before {\n content: \"added\";\n background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 25%, var(--color-background, Canvas));\n border: 1px solid color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 45%, transparent);\n}\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"]:not(tr, th, td)::before {\n content: \"removed\";\n background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 20%, var(--color-background, Canvas));\n border: 1px solid color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 45%, transparent);\n}\n[data-diff] [data-diff]::before { content: none !important; }\n\n/* Nav change markers (the sidebar is client-rendered from nav.json, so\n the styles ship here rather than with the component). Same contract as\n the tab markers: a GLYPH carries the meaning, colour reinforces it,\n visually-hidden text announces it. */\n.db-nav-mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n width: 1rem;\n height: 1rem;\n margin-left: auto;\n border-radius: 99px;\n font-size: 0.75rem;\n font-weight: 700;\n line-height: 1;\n color: var(--color-muted-foreground, currentColor);\n}\n.db-nav-mark[data-nav-mark=\"added\"] { color: var(--color-success, oklch(0.72 0.19 150)); }\n.db-nav-mark[data-nav-mark=\"changed\"] { color: var(--color-warning, oklch(0.8 0.16 85)); }\n.db-nav-mark[data-nav-mark=\"removed\"] { color: var(--color-destructive, oklch(0.64 0.21 25)); }\n.db-nav-mark[data-nav-mark=\"moved\"] { color: var(--color-info, oklch(0.7 0.14 240)); }\n/* a subtree marker is a hint about descendants, not a claim about this row */\n.db-nav-mark[data-nav-mark-subtree] { opacity: 0.55; }\n/* removed pages get a SECOND non-colour signal */\n/* Belt-and-braces: a subtree mark must never strike through its own row. */\n[data-nav-mark-row=\"removed\"]:not([data-nav-mark-subtree]) { text-decoration: line-through; opacity: 0.7; }\n@media (forced-colors: active) {\n .db-nav-mark { color: CanvasText; }\n}\n\n/* Which tab changed? A marked panel is invisible until opened, so its\n trigger carries a marker (the group's bar says \"in here\", the marker\n says \"this one\"). The marker is a GLYPH, not a colored dot: colour\n alone fails WCAG 1.4.1 and background-tinted dots vanish entirely in\n forced-colors mode. Colour reinforces; shape carries the meaning.\n Visually-hidden text rides along (emitted by the serializer). */\n.db-tab-mark {\n display: inline-block;\n margin-left: 0.35rem;\n font-size: 0.75rem;\n font-weight: 700;\n line-height: 1;\n vertical-align: middle;\n color: var(--color-muted-foreground, currentColor);\n}\n[data-diff-hidden] .db-tab-mark { display: none; }\n.db-tab-mark[data-diff-tab=\"changed\"] { color: var(--color-warning, oklch(0.8 0.16 85)); }\n.db-tab-mark[data-diff-tab=\"added\"] { color: var(--color-success, oklch(0.72 0.19 150)); }\n.db-tab-mark[data-diff-tab=\"removed\"] { color: var(--color-destructive, oklch(0.64 0.21 25)); }\n.db-tab-mark[data-diff-tab=\"moved\"] { color: var(--color-info, oklch(0.7 0.14 240)); }\n@media (forced-colors: active) {\n .db-tab-mark { color: CanvasText; }\n}\nins.db-diff-ins {\n text-decoration: none;\n background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 22%, transparent);\n border-radius: 2px;\n padding: 0 1px;\n}\ndel.db-diff-del {\n background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 18%, transparent);\n border-radius: 2px;\n padding: 0 1px;\n}\n.db-diff-pair { display: grid; gap: 0.25rem; margin-block: 1rem; }\n.db-diff-pair-label {\n font-size: 0.7rem;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--color-muted-foreground, inherit);\n opacity: 0.8;\n}\ntr[data-diff] { border-left-width: 3px; }\n\n/* Folded unchanged sections are CHROME, not content callouts:\n dashed, dimmed, compact \u2014 visually \"collapsed context\". */\n.db-diff-fold {\n border: 1px dashed color-mix(in oklch, currentColor 25%, transparent) !important;\n border-left-width: 1px !important;\n background: transparent !important;\n opacity: 0.7;\n margin-block: 0.5rem !important;\n}\n.db-diff-fold:hover, .db-diff-fold[open] { opacity: 1; }\n.db-diff-fold summary, .db-diff-fold [data-slot=\"trigger\"] {\n font-size: 0.85rem;\n}\n\n/* The comparison banner at the top of each reader page. */\n.db-diff-banner-row { display: flex; flex-wrap: wrap; gap: 0.4rem 0.8rem; align-items: center; width: 100%; }\n.db-diff-banner {\n border: 1px solid color-mix(in oklch, currentColor 15%, transparent);\n border-radius: 8px;\n padding: 0.75rem 1rem;\n margin-block-end: 1.5rem;\n font-size: 0.85rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.4rem 1rem;\n align-items: center;\n}\n.db-diff-banner .db-chip {\n display: inline-block;\n border-radius: 99px;\n padding: 0.05rem 0.6rem;\n font-size: 0.75rem;\n border: 1px solid transparent;\n}\n.db-chip-added { background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 15%, transparent); }\n.db-chip-removed { background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 12%, transparent); }\n.db-chip-changed { background: color-mix(in oklch, var(--color-warning, oklch(0.8 0.16 85)) 15%, transparent); }\n.db-chip-moved { background: color-mix(in oklch, var(--color-info, oklch(0.7 0.14 240)) 12%, transparent); }\n.db-chip-muted { border-color: color-mix(in oklch, currentColor 25%, transparent); opacity: 0.8; }\n\n/* The persistent view-switcher bar (Reader / Docs-ops / Agent):\n perspectives on ONE comparison \u2014 tabs are links, verification\n lives in the chrome, the Monitor CTA sits next to the value. */\n.db-cmp-bar {\n position: sticky;\n /* BELOW the docs header, not under it: both were sticky at top:0 and\n the header's higher z-index simply swallowed this bar on scroll \u2014\n the chrome carrying the trust contract was the chrome that vanished.\n --db-header-height is declared by DocsLayout (the header sizes itself\n from the same variable, so they cannot drift). */\n top: var(--db-header-height, 3.25rem);\n z-index: 30;\n display: flex;\n flex-wrap: wrap;\n gap: 0.4rem 0.9rem;\n align-items: center;\n padding: 0.5rem 0.9rem;\n margin-block-end: 1rem;\n border: 1px solid color-mix(in oklch, currentColor 15%, transparent);\n border-radius: 8px;\n background: var(--color-background, Canvas);\n font-size: 0.8rem;\n}\n.db-cmp-bar .db-chip {\n display: inline-block;\n border-radius: 99px;\n padding: 0.05rem 0.6rem;\n font-size: 0.75rem;\n border: 1px solid transparent;\n}\n.db-cmp-bar button.db-chip { cursor: pointer; background: transparent; font: inherit; font-size: 0.75rem; color: inherit; }\n.db-cmp-tabs {\n display: inline-flex;\n gap: 0.15rem;\n border: 1px solid color-mix(in oklch, currentColor 20%, transparent);\n border-radius: 99px;\n padding: 0.1rem;\n}\n.db-cmp-tab {\n padding: 0.1rem 0.7rem;\n border-radius: 99px;\n text-decoration: none;\n color: inherit;\n}\n.db-cmp-tab[aria-current=\"page\"] {\n background: color-mix(in oklch, currentColor 12%, transparent);\n font-weight: 600;\n}\n.db-cmp-pairs { position: relative; display: inline-block; }\n.db-cmp-pairs summary { cursor: pointer; }\n.db-cmp-pairs ul {\n position: absolute;\n top: 100%;\n left: 0;\n margin: 0.25rem 0 0;\n padding: 0.35rem 0.75rem;\n list-style: none;\n background: var(--color-background, Canvas);\n border: 1px solid color-mix(in oklch, currentColor 20%, transparent);\n border-radius: 8px;\n white-space: nowrap;\n z-index: 40;\n}\n.db-cmp-monitor { text-decoration: none; border-color: color-mix(in oklch, currentColor 30%, transparent) !important; }\n.db-cmp-monitor:hover { background: color-mix(in oklch, currentColor 10%, transparent); }\n\n/* A wholly added/removed PAGE carries one mark around all its content.\n The per-block tint would be overwhelming at page scale, so page level\n gets a stronger edge and a much lighter wash \u2014 legible at a glance,\n still readable as prose. The \"added\"/\"removed\" pill (the ::before on\n [data-diff]) rides on this wrapper, which is exactly right: it labels\n the page, once. */\nhtml:not([data-diff-hidden]) [data-diff].db-diff-page {\n border-left-width: 5px;\n padding-left: 1rem;\n padding-block: 0.5rem;\n border-radius: 0 6px 6px 0;\n}\nhtml:not([data-diff-hidden]) [data-diff=\"added\"].db-diff-page {\n background: color-mix(in oklch, var(--color-success, oklch(0.72 0.19 150)) 5%, transparent);\n}\nhtml:not([data-diff-hidden]) [data-diff=\"removed\"].db-diff-page {\n background: color-mix(in oklch, var(--color-destructive, oklch(0.64 0.21 25)) 5%, transparent);\n opacity: 1; /* the page is READABLE \u2014 the mark says removed, not the dimming */\n}\n/* \u2026and a removed PAGE is not struck through \u2014 that is for a deleted line,\n not for a document the reader still needs to read. */\n[data-diff=\"removed\"].db-diff-page :is(p, li, code, pre) { text-decoration: none; }\n\n/* Changed raw HTML: the faithful new render, plus the previous version\n one click away. Without it a deletion inside opaque markup (a dropped\n table column) is simply invisible \u2014 the reader is told \"something\n changed\" and shown no evidence. */\n.db-diff-html-before {\n margin-block-start: 0.5rem;\n font-size: 0.8rem;\n border: 1px dashed color-mix(in oklch, currentColor 25%, transparent);\n border-radius: 6px;\n padding: 0.25rem 0.6rem;\n}\n/* Full-strength text: the summary is a CONTROL \u2014 dimming it (opacity or\n muted-foreground) drops it below 4.5:1 and axe rightly fails it. */\n.db-diff-html-before summary { cursor: pointer; color: inherit; }\n.db-diff-html-before[open] summary { margin-block-end: 0.5rem; }\n[data-diff-hidden] .db-diff-html-before { display: none; }\n\n/* Merged code diffs carry a hidden clean twin (the post-change\n render); Hide highlights swaps them. */\n.db-diff-code { position: relative; }\n.db-diff-code-clean { display: none; }\n[data-diff-hidden] .db-diff-code-clean { display: block; }\n[data-diff-hidden] .db-diff-code-merged { display: none; }\n\n/* Whitespace-only code edits are still marked (indentation is\n semantic in YAML/Python/markdown) \u2014 but NAMED, so the reader\n is not hunting an invisible 3-space shift. */\nhtml:not([data-diff-hidden]) .db-diff-code-ws .db-diff-code-merged::before {\n content: \"whitespace only\";\n position: absolute;\n top: 0;\n right: 0.6rem;\n transform: translateY(-55%);\n z-index: 1;\n font-size: 0.65rem;\n font-weight: 600;\n letter-spacing: 0.04em;\n line-height: 1.4;\n padding: 0 0.5rem;\n border-radius: 99px;\n background: color-mix(in oklch, var(--color-warning, oklch(0.8 0.16 85)) 22%, var(--color-background, Canvas));\n border: 1px solid color-mix(in oklch, var(--color-warning, oklch(0.8 0.16 85)) 45%, transparent);\n}\n\n/* Hide highlights \u2014 read the target version clean. One attribute on\n <html>: the guarded mark rules above stop applying by themselves;\n only content that does not exist in the target version (removed\n blocks, del runs, the \"before\" code side) needs hiding, plus the\n ins tint. */\n[data-diff-hidden] :is([data-diff=\"removed\"], del.db-diff-del, .db-diff-pair-label) { display: none !important; }\n[data-diff-hidden] ins.db-diff-ins { background: transparent; padding: 0; }\n";
79
+ //# sourceMappingURL=diff-decoration.d.ts.map