doxla 0.2.0 → 0.3.0

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
@@ -2,6 +2,16 @@
2
2
 
3
3
  Improve documentation discoverability within repos. Doxla discovers all `.md` files in your repository, builds a beautiful docs viewer, and deploys it to GitHub Pages.
4
4
 
5
+ ## Why?
6
+
7
+ Documentation is most valuable when it lives next to the code it describes — but in-repo markdown has a discoverability problem. Files get buried in directory trees, raw markdown is hard to read in editors, and preview plugins vary by IDE.
8
+
9
+ Meanwhile, AI coding assistants (Claude Code, Copilot, Cursor) work with what's in the repository. Docs in Notion or Google Docs are invisible to them. In-repo markdown is context they can read and act on.
10
+
11
+ Doxla bridges the gap: keep your docs as `.md` files in your repo (where both humans and AI agents can find them), and Doxla turns them into a readable, searchable site.
12
+
13
+ Read the full rationale: [Why Doxla?](RATIONALE.md)
14
+
5
15
  ## Quick Start
6
16
 
7
17
  ### Deploy to GitHub Pages
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doxla",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Improve documentation discoverability within repos",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,7 +37,7 @@ export function DocPage({ doc, theme }: DocPageProps) {
37
37
 
38
38
  <Separator className="mb-6" />
39
39
 
40
- <MarkdownRenderer content={doc.content} theme={theme} />
40
+ <MarkdownRenderer content={doc.content} theme={theme} docPath={doc.path} />
41
41
  </div>
42
42
  );
43
43
  }
@@ -9,9 +9,33 @@ import type { Theme } from "../App";
9
9
  interface MarkdownRendererProps {
10
10
  content: string;
11
11
  theme: Theme;
12
+ docPath?: string;
12
13
  }
13
14
 
14
- export function MarkdownRenderer({ content, theme }: MarkdownRendererProps) {
15
+ function resolveDocLink(href: string, docPath: string): string | null {
16
+ if (!href.match(/\.md(#.*)?$/i)) return null;
17
+ if (/^https?:\/\//.test(href)) return null;
18
+
19
+ const [filePart, anchor] = href.split("#");
20
+ const docDir = docPath.includes("/") ? docPath.replace(/\/[^/]+$/, "") : "";
21
+ const parts = (docDir ? `${docDir}/${filePart}` : filePart).split("/");
22
+
23
+ const resolved: string[] = [];
24
+ for (const part of parts) {
25
+ if (part === "..") resolved.pop();
26
+ else if (part !== ".") resolved.push(part);
27
+ }
28
+
29
+ const slug = resolved
30
+ .join("/")
31
+ .replace(/\.md$/i, "")
32
+ .toLowerCase()
33
+ .replace(/[^a-z0-9/.-]/g, "-");
34
+
35
+ return `#/doc/${slug}${anchor ? `#${anchor}` : ""}`;
36
+ }
37
+
38
+ export function MarkdownRenderer({ content, theme, docPath }: MarkdownRendererProps) {
15
39
  const syntaxStyle = theme === "dark" ? oneDark : oneLight;
16
40
 
17
41
  const components: Components = useMemo(
@@ -43,8 +67,18 @@ export function MarkdownRenderer({ content, theme }: MarkdownRendererProps) {
43
67
  </SyntaxHighlighter>
44
68
  );
45
69
  },
70
+ a(props) {
71
+ const { href, children, ...rest } = props;
72
+ if (href && docPath) {
73
+ const resolved = resolveDocLink(href, docPath);
74
+ if (resolved) {
75
+ return <a href={resolved} {...rest}>{children}</a>;
76
+ }
77
+ }
78
+ return <a href={href} {...rest}>{children}</a>;
79
+ },
46
80
  }),
47
- [syntaxStyle],
81
+ [syntaxStyle, docPath],
48
82
  );
49
83
 
50
84
  return (