@entropicwarrior/sdoc 0.2.7 → 0.2.9

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
@@ -25,12 +25,23 @@ Markdown has no formal structure — section boundaries are ambiguous, extractio
25
25
 
26
26
  **PDF and HTML export** — export any document to A4 PDF via headless Chrome, or to standalone HTML. Available as both a CLI tool and a VS Code command.
27
27
 
28
- **A VS Code extension** — live preview, sticky scroll, code folding, document symbols, mermaid rendering, and commands for all the above.
28
+ **A VS Code extension** — live preview, sticky scroll, code folding, document symbols, mermaid/SVG rendering, and commands for all the above.
29
29
 
30
30
  ## Quick Start
31
31
 
32
+ ### Install from the VS Code Marketplace
33
+
34
+ Search for **"SDOC - Docs for Human/Agent Teams"** by **Entropic Warrior MS Enfin** in the VS Code Extensions panel, or install from the command line:
35
+
36
+ ```bash
37
+ code --install-extension entropicwarrior-msenfin.vscode-sdoc
38
+ ```
39
+
40
+ This gives you automatic updates. If you previously installed from a `.vsix` file, uninstall that version first to avoid conflicts.
41
+
42
+ ### Build from source
43
+
32
44
  ```bash
33
- # Build and install the VS Code extension
34
45
  npm install
35
46
  npm run package
36
47
  code --install-extension dist/sdoc-*.vsix
@@ -159,6 +170,10 @@ Fenced with triple backticks, optional language tag for syntax highlighting. The
159
170
 
160
171
  Code blocks tagged `mermaid` render as SVG diagrams — flowcharts, sequence diagrams, class diagrams, state diagrams, and more.
161
172
 
173
+ ### SVG Diagrams
174
+
175
+ Code blocks tagged `svg` render as inline SVG graphics, giving full control over shapes, layout, and styling. `<script>` and `<foreignObject>` tags are stripped for security.
176
+
162
177
  ### Images
163
178
 
164
179
  Markdown-style images with optional width and alignment:
@@ -408,6 +408,22 @@ Content of Section B.
408
408
  Mermaid supports flowcharts (\`graph\`), sequence diagrams (\`sequenceDiagram\`), class diagrams (\`classDiagram\`), state diagrams (\`stateDiagram-v2\`), and more. The Mermaid library is loaded from CDN only when a document contains mermaid blocks.
409
409
  }
410
410
 
411
+ # SVG Diagrams @svg
412
+ {
413
+ Code blocks with the \`svg\` language tag render as inline SVG graphics. This gives full control over shapes, layout, and styling for diagrams that need more precision than Mermaid offers:
414
+
415
+ ````
416
+ ```svg
417
+ <svg viewBox="0 0 200 60" xmlns="http://www.w3.org/2000/svg">
418
+ <rect x="10" y="10" width="80" height="40" rx="5" fill="#4a90d9" />
419
+ <text x="50" y="35" text-anchor="middle" fill="white" font-family="sans-serif" font-size="13">Hello</text>
420
+ </svg>
421
+ ```
422
+ ````
423
+
424
+ For security, \`<script>\` and \`<foreignObject>\` tags are stripped from the SVG content before rendering. All other SVG elements pass through as-is.
425
+ }
426
+
411
427
  # Math Blocks @math-blocks
412
428
  {
413
429
  Code blocks with the \`math\` language tag are rendered as display equations via KaTeX:
@@ -165,6 +165,7 @@
165
165
  \`{[table]}\` | \`<table>\`
166
166
  Code fence | \`<pre><code>\`
167
167
  Mermaid code fence | \`<pre class="mermaid">\` (rendered as SVG)
168
+ SVG code fence | \`<div class="sdoc-svg-block">\` (inline SVG)
168
169
  \`**bold**\` | \`<strong>\`
169
170
  \`*italic*\` | \`<em>\`
170
171
  \`\\\`code\\\`\` | \`<code>\`
@@ -10,8 +10,9 @@
10
10
  # About @about
11
11
  {
12
12
  The formal SDOC v0.2 specification. Defines syntax for scopes,
13
- lists, tables, code blocks, inline formatting, references,
14
- the meta scope, scope types, data blocks, and comments.
13
+ lists, tables (with formulas), code blocks, inline formatting,
14
+ references, autolinks (including bare email detection), the meta
15
+ scope, scope types, data blocks, comments, and collapsible scopes.
15
16
  Includes the formal EBNF grammar. Read for edge cases and
16
17
  parser behaviour questions. For a friendlier user-facing
17
18
  reference, see \`docs/reference/syntax.sdoc\`.
@@ -607,6 +608,9 @@ Content of Section B.
607
608
  - Optional language tag after the opening fence
608
609
  - The special language tag `math` renders the block as a display equation via KaTeX (no copy button)
609
610
  - The special language tag `mermaid` renders the block as an SVG diagram via the Mermaid library
611
+ - The special language tag `svg` renders the block as an inline SVG graphic. Script and foreignObject tags are stripped for security.
612
+ - Fences may use more than three backticks (e.g. four or five) to nest code blocks that themselves contain triple backticks. The closing fence must match the opening fence length.
613
+ - Code blocks display a copy-to-clipboard button (visible on hover) in all rendered HTML output
610
614
  }
611
615
 
612
616
  # Include by Link @code-includes
@@ -819,9 +823,14 @@ Content of Section B.
819
823
  {[.]
820
824
  - Key matching is case-insensitive
821
825
  - The pattern requires at least one space after the colon (`key: value`, not `key:value`)
822
- - Well-known keys: `style`, `styleappend`/`style-append`, `header`, `footer`, `sdoc-version`
826
+ - Well-known keys: `style`, `styleappend`/`style-append`, `header`, `footer`, `sdoc-version`, `type`, `tags`, `uuid`, `company`, `confidential`
823
827
  - `sdoc-version` identifies the SDOC format version the document targets (current: `0.2`). A parser warning is emitted when this key is missing.
824
- - All other keys are stored as custom properties (e.g., `author`, `date`, `version`, `status`, `tags`)
828
+ - `type` classifies the document (e.g. `doc`, `skill`). Promoted to `meta.type`.
829
+ - `tags` is a comma-separated list of keywords. Promoted to `meta.tags` (array).
830
+ - `uuid` provides a stable unique identifier. Promoted to `meta.uuid`.
831
+ - `company` sets the company name for footer display. Promoted to `meta.company`.
832
+ - `confidential` marks the document as confidential. When set (any truthy value), a confidential notice banner is displayed at the top of the rendered output. Promoted to `meta.confidential`.
833
+ - All other keys are stored as custom properties in `meta.properties` (e.g., `author`, `date`, `version`, `status`)
825
834
  - Sub-scope syntax takes precedence: if both `# Style { path }` and `style: path` exist, the sub-scope value wins
