@entropicwarrior/sdoc 0.2.1 → 0.2.3

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.
@@ -307,7 +307,7 @@ Content of Section B.
307
307
 
308
308
  # References @references
309
309
  {
310
- Assign a slug with \`@slug\` on a heading, then reference it anywhere with \`@slug\`:
310
+ Assign a slug with \`@slug\` on a heading, then reference it elsewhere in the same document with \`@slug\`:
311
311
 
312
312
  ```
313
313
  # Setup @setup {
@@ -318,6 +318,14 @@ Content of Section B.
318
318
  Make sure you complete @setup first.
319
319
  }
320
320
  ```
321
+
322
+ References are **document-local only** — \`@slug\` resolves within the current file. To point a reader to a section in another file, use a link with a fragment:
323
+
324
+ ```
325
+ See [Setup](./other-file.sdoc#setup) for details.
326
+ ```
327
+
328
+ Do not write \`@setup\` when the target scope is in a different file — it will be flagged as a broken reference.
321
329
  }
322
330
 
323
331
  # Code Blocks @code-blocks
@@ -672,6 +680,25 @@ Content of Section B.
672
680
  ```
673
681
  }
674
682
 
683
+ # Cross-Document @slug References @cross-doc-refs
684
+ {
685
+ \`@slug\` references are document-local. Using \`@slug\` to refer to a section in another file produces a broken reference error:
686
+
687
+ **Wrong** — \`@setup\` does not exist in this file:
688
+
689
+ ```
690
+ See `getting-started.sdoc` @setup for installation steps.
691
+ ```
692
+
693
+ **Right** — use a link with a fragment:
694
+
695
+ ```
696
+ See [Setup](./getting-started.sdoc#setup) for installation steps.
697
+ ```
698
+
699
+ The fragment (\`#setup\`) matches the target scope's \`@id\`. Use \`\\@\` if you need a literal \`@\` in text without triggering reference resolution.
700
+ }
701
+
675
702
  # @References Inside Link Labels @refs-in-link-labels
676
703
  {
677
704
  Inline \`@references\` are parsed everywhere, including inside link labels. If you mention a scope ID in a link label, escape the \`@\` to prevent it being treated as a reference to the current document:
@@ -393,6 +393,8 @@ Content of Section B.
393
393
  {
394
394
  {[.]
395
395
  - A reference is `@id` in text (unescaped)
396
+ - References are document-local: `@id` resolves to a scope within the same file only. Cross-document reference syntax may be introduced in a future version but is not part of v0.2
397
+ - To link to a section in another file, use a standard link with a fragment: `[Label](./other-file.sdoc#section-id)`
396
398
  - References link to the scope with that ID
397
399
  - ID uniqueness is strongly recommended; tooling may warn on duplicates
398
400
  - References are parsed inside link labels — use `\@` to include a literal `@` in a link label without triggering a reference
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.3",
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
  }