@rightkit/doc-render 0.0.0-stage → 0.1.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.
Files changed (49) hide show
  1. package/LICENSE-APACHE +201 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +2 -2
  4. package/dist/citations/formatter.d.ts +7 -0
  5. package/dist/citations/formatter.js +106 -0
  6. package/dist/citations/types.d.ts +46 -0
  7. package/dist/citations/types.js +6 -0
  8. package/dist/detectors/callout.d.ts +13 -0
  9. package/dist/detectors/callout.js +32 -0
  10. package/dist/detectors/comparison.d.ts +15 -0
  11. package/dist/detectors/comparison.js +38 -0
  12. package/dist/detectors/descriptionList.d.ts +9 -0
  13. package/dist/detectors/descriptionList.js +16 -0
  14. package/dist/detectors/faq.d.ts +14 -0
  15. package/dist/detectors/faq.js +37 -0
  16. package/dist/detectors/frontmatter.d.ts +11 -0
  17. package/dist/detectors/frontmatter.js +23 -0
  18. package/dist/detectors/index.d.ts +22 -0
  19. package/dist/detectors/index.js +85 -0
  20. package/dist/detectors/nodeText.d.ts +3 -0
  21. package/dist/detectors/nodeText.js +11 -0
  22. package/dist/detectors/richTable.d.ts +10 -0
  23. package/dist/detectors/richTable.js +35 -0
  24. package/dist/detectors/stepper.d.ts +12 -0
  25. package/dist/detectors/stepper.js +58 -0
  26. package/dist/detectors/types.d.ts +26 -0
  27. package/dist/detectors/types.js +2 -0
  28. package/dist/footnotes.d.ts +12 -0
  29. package/dist/footnotes.js +23 -0
  30. package/dist/headingSlugs.d.ts +4 -0
  31. package/dist/headingSlugs.js +19 -0
  32. package/dist/index.d.ts +15 -0
  33. package/dist/index.js +11 -0
  34. package/dist/parseMarkdown.d.ts +7 -0
  35. package/dist/parseMarkdown.js +39 -0
  36. package/dist/remarkCitations.d.ts +13 -0
  37. package/dist/remarkCitations.js +78 -0
  38. package/dist/remarkDefListAbbr.d.ts +30 -0
  39. package/dist/remarkDefListAbbr.js +292 -0
  40. package/dist/remarkWikiLink.d.ts +10 -0
  41. package/dist/remarkWikiLink.js +63 -0
  42. package/dist/renderers.d.ts +17 -0
  43. package/dist/renderers.js +88 -0
  44. package/dist/security.d.ts +12 -0
  45. package/dist/security.js +78 -0
  46. package/dist/shikiHighlighter.d.ts +26 -0
  47. package/dist/shikiHighlighter.js +184 -0
  48. package/package.json +64 -3
  49. package/styles.css +10 -0