826
835
  - Key:value and sub-scope syntax can be mixed freely in the same meta scope
827
836
  - Each key:value pair must be on its own paragraph line (separated by blank lines from other pairs)
@@ -832,7 +841,7 @@ Content of Section B.
832
841
 
833
842
  # Interactive Preview @interactive-preview
834
843
  {
835
- The VSCode extension provides an interactive preview with the following features. These are preview-only behaviours and do not affect the SDOC format or static HTML export.
844
+ The VSCode extension provides an interactive preview. Some features (collapsible scopes, copy-to-clipboard) also work in all rendered HTML output. Click-to-navigate is preview-only.
836
845
 
837
846
  # Theme Support @theme-support
838
847
  {
@@ -843,8 +852,8 @@ Content of Section B.
843
852
  - Dark mode overrides the \`--sdoc-*\` CSS custom properties and hardcoded \`rgba()\` colours (table headers, code blocks, confidential notices, error blocks)
844
853
  - Mermaid diagrams use the \`dark\` theme when in a dark colour theme and \`neutral\` otherwise
845
854
  - Theme changes take effect immediately (CSS-driven) and trigger a full preview rebuild (for Mermaid re-initialisation)
846
- - Exported HTML follows the user's OS colour preference via \`@media (prefers-color-scheme: dark)\`. Print output always uses light theme colours
847
- - Custom stylesheets (\`sdoc.config.json\` or \`@meta style\`) that use \`--sdoc-*\` variables inherit dark values automatically. Hardcoded colours in custom styles are not overridden
855
+ - Exported HTML follows the user's OS colour preference via `\@media (prefers-color-scheme: dark)`. Print output always uses light theme colours
856
+ - Custom stylesheets (`sdoc.config.json` or `\@meta style`) that use `--sdoc-*` variables inherit dark values automatically. Hardcoded colours in custom styles are not overridden
848
857
  }
849
858
  }
850
859
 
@@ -852,11 +861,14 @@ Content of Section B.
852
861
  {
853
862
  Scope headings that have children display a toggle triangle, visible on hover. Clicking the triangle collapses the scope's children (hides the content below the heading). Clicking again expands them.
854
863
 
864
+ Collapsible scopes work in all HTML output: the VS Code preview, exported HTML, Open in Browser, and the document server viewer. The toggle CSS and click handler are included in the default stylesheet and rendered HTML.
865
+
855
866
  {[.]
856
867
  - The triangle points right when collapsed, down when expanded
857
- - Collapse state is preserved across preview refreshes using webview state
868
+ - In the VS Code preview, collapse state is preserved across refreshes using webview state
858
869
  - Scopes are identified by their `@id` if present, otherwise by source line number
859
870
  - Only scopes with children show the toggle
871
+ - Toggles are hidden in print output and all sections are expanded
860
872
  }
861
873
  }
862
874
 
@@ -1011,9 +1023,10 @@ Content of Section A.
1011
1023
 
1012
1024
  code_block = fence_open raw_text fence_close ;
1013
1025
  data_block = data_fence_open raw_text fence_close ;
1014
- fence_open = "```" [lang] [ws "src:" path] [ws "lines:" range] newline ;
1015
- data_fence_open = "```" lang ws ":data" newline ;
1016
- fence_close = "```" newline ;
1026
+ fence_open = backticks [lang] [ws "src:" path] [ws "lines:" range] newline ;
1027
+ data_fence_open = backticks lang ws ":data" newline ;
1028
+ fence_close = backticks newline ; (* must match opening fence length *)
1029
+ backticks = "```" { "`" } ; (* 3 or more backticks *)
1017
1030
 
1018
1031
  line_comment = "//" { any_char } newline ; (* discarded, not in AST *)
1019
1032
 
package/llms.txt CHANGED
@@ -8,7 +8,7 @@ The authoring guide (`docs/reference/sdoc-authoring.sdoc`) is written as an AI a
8
8
 
9
9
  ## Docs
10
10
 
11
- - [SDOC Authoring Guide](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/docs/reference/sdoc-authoring.sdoc): Skill document — how to write correct SDOC files. Covers document structure, inline formatting, block types (lists, tables, code, blockquotes), mermaid diagrams, and common mistakes. Start here to learn the format.
11
+ - [SDOC Authoring Guide](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/docs/reference/sdoc-authoring.sdoc): Skill document — how to write correct SDOC files. Covers document structure, inline formatting, block types (lists, tables, code, blockquotes), mermaid/SVG diagrams, and common mistakes. Start here to learn the format.
12
12
  - [SDOC Specification](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/lexica/specification.sdoc): Formal v0.1 specification with EBNF grammar. Defines syntax for scopes, lists, tables, code blocks, inline formatting, references, and the meta scope.
13
13
  - [Why SDOC Over Markdown](https://raw.githubusercontent.com/entropicwarrior/sdoc/main/docs/guide/why-sdoc.sdoc): The case for SDOC — structural problems with Markdown, why explicit scoping solves them, parsing safety as a security property, and the adoption path forward.
14
14
 
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@entropicwarrior/sdoc",
3
- "displayName": "SDOC",
3
+ "displayName": "SDOC - Docs for Human/Agent Teams",
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.7",
6
- "publisher": "entropicwarrior",
5
+ "version": "0.2.9",
6
+ "publisher": "entropicwarrior-msenfin",
7
7
  "license": "MIT",
8
8
  "repository": {
9
9
  "type": "git",
package/src/sdoc.js CHANGED
@@ -1390,6 +1390,14 @@ function escapeHtml(value) {
1390
1390
  .replace(/"/g, "&quot;");
1391
1391
  }
1392
1392
 
1393
+ function sanitizeSvg(svg) {
1394
+ return svg
1395
+ .replace(/<script[\s>][\s\S]*?<\/script\s*>/gi, "")
1396
+ .replace(/<script\s*\/>/gi, "")
1397
+ .replace(/<foreignObject[\s>][\s\S]*?<\/foreignObject\s*>/gi, "")
1398
+ .replace(/<foreignObject\s*\/>/gi, "");
1399
+ }
1400
+
1393
1401
  function escapeAttr(value) {
1394
1402
  return escapeHtml(value).replace(/'/g, "&#39;");
1395
1403
  }
@@ -1928,6 +1936,9 @@ function renderNode(node, depth) {
1928
1936
  if (node.lang === "mermaid") {
1929
1937
  return `<pre class="mermaid"${dl}>${escapeHtml(node.text)}</pre>`;
1930
1938
  }
1939
+ if (node.lang === "svg") {
1940
+ return `<div class="sdoc-svg-block"${dl}>${sanitizeSvg(node.text)}</div>`;
1941
+ }
1931
1942
  if (node.lang === "math") {
1932
1943
  return `<div class="sdoc-math sdoc-math-block"${dl}>${renderKatex(node.text, true)}</div>`;
1933
1944
  }
@@ -2371,6 +2382,16 @@ const DEFAULT_STYLE = `
2371
2382
  margin-left: 0.5%;
2372
2383
  }
2373
2384
 
2385
+ .sdoc-svg-block {
2386
+ margin: 0.6rem 0;
2387
+ text-align: center;
2388
+ }
2389
+
2390
+ .sdoc-svg-block svg {
2391
+ max-width: 100%;
2392
+ height: auto;
2393
+ }
2394
+
2374
2395
  .sdoc-code {
2375
2396
  background: rgba(22, 21, 19, 0.06);
2376
2397
  border: 1px solid var(--sdoc-border);
@@ -2416,6 +2437,54 @@ const DEFAULT_STYLE = `
2416
2437
  pointer-events: none;
2417
2438
  }
2418
2439
 
2440
+ /* Collapsible scope toggles */
2441
+ .sdoc-heading:has(.sdoc-toggle) {
2442
+ position: relative;
2443
+ }
2444
+
2445
+ .sdoc-toggle {
2446
+ position: absolute;
2447
+ left: -1.4em;
2448
+ top: 0;
2449
+ bottom: 0;
2450
+ width: 1.2em;
2451
+ cursor: pointer;
2452
+ opacity: 0;
2453
+ transition: opacity 0.15s;
2454
+ }
2455
+
2456
+ .sdoc-toggle::before {
2457
+ content: '';
2458
+ display: block;
2459
+ width: 0.45em;
2460
+ height: 0.45em;
2461
+ border-right: 2px solid var(--sdoc-muted);
2462
+ border-bottom: 2px solid var(--sdoc-muted);
2463
+ transition: transform 0.15s;
2464
+ transform: rotate(45deg);
2465
+ position: absolute;
2466
+ top: 0.18em;
2467
+ left: 50%;
2468
+ margin-left: -0.3em;
2469
+ }
2470
+
2471
+ .sdoc-scope:hover > .sdoc-heading > .sdoc-toggle {
2472
+ opacity: 1;
2473
+ }
2474
+
2475
+ .sdoc-scope.sdoc-collapsed > .sdoc-heading > .sdoc-toggle {
2476
+ opacity: 0.6;
2477
+ }
2478
+
2479
+ .sdoc-scope.sdoc-collapsed > .sdoc-heading > .sdoc-toggle::before {
2480
+ transform: rotate(-45deg);
2481
+ margin-left: -0.15em;
2482
+ }
2483
+
2484
+ .sdoc-scope.sdoc-collapsed > .sdoc-scope-children {
2485
+ display: none;
2486
+ }
2487
+
2419
2488
  `;
2420
2489
 
2421
2490
  const PRINT_STYLE = `
@@ -2447,9 +2516,12 @@ const PRINT_STYLE = `
2447
2516
  html {
2448
2517
  font-size: 80%;
2449
2518
  }
2450
- .sdoc-copy-btn {
2519
+ .sdoc-copy-btn, .sdoc-toggle {
2451
2520
  display: none;
2452
2521
  }
2522
+ .sdoc-scope.sdoc-collapsed > .sdoc-scope-children {
2523
+ display: block;
2524
+ }
2453
2525
  body {
2454
2526
  height: auto;
2455
2527
  overflow: visible;
@@ -2476,6 +2548,10 @@ const PRINT_STYLE = `
2476
2548
  }
2477
2549
  `;
2478
2550
 
2551
+ const COLLAPSE_SCRIPT = `document.addEventListener("click",function(e){if(!e.target.classList.contains("sdoc-toggle"))return;e.stopPropagation();var s=e.target.closest(".sdoc-scope");if(s)s.classList.toggle("sdoc-collapsed")});`;
2552
+
2553
+ const COPY_SCRIPT = `document.addEventListener("click",function(e){if(!e.target.classList.contains("sdoc-copy-btn"))return;e.stopPropagation();e.preventDefault();var w=e.target.closest(".sdoc-code-wrap");if(!w)return;var c=w.querySelector("code");if(!c)return;var t=c.textContent;var b=e.target;if(navigator.clipboard){navigator.clipboard.writeText(t).then(function(){b.textContent="\\u2713";setTimeout(function(){b.textContent="\\u29C9"},1500)})}else{var a=document.createElement("textarea");a.value=t;a.style.position="fixed";a.style.opacity="0";document.body.appendChild(a);a.select();document.execCommand("copy");document.body.removeChild(a);b.textContent="\\u2713";setTimeout(function(){b.textContent="\\u29C9"},1500)}});`;
2554
+
2479
2555
  const MERMAID_CDN = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js";
2480
2556
  const KATEX_CDN_CSS = "https://cdn.jsdelivr.net/npm/katex@0.16/dist/katex.min.css";
2481
2557
  const HLJS_CDN = "https://cdn.jsdelivr.net/npm/@highlightjs/cdn-assets@11.11.1/highlight.min.js";
@@ -2524,7 +2600,7 @@ function hasMermaidBlocks(nodes) {
2524
2600
 
2525
2601
  function hasHighlightableCodeBlocks(nodes) {
2526
2602
  for (const node of nodes) {
2527
- if (node.type === "code" && node.lang && node.lang !== "mermaid" && node.lang !== "math") return true;
2603
+ if (node.type === "code" && node.lang && node.lang !== "mermaid" && node.lang !== "math" && node.lang !== "svg") return true;
2528
2604
  if (node.children && hasHighlightableCodeBlocks(node.children)) return true;
2529
2605
  if (node.items) {
2530
2606
  for (const item of node.items) {
@@ -2574,7 +2650,8 @@ function renderHtmlDocumentFromParsed(parsed, title, options = {}) {
2574
2650
 
2575
2651
  const cssBase = options.cssOverride ?? DEFAULT_STYLE;
2576
2652
  const cssAppend = options.cssAppend ? `\n${options.cssAppend}\n${PRINT_STYLE}` : `\n${PRINT_STYLE}`;
2577
- const scriptTag = options.script ? `\n<script>${options.script}</script>` : "";
2653
+ const builtinScript = COLLAPSE_SCRIPT + COPY_SCRIPT;
2654
+ const scriptTag = options.script ? `\n<script>${options.script}</script>` : `\n<script>${builtinScript}</script>`;
2578
2655
  const mermaidTheme = options.mermaidTheme ?? "neutral";
2579
2656
  const mermaidInit = mermaidTheme === "auto"
2580
2657
  ? `var isDark=window.matchMedia("(prefers-color-scheme:dark)").matches;mermaid.initialize({startOnLoad:true,theme:isDark?"dark":"neutral",themeCSS:".node rect, .node polygon, .node circle { rx: 4; ry: 4; }"});`
@@ -2791,8 +2868,9 @@ function getAllTaggedScopes(nodes) {
2791
2868
  const result = [];
2792
2869
  function walk(nodeList) {
2793
2870
  for (const node of nodeList) {
2794
- if (node.type === "scope") {
2795
- if (node.id && node.id.toLowerCase() !== "meta" && node.id.toLowerCase() !== "about") {
2871
+ if (node.type === "scope" && node.hasHeading) {
2872
+ const id = (node.id || "").toLowerCase();
2873
+ if (id !== "meta" && id !== "about") {
2796
2874
  result.push(node);
2797
2875
  }
2798
2876
  if (node.children) walk(node.children);
@@ -3071,5 +3149,6 @@ module.exports = {
3071
3149
  parseInline,
3072
3150
  renderKatex,
3073
3151
  escapeHtml,
3074
- escapeAttr
3152
+ escapeAttr,
3153
+ sanitizeSvg
3075
3154
  };