@entropicwarrior/sdoc 0.2.1 → 0.2.2

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/sdoc.js +54 -2
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@entropicwarrior/sdoc",
3
3
  "displayName": "SDOC",
4
4
  "description": "A plain-text documentation format with explicit brace scoping — deterministic parsing, AI-agent efficiency, and 10-50x token savings vs Markdown.",
5
- "version": "0.2.1",
5
+ "version": "0.2.2",
6
6
  "publisher": "entropicwarrior",
7
7
  "license": "MIT",
8
8
  "repository": {
package/src/sdoc.js CHANGED
@@ -2122,6 +2122,36 @@ const PRINT_STYLE = `
2122
2122
 
2123
2123
  const MERMAID_CDN = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js";
2124
2124
  const KATEX_CDN_CSS = "https://cdn.jsdelivr.net/npm/katex@0.16/dist/katex.min.css";
2125
+ const HLJS_CDN = "https://cdn.jsdelivr.net/npm/@highlightjs/cdn-assets@11.11.1/highlight.min.js";
2126
+
2127
+ // Custom highlight.js grammar for SDOC language – registered before highlightElement calls
2128
+ const HLJS_SDOC_GRAMMAR = `hljs.registerLanguage('sdoc',function(hljs){return{name:'SDOC',aliases:['sdoc'],contains:[
2129
+ hljs.COMMENT('^\\\\s*//','$'),
2130
+ {className:'section',begin:'^\\\\s*#+\\\\s+',end:'$',contains:[
2131
+ {className:'symbol',begin:'@[A-Za-z_][A-Za-z0-9_-]*'}
2132
+ ]},
2133
+ {className:'meta',begin:'^\\\\s*@[A-Za-z_][A-Za-z0-9_-]*',end:'(?=\\\\s*\\\\{|$)'},
2134
+ {className:'code',begin:'\`\`\`',end:'\`\`\`',contains:[hljs.BACKSLASH_ESCAPE]},
2135
+ {className:'code',begin:'\`[^\`]+\`'},
2136
+ {className:'string',begin:'\\\\[',end:'\\\\]\\\\([^)]*\\\\)',contains:[
2137
+ {className:'link',begin:'\\\\(',end:'\\\\)'}
2138
+ ]},
2139
+ {className:'keyword',begin:'\\\\{[!?+\\\\-=~^]',end:'[!?+\\\\-=~^]\\\\}'},
2140
+ {className:'keyword',begin:'\\\\{\\\\[(?:\\\\.|#|\\\\d+|table)\\\\]'},
2141
+ {className:'strong',begin:'\\\\*\\\\*',end:'\\\\*\\\\*'},
2142
+ {className:'emphasis',begin:'(?<!\\\\*)\\\\*(?!\\\\*)',end:'\\\\*(?!\\\\*)'},
2143
+ {className:'deletion',begin:'~~',end:'~~'},
2144
+ {className:'bullet',begin:'^\\\\s*[-]\\\\s'},
2145
+ {className:'bullet',begin:'^\\\\s*\\\\d+[.)]\\\\ '},
2146
+ {className:'quote',begin:'^\\\\s*>',end:'$'},
2147
+ {className:'symbol',begin:'(?<!\\\\\\\\)@[A-Za-z_][A-Za-z0-9_-]*'},
2148
+ {className:'attr',begin:'[A-Za-z_][A-Za-z0-9_-]*(?=\\\\s*:)',end:':',excludeEnd:true}
2149
+ ]}});`;
2150
+
2151
+ // Highlight.js GitHub light theme (inline so it works inside shadow DOM in the web viewer)
2152
+ const HLJS_LIGHT_CSS = "pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}";
2153
+ // Highlight.js GitHub dark theme color overrides (used in @media and VS Code dark-mode CSS)
2154
+ const HLJS_DARK_COLORS_CSS = ".hljs{color:#c9d1d9;background:transparent}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}";
2125
2155
 
2126
2156
  function hasMermaidBlocks(nodes) {
2127
2157
  for (const node of nodes) {
@@ -2136,6 +2166,19 @@ function hasMermaidBlocks(nodes) {
2136
2166
  return false;
2137
2167
  }
2138
2168
 
2169
+ function hasHighlightableCodeBlocks(nodes) {
2170
+ for (const node of nodes) {
2171
+ if (node.type === "code" && node.lang && node.lang !== "mermaid" && node.lang !== "math") return true;
2172
+ if (node.children && hasHighlightableCodeBlocks(node.children)) return true;
2173
+ if (node.items) {
2174
+ for (const item of node.items) {
2175
+ if (item.children && hasHighlightableCodeBlocks(item.children)) return true;
2176
+ }
2177
+ }
2178
+ }
2179
+ return false;
2180
+ }
2181
+
2139
2182
  function renderBodyNodes(nodes) {
2140
2183
  return nodes
2141
2184
  .map((node, index) => {
@@ -2186,6 +2229,15 @@ function renderHtmlDocumentFromParsed(parsed, title, options = {}) {
2186
2229
  const katexCssTag = body.includes('class="katex"')
2187
2230
  ? `\n<link rel="stylesheet" href="${KATEX_CDN_CSS}" />`
2188
2231
  : "";
2232
+ const hasHljs = hasHighlightableCodeBlocks(parsed.nodes);
2233
+ // Highlight.js CSS is inlined (not a <link>) so it is extracted by parseDocHtml in the web viewer
2234
+ // and applied inside shadow DOM. The @media query handles dark mode in browsers.
2235
+ const hljsCssInline = hasHljs
2236
+ ? `\n${HLJS_LIGHT_CSS}\n.sdoc-code code.hljs{padding:0;background:transparent}\n@media (prefers-color-scheme:dark){${HLJS_DARK_COLORS_CSS}}`
2237
+ : "";
2238
+ const hljsScript = hasHljs
2239
+ ? `\n<script src="${HLJS_CDN}"></script>\n<script>${HLJS_SDOC_GRAMMAR}\ndocument.querySelectorAll('pre.sdoc-code code[class*="language-"]').forEach(function(b){hljs.highlightElement(b);});</script>`
2240
+ : "";
2189
2241
 
2190
2242
  return `<!DOCTYPE html>
2191
2243
  <html lang="en">
@@ -2194,7 +2246,7 @@ function renderHtmlDocumentFromParsed(parsed, title, options = {}) {
2194
2246
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2195
2247
  <title>${escapeHtml(title)}</title>
2196
2248
  <style>
2197
- ${cssBase}${cssAppend}
2249
+ ${cssBase}${cssAppend}${hljsCssInline}
2198
2250
  </style>${katexCssTag}
2199
2251
  </head>
2200
2252
  <body>
@@ -2208,7 +2260,7 @@ ${cssBase}${cssAppend}
2208
2260
  </main>
2209
2261
  </div>
2210
2262
  ${footerContent ? `<footer class="sdoc-page-footer">${footerContent}</footer>` : ""}
2211
- </div>${scriptTag}${mermaidScript}
2263
+ </div>${scriptTag}${mermaidScript}${hljsScript}
2212
2264
  </body>
2213
2265
  </html>`;
2214
2266
  }