@@ -0,0 +1,184 @@
1
+ import onigWasmUrl from "shiki/onig.wasm?url";
2
+ /**
3
+ * The one Shiki highlighter shared by the Markdown preview (`CodeBlock`), the hybrid editor's
4
+ * code-fence decorations and the language validator.
5
+ *
6
+ * Built on `shiki/core` rather than `shiki/bundle/web` so the build ships only what is listed here:
7
+ * two themes and a curated grammar set, each grammar its own lazy chunk. Oniguruma stays the regex
8
+ * engine — the JavaScript engine tokenizes identically but transpiling a grammar's patterns made the
9
+ * first highlight of a fence several times slower (≈1.6–2 s vs ≈0.3 s for a 40-line TS/C++ fence).
10
+ * Its WASM ships as a binary asset fetched on first use, not as a base64 JS module that has to be
11
+ * parsed. When WebAssembly is unavailable (macOS Lockdown Mode) the JavaScript engine takes over.
12
+ */
13
+ export const SHIKI_LIGHT_THEME = "github-light";
14
+ export const SHIKI_DARK_THEME = "github-dark";
15
+ /** Curated grammars: mainstream Markdown fence languages. Anything else renders as plain text. */
16
+ const LANGUAGE_LOADERS = new Map([
17
+ ["astro", () => import("shiki/langs/astro.mjs")],
18
+ ["bat", () => import("shiki/langs/bat.mjs")],
19
+ ["c", () => import("shiki/langs/c.mjs")],
20
+ ["cpp", () => import("shiki/langs/cpp.mjs")],
21
+ ["csharp", () => import("shiki/langs/csharp.mjs")],
22
+ ["css", () => import("shiki/langs/css.mjs")],
23
+ ["csv", () => import("shiki/langs/csv.mjs")],
24
+ ["diff", () => import("shiki/langs/diff.mjs")],
25
+ ["docker", () => import("shiki/langs/docker.mjs")],
26
+ ["dotenv", () => import("shiki/langs/dotenv.mjs")],
27
+ ["glsl", () => import("shiki/langs/glsl.mjs")],
28
+ ["go", () => import("shiki/langs/go.mjs")],
29
+ ["graphql", () => import("shiki/langs/graphql.mjs")],
30
+ ["handlebars", () => import("shiki/langs/handlebars.mjs")],
31
+ ["html", () => import("shiki/langs/html.mjs")],
32
+ ["http", () => import("shiki/langs/http.mjs")],
33
+ ["ini", () => import("shiki/langs/ini.mjs")],
34
+ ["java", () => import("shiki/langs/java.mjs")],
35
+ ["javascript", () => import("shiki/langs/javascript.mjs")],
36
+ ["jinja", () => import("shiki/langs/jinja.mjs")],
37
+ ["json", () => import("shiki/langs/json.mjs")],
38
+ ["json5", () => import("shiki/langs/json5.mjs")],
39
+ ["jsonc", () => import("shiki/langs/jsonc.mjs")],
40
+ ["jsonl", () => import("shiki/langs/jsonl.mjs")],
41
+ ["jsx", () => import("shiki/langs/jsx.mjs")],
42
+ ["julia", () => import("shiki/langs/julia.mjs")],
43
+ ["kotlin", () => import("shiki/langs/kotlin.mjs")],
44
+ ["less", () => import("shiki/langs/less.mjs")],
45
+ ["lua", () => import("shiki/langs/lua.mjs")],
46
+ ["make", () => import("shiki/langs/make.mjs")],
47
+ ["markdown", () => import("shiki/langs/markdown.mjs")],
48
+ ["mdx", () => import("shiki/langs/mdx.mjs")],
49
+ ["php", () => import("shiki/langs/php.mjs")],
50
+ ["postcss", () => import("shiki/langs/postcss.mjs")],
51
+ ["powershell", () => import("shiki/langs/powershell.mjs")],
52
+ ["python", () => import("shiki/langs/python.mjs")],
53
+ ["r", () => import("shiki/langs/r.mjs")],
54
+ ["regexp", () => import("shiki/langs/regexp.mjs")],
55
+ ["ruby", () => import("shiki/langs/ruby.mjs")],
56
+ ["rust", () => import("shiki/langs/rust.mjs")],
57
+ ["sass", () => import("shiki/langs/sass.mjs")],
58
+ ["scss", () => import("shiki/langs/scss.mjs")],
59
+ ["shellscript", () => import("shiki/langs/shellscript.mjs")],
60
+ ["shellsession", () => import("shiki/langs/shellsession.mjs")],
61
+ ["sql", () => import("shiki/langs/sql.mjs")],
62
+ ["svelte", () => import("shiki/langs/svelte.mjs")],
63
+ ["swift", () => import("shiki/langs/swift.mjs")],
64
+ ["toml", () => import("shiki/langs/toml.mjs")],
65
+ ["tsx", () => import("shiki/langs/tsx.mjs")],
66
+ ["typescript", () => import("shiki/langs/typescript.mjs")],
67
+ ["vue", () => import("shiki/langs/vue.mjs")],
68
+ ["xml", () => import("shiki/langs/xml.mjs")],
69
+ ["yaml", () => import("shiki/langs/yaml.mjs")],
70
+ ]);
71
+ /** Shiki's own aliases for the curated grammars (mirrors each grammar's `aliases`). */
72
+ const LANGUAGE_ALIASES = new Map([
73
+ ["batch", "bat"],
74
+ ["cmd", "bat"],
75
+ ["c++", "cpp"],
76
+ ["c#", "csharp"],
77
+ ["cs", "csharp"],
78
+ ["dockerfile", "docker"],
79
+ ["hbs", "handlebars"],
80
+ ["properties", "ini"],
81
+ ["js", "javascript"],
82
+ ["cjs", "javascript"],
83
+ ["mjs", "javascript"],
84
+ ["jl", "julia"],
85
+ ["kt", "kotlin"],
86
+ ["kts", "kotlin"],
87
+ ["makefile", "make"],
88
+ ["md", "markdown"],
89
+ ["ps", "powershell"],
90
+ ["ps1", "powershell"],
91
+ ["pwsh", "powershell"],
92
+ ["py", "python"],
93
+ ["regex", "regexp"],
94
+ ["rb", "ruby"],
95
+ ["rs", "rust"],
96
+ ["bash", "shellscript"],
97
+ ["sh", "shellscript"],
98
+ ["shell", "shellscript"],
99
+ ["zsh", "shellscript"],
100
+ ["console", "shellsession"],
101
+ ["ts", "typescript"],
102
+ ["cts", "typescript"],
103
+ ["mts", "typescript"],
104
+ ["yml", "yaml"],
105
+ ]);
106
+ /** Names Shiki renders as plain text without a grammar. */
107
+ const PLAIN_TEXT_LANGUAGES = new Set(["text", "txt", "plain", "plaintext"]);
108
+ /**
109
+ * Canonical Shiki language id for a fence info string, `"text"` for the plain-text names, or
110
+ * `null` when the language is not in the curated set.
111
+ */
112
+ export function resolveShikiLanguage(language) {
113
+ const normalized = (language ?? "").trim().toLowerCase();
114
+ if (normalized.length === 0)
115
+ return null;
116
+ if (PLAIN_TEXT_LANGUAGES.has(normalized))
117
+ return "text";
118
+ if (LANGUAGE_LOADERS.has(normalized))
119
+ return normalized;
120
+ return LANGUAGE_ALIASES.get(normalized) ?? null;
121
+ }
122
+ async function createRegexEngine() {
123
+ try {
124
+ const { createOnigurumaEngine } = await import("shiki/engine/oniguruma");
125
+ // Hand Shiki bytes, never the Response: for a Response it calls `instantiateStreaming`, which
126
+ // rejects unless the asset protocol serves `application/wasm`. Any fetch or instantiate failure
127
+ // lands in the catch below and falls back to the JavaScript regex engine.
128
+ return await createOnigurumaEngine(() => fetch(onigWasmUrl).then((response) => {
129
+ if (!response.ok)
130
+ throw new Error(`Shiki WASM failed to load (${response.status})`);
131
+ return response.arrayBuffer();
132
+ }));
133
+ }
134
+ catch {
135
+ const { createJavaScriptRegexEngine } = await import("shiki/engine/javascript");
136
+ return createJavaScriptRegexEngine({ forgiving: true });
137
+ }
138
+ }
139
+ let highlighterPromise = null;
140
+ /** Lazy singleton — Shiki, its engine and the two themes load when the first code block needs them. */
141
+ export function getShikiHighlighter() {
142
+ if (!highlighterPromise) {
143
+ highlighterPromise = (async () => {
144
+ const [{ createHighlighterCore }, engine, light, dark] = await Promise.all([
145
+ import("shiki/core"),
146
+ createRegexEngine(),
147
+ import("shiki/themes/github-light.mjs"),
148
+ import("shiki/themes/github-dark.mjs"),
149
+ ]);
150
+ return createHighlighterCore({ themes: [light.default, dark.default], langs: [], engine });
151
+ })();
152
+ highlighterPromise.catch(() => {
153
+ highlighterPromise = null;
154
+ });
155
+ }
156
+ return highlighterPromise;
157
+ }
158
+ const languageLoads = new Map();
159
+ /**
160
+ * Loads the grammar for `language` into `highlighter` and returns the id to pass to Shiki, or
161
+ * `null` when the language is unknown or its grammar failed to load (callers render plain text).
162
+ */
163
+ export async function ensureShikiLanguage(highlighter, language) {
164
+ const resolved = resolveShikiLanguage(language);
165
+ if (resolved === null || resolved === "text")
166
+ return resolved;
167
+ let load = languageLoads.get(resolved);
168
+ if (!load) {
169
+ const loader = LANGUAGE_LOADERS.get(resolved);
170
+ if (!loader)
171
+ return null;
172
+ load = loader()
173
+ .then((module) => highlighter.loadLanguage(module.default))
174
+ .then(() => true);
175
+ languageLoads.set(resolved, load);
176
+ load.catch(() => languageLoads.delete(resolved));
177
+ }
178
+ try {
179
+ return (await load) ? resolved : null;
180
+ }
181
+ catch {
182
+ return null;
183
+ }
184
+ }
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@rightkit/doc-render",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Brand-neutral, read-only document renderer (Markdown, sanitized HTML, safe email frame) for Right Suite readers.",
5
+ "license": "MIT OR Apache-2.0",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./styles.css": "./styles.css"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "styles.css",
19
+ "README.md",
20
+ "LICENSE-MIT",
21
+ "LICENSE-APACHE"
22
+ ],
23
+ "sideEffects": [
24
+ "*.css"
25
+ ],
26
+ "peerDependencies": {
27
+ "react": ">=18 <21"
28
+ },
29
+ "dependencies": {
30
+ "@types/mdast": "^4.0.4",
31
+ "dompurify": "^3.4.16",
32
+ "github-slugger": "^2.0.0",
33
+ "katex": "^0.18.7",
34
+ "mermaid": "^11.17.2",
35
+ "remark-frontmatter": "^5.0.0",
36
+ "remark-gfm": "^4.0.1",
37
+ "remark-math": "^6.0.0",
38
+ "remark-parse": "^11.0.0",
39
+ "remark-smartypants": "^3.0.3",
40
+ "shiki": "^4.4.3",
41
+ "unified": "^11.0.5"
42
+ },
43
+ "devDependencies": {
44
+ "@types/react": "^19.0.0",
45
+ "@types/react-dom": "^19.0.0",
46
+ "esbuild": "^0.28.1",
47
+ "react": "^19.0.0",
48
+ "react-dom": "^19.0.0",
49
+ "typescript": "^5.7.0",
50
+ "vitest": "^2.1.8"
51
+ },
52
+ "publishConfig": {
53
+ "registry": "https://registry.npmjs.org",
54
+ "access": "public"
55
+ },
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/bogusyogi/claude.git",
59
+ "directory": "tools/rightkit/packages/doc-render"
60
+ },
61
+ "scripts": {
62
+ "build": "tsc -p tsconfig.json && cp src/styles.css styles.css",
63
+ "typecheck": "tsc -p tsconfig.json --noEmit",
64
+ "test": "vitest run",
65
+ "test:browser": "node test/browser-e2e.mjs"
66
+ }
6
67
  }
package/styles.css ADDED
@@ -0,0 +1,10 @@
1
+ .rk-doc-markdown, .rk-doc-html { box-sizing:border-box; max-width:78rem; margin:0 auto; padding:2rem 1.25rem; line-height:1.65; overflow-wrap:anywhere; }
2
+ .rk-doc-markdown h1, .rk-doc-markdown h2, .rk-doc-markdown h3, .rk-doc-markdown h4, .rk-doc-html h1, .rk-doc-html h2, .rk-doc-html h3, .rk-doc-html h4 { margin:1.5em 0 .5em; line-height:1.25; }
3
+ .rk-doc-markdown p, .rk-doc-html p { margin:0 0 1rem; }
4
+ .rk-doc-markdown a, .rk-doc-html a { color:#2563a9; text-decoration:underline; }
5
+ .rk-doc-markdown img, .rk-doc-html img { max-width:100%; height:auto; }
6
+ .rk-doc-markdown blockquote, .rk-doc-html blockquote { margin:1rem 0; padding:.5rem 1rem; border-left:4px solid #c7c7c7; color:#555; }
7
+ .rk-doc-markdown pre, .rk-doc-html pre { overflow:auto; padding:.8rem 1rem; border-radius:.35rem; background:#f1f3f5; }
8
+ .rk-doc-markdown code, .rk-doc-html code { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; }
9
+ .rk-doc-markdown table, .rk-doc-html table { width:100%; border-collapse:collapse; margin:1rem 0; }
10
+ .rk-doc-markdown th, .rk-doc-markdown td, .rk-doc-html th, .rk-doc-html td { padding:.4rem .6rem; border:1px solid #d5d5d5; text-align:left; vertical-align:top; }