pict-section-inlinedocumentation 1.0.1 → 1.0.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.
Files changed (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -13
  3. package/diagrams/four-levels-of-embeddedness.excalidraw +717 -0
  4. package/diagrams/four-levels-of-embeddedness.mmd +4 -0
  5. package/diagrams/four-levels-of-embeddedness.svg +2 -0
  6. package/docs/README.md +10 -19
  7. package/docs/_cover.md +6 -0
  8. package/docs/_sidebar.md +8 -4
  9. package/docs/_version.json +3 -3
  10. package/docs/architecture.md +2 -44
  11. package/docs/diagrams/component-diagram.excalidraw +3546 -0
  12. package/docs/diagrams/component-diagram.mmd +42 -0
  13. package/docs/diagrams/component-diagram.svg +2 -0
  14. package/docs/diagrams/four-levels-of-embeddedness.excalidraw +717 -0
  15. package/docs/diagrams/four-levels-of-embeddedness.mmd +9 -0
  16. package/docs/diagrams/four-levels-of-embeddedness.svg +2 -0
  17. package/docs/diagrams/project-layout.excalidraw +6082 -0
  18. package/docs/diagrams/project-layout.mmd +25 -0
  19. package/docs/diagrams/project-layout.svg +2 -0
  20. package/docs/embedding-level2-routes.md +1 -1
  21. package/docs/embedding-level4-autogen.md +1 -1
  22. package/docs/examples/README.md +9 -0
  23. package/docs/examples/bookshop/README.md +582 -0
  24. package/docs/examples/bookshop/bookshop_example.js +4519 -0
  25. package/docs/examples/bookshop/index.html +236 -0
  26. package/docs/index.html +6 -5
  27. package/docs/overview.md +4 -4
  28. package/docs/reference.md +2 -27
  29. package/docs/retold-catalog.json +77 -178
  30. package/docs/retold-keyword-index.json +14134 -1917
  31. package/example_applications/bookshop/package.json +9 -1
  32. package/package.json +54 -54
  33. package/source/providers/Pict-Provider-InlineDocumentation.js +27 -3
  34. package/source/views/Pict-View-InlineDocumentation-Layout.js +135 -3
  35. package/source/views/Pict-View-InlineDocumentation-Nav.js +101 -10
@@ -30,5 +30,13 @@
30
30
  "from": "./data/pict_documentation_topics.json",
31
31
  "to": "./dist/docs/"
32
32
  }
33
- ]
33
+ ],
34
+ "retold": {
35
+ "ExampleApplication": {
36
+ "Stage": true,
37
+ "Title": "Bookshop",
38
+ "Summary": "E-commerce demo with route-mapped help, data-attribute tooltips, and F1 toggling for pict-section-inlinedocumentation.",
39
+ "Complexity": "Intermediate"
40
+ }
41
+ }
34
42
  }
package/package.json CHANGED
@@ -1,56 +1,56 @@
1
1
  {
2
- "name": "pict-section-inlinedocumentation",
3
- "version": "1.0.1",
4
- "description": "Pict embeddable inline documentation browser with topic support",
5
- "main": "source/Pict-Section-InlineDocumentation.js",
6
- "scripts": {
7
- "start": "node source/Pict-Section-InlineDocumentation.js",
8
- "test": "npx quack test",
9
- "tests": "npx quack test -g",
10
- "coverage": "npx quack coverage",
11
- "build": "npx quack build",
12
- "test-browser": "npx mocha test/Browser_Integration_tests.js --timeout 60000"
13
- },
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/stevenvelozo/pict-section-inlinedocumentation.git"
17
- },
18
- "author": "steven velozo <steven@velozo.com>",
19
- "license": "MIT",
20
- "bugs": {
21
- "url": "https://github.com/stevenvelozo/pict-section-inlinedocumentation/issues"
22
- },
23
- "homepage": "https://github.com/stevenvelozo/pict-section-inlinedocumentation#readme",
24
- "dependencies": {
25
- "lunr": "^2.3.9",
26
- "pict-provider": "^1.0.13",
27
- "pict-section-content": "^1.0.0",
28
- "pict-section-markdowneditor": "^1.0.15",
29
- "pict-section-modal": "^1.0.1",
30
- "pict-view": "^1.0.68"
31
- },
32
- "devDependencies": {
33
- "pict": "^1.0.368",
34
- "pict-docuserve": "^1.0.0",
35
- "puppeteer": "^24.40.0",
36
- "quackage": "^1.2.3"
37
- },
38
- "mocha": {
39
- "diff": true,
40
- "extension": [
41
- "js"
42
- ],
43
- "package": "./package.json",
44
- "reporter": "spec",
45
- "slow": "75",
46
- "timeout": "5000",
47
- "ui": "tdd",
48
- "watch-files": [
49
- "source/**/*.js",
50
- "test/**/*.js"
51
- ],
52
- "watch-ignore": [
53
- "lib/vendor"
54
- ]
55
- }
2
+ "name": "pict-section-inlinedocumentation",
3
+ "version": "1.0.3",
4
+ "description": "Pict embeddable inline documentation browser with topic support",
5
+ "main": "source/Pict-Section-InlineDocumentation.js",
6
+ "scripts": {
7
+ "start": "node source/Pict-Section-InlineDocumentation.js",
8
+ "test": "npx quack test",
9
+ "tests": "npx quack test -g",
10
+ "coverage": "npx quack coverage",
11
+ "build": "npx quack build",
12
+ "test-browser": "npx mocha test/Browser_Integration_tests.js --timeout 60000"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/fable-retold/pict-section-inlinedocumentation.git"
17
+ },
18
+ "author": "steven velozo <steven@velozo.com>",
19
+ "license": "MIT",
20
+ "bugs": {
21
+ "url": "https://github.com/fable-retold/pict-section-inlinedocumentation/issues"
22
+ },
23
+ "homepage": "https://github.com/fable-retold/pict-section-inlinedocumentation#readme",
24
+ "dependencies": {
25
+ "lunr": "^2.3.9",
26
+ "pict-provider": "^1.0.13",
27
+ "pict-section-content": "^1.0.7",
28
+ "pict-section-markdowneditor": "^1.0.15",
29
+ "pict-section-modal": "^1.1.4",
30
+ "pict-view": "^1.0.68"
31
+ },
32
+ "devDependencies": {
33
+ "pict": "^1.0.372",
34
+ "pict-docuserve": "^1.4.4",
35
+ "puppeteer": "^24.40.0",
36
+ "quackage": "^1.3.0"
37
+ },
38
+ "mocha": {
39
+ "diff": true,
40
+ "extension": [
41
+ "js"
42
+ ],
43
+ "package": "./package.json",
44
+ "reporter": "spec",
45
+ "slow": "75",
46
+ "timeout": "5000",
47
+ "ui": "tdd",
48
+ "watch-files": [
49
+ "source/**/*.js",
50
+ "test/**/*.js"
51
+ ],
52
+ "watch-ignore": [
53
+ "lib/vendor"
54
+ ]
55
+ }
56
56
  }
@@ -2213,24 +2213,48 @@ class InlineDocumentationProvider extends libPictProvider
2213
2213
  }
2214
2214
  else if (tmpCurrentGroup)
2215
2215
  {
2216
- // Indented item — document within the current group
2216
+ // Indented item — a document (link) or a sub-folder (no link)
2217
+ // within the current group. Capture the nesting Level from the
2218
+ // indentation so the nav can render real hierarchy.
2219
+ let tmpLevel = Math.max(0, Math.floor((tmpIndent - 2) / 2));
2217
2220
  if (tmpLinkMatch)
2218
2221
  {
2219
2222
  tmpCurrentGroup.Items.push({
2220
2223
  Name: tmpLinkMatch[1].trim(),
2221
- Path: this._normalizePath(tmpLinkMatch[2].trim())
2224
+ Path: this._normalizePath(tmpLinkMatch[2].trim()),
2225
+ Level: tmpLevel
2222
2226
  });
2223
2227
  }
2224
2228
  else
2225
2229
  {
2226
2230
  tmpCurrentGroup.Items.push({
2227
2231
  Name: tmpItemContent,
2228
- Path: ''
2232
+ Path: '',
2233
+ Level: tmpLevel,
2234
+ IsFolder: true
2229
2235
  });
2230
2236
  }
2231
2237
  }
2232
2238
  }
2233
2239
 
2240
+ // Make sub-folder nodes actionable: resolve each folder's first
2241
+ // descendant document (the items are in depth-first order, so scan
2242
+ // forward until we leave the folder's subtree) so clicking the folder
2243
+ // opens its first child.
2244
+ for (let g = 0; g < tmpGroups.length; g++)
2245
+ {
2246
+ let tmpItems = tmpGroups[g].Items || [];
2247
+ for (let a = 0; a < tmpItems.length; a++)
2248
+ {
2249
+ if (!tmpItems[a].IsFolder) { continue; }
2250
+ for (let b = a + 1; b < tmpItems.length; b++)
2251
+ {
2252
+ if ((tmpItems[b].Level || 0) <= (tmpItems[a].Level || 0)) { break; }
2253
+ if (tmpItems[b].Path) { tmpItems[a].FirstChildPath = tmpItems[b].Path; break; }
2254
+ }
2255
+ }
2256
+ }
2257
+
2234
2258
  return tmpGroups;
2235
2259
  }
2236
2260
 
@@ -25,14 +25,27 @@ const _ViewConfiguration =
25
25
  overflow: hidden;
26
26
  }
27
27
  .pict-inline-doc-nav-container {
28
- width: 240px;
29
- min-width: 200px;
30
- max-width: 300px;
28
+ width: var(--pict-inline-doc-nav-width, 240px);
29
+ min-width: 160px;
30
+ max-width: 640px;
31
31
  border-right: 1px solid var(--theme-color-border-default, #E5DED4);
32
32
  background: var(--theme-color-background-secondary, #F7F5F0);
33
33
  overflow-y: auto;
34
34
  flex-shrink: 0;
35
35
  }
36
+ .pict-inline-doc-resizer {
37
+ flex: 0 0 6px;
38
+ width: 6px;
39
+ margin: 0 -3px;
40
+ z-index: 2;
41
+ cursor: col-resize;
42
+ background: transparent;
43
+ transition: background 0.12s ease;
44
+ }
45
+ .pict-inline-doc-resizer:hover,
46
+ .pict-inline-doc-resizer.pict-inline-doc-resizing {
47
+ background: var(--theme-color-brand-primary, #2E7D74);
48
+ }
36
49
  .pict-inline-doc-content-container {
37
50
  flex: 1;
38
51
  overflow-y: auto;
@@ -42,6 +55,26 @@ const _ViewConfiguration =
42
55
  .pict-inline-doc-nav-container.pict-inline-doc-nav-hidden {
43
56
  display: none;
44
57
  }
58
+ /* Whole-panel collapse: shrink the nav to a thin strip showing only the
59
+ expand chevron, handing the freed width back to the content. The Nav
60
+ view toggles .pict-inline-doc-nav-collapsed on the container. width is
61
+ !important so it beats any inline width left by the drag-resizer. */
62
+ .pict-inline-doc-nav-container.pict-inline-doc-nav-collapsed {
63
+ width: 40px !important;
64
+ min-width: 40px;
65
+ }
66
+ .pict-inline-doc-nav-container.pict-inline-doc-nav-collapsed + .pict-inline-doc-resizer {
67
+ display: none;
68
+ }
69
+ .pict-inline-doc-nav-collapsed .pict-inline-doc-nav-collapsed-header {
70
+ flex-direction: column;
71
+ padding: 0.6em 0;
72
+ justify-content: flex-start;
73
+ }
74
+ .pict-inline-doc-nav-collapsed .pict-inline-doc-nav-current-title,
75
+ .pict-inline-doc-nav-collapsed .pict-inline-doc-nav-search-icon {
76
+ display: none;
77
+ }
45
78
  /* Compact mode: stack nav above content when container is narrow */
46
79
  .pict-inline-doc.pict-inline-doc-compact {
47
80
  flex-direction: column;
@@ -55,6 +88,9 @@ const _ViewConfiguration =
55
88
  overflow-y: visible;
56
89
  flex-shrink: 0;
57
90
  }
91
+ .pict-inline-doc.pict-inline-doc-compact .pict-inline-doc-resizer {
92
+ display: none;
93
+ }
58
94
  `,
59
95
 
60
96
  Templates:
@@ -64,6 +100,7 @@ const _ViewConfiguration =
64
100
  Template: /*html*/`
65
101
  <div class="pict-inline-doc">
66
102
  <div class="pict-inline-doc-nav-container" id="InlineDoc-Nav-Container"></div>
103
+ <div class="pict-inline-doc-resizer" id="InlineDoc-Resizer" title="Drag to resize the navigation"></div>
67
104
  <div class="pict-inline-doc-content-container" id="InlineDoc-Content-Container"></div>
68
105
  </div>
69
106
  `
@@ -93,12 +130,107 @@ class InlineDocumentationLayoutView extends libPictView
93
130
  // Inject all view CSS into the PICT-CSS style element
94
131
  this.pict.CSSMap.injectCSS();
95
132
 
133
+ // Restore any saved nav width, then make the nav/content divider draggable
134
+ this._restoreNavWidth();
135
+ this._wireNavResizer();
136
+
96
137
  // Watch for size changes and toggle compact mode
97
138
  this._setupCompactModeObserver();
98
139
 
99
140
  return super.onAfterRender();
100
141
  }
101
142
 
143
+ /**
144
+ * Make the divider between the nav and the content draggable so the reader
145
+ * can widen or narrow the navigation. The width lives in a CSS custom
146
+ * property on the layout container and is persisted to localStorage.
147
+ */
148
+ _wireNavResizer()
149
+ {
150
+ if (typeof document === 'undefined')
151
+ {
152
+ return;
153
+ }
154
+
155
+ let tmpResizer = document.getElementById('InlineDoc-Resizer');
156
+ let tmpContainer = document.querySelector('.pict-inline-doc');
157
+ let tmpNav = document.getElementById('InlineDoc-Nav-Container');
158
+ if (!tmpResizer || !tmpContainer || !tmpNav)
159
+ {
160
+ return;
161
+ }
162
+
163
+ let tmpMin = 160;
164
+ let tmpMax = 640;
165
+ let tmpSelf = this;
166
+
167
+ tmpResizer.addEventListener('mousedown', (pDownEvent) =>
168
+ {
169
+ pDownEvent.preventDefault();
170
+ let tmpStartX = pDownEvent.clientX;
171
+ let tmpStartWidth = tmpNav.offsetWidth;
172
+
173
+ tmpResizer.classList.add('pict-inline-doc-resizing');
174
+ document.body.style.userSelect = 'none';
175
+ document.body.style.cursor = 'col-resize';
176
+
177
+ // A drag needs document-level move/up tracking; both listeners are
178
+ // removed on release, so nothing leaks across renders.
179
+ let fMove = (pMoveEvent) =>
180
+ {
181
+ let tmpWidth = tmpStartWidth + (pMoveEvent.clientX - tmpStartX);
182
+ tmpWidth = Math.max(tmpMin, Math.min(tmpMax, tmpWidth));
183
+ tmpContainer.style.setProperty('--pict-inline-doc-nav-width', tmpWidth + 'px');
184
+ };
185
+ let fUp = () =>
186
+ {
187
+ document.removeEventListener('mousemove', fMove);
188
+ document.removeEventListener('mouseup', fUp);
189
+ tmpResizer.classList.remove('pict-inline-doc-resizing');
190
+ document.body.style.userSelect = '';
191
+ document.body.style.cursor = '';
192
+ tmpSelf._persistNavWidth(tmpContainer.style.getPropertyValue('--pict-inline-doc-nav-width'));
193
+ };
194
+
195
+ document.addEventListener('mousemove', fMove);
196
+ document.addEventListener('mouseup', fUp);
197
+ });
198
+ }
199
+
200
+ _persistNavWidth(pWidth)
201
+ {
202
+ try
203
+ {
204
+ if (pWidth && typeof localStorage !== 'undefined')
205
+ {
206
+ localStorage.setItem('pict-inline-doc-nav-width', pWidth);
207
+ }
208
+ }
209
+ catch (pError) { /* storage unavailable — the width just won't persist */ }
210
+ }
211
+
212
+ _restoreNavWidth()
213
+ {
214
+ try
215
+ {
216
+ if (typeof localStorage === 'undefined')
217
+ {
218
+ return;
219
+ }
220
+ let tmpSaved = localStorage.getItem('pict-inline-doc-nav-width');
221
+ if (!tmpSaved)
222
+ {
223
+ return;
224
+ }
225
+ let tmpContainer = document.querySelector('.pict-inline-doc');
226
+ if (tmpContainer)
227
+ {
228
+ tmpContainer.style.setProperty('--pict-inline-doc-nav-width', tmpSaved);
229
+ }
230
+ }
231
+ catch (pError) { /* ignore */ }
232
+ }
233
+
102
234
  /**
103
235
  * Set up a ResizeObserver to toggle compact mode when the container
104
236
  * is too narrow for a side-by-side nav + content layout.
@@ -112,7 +112,7 @@ const _ViewConfiguration =
112
112
  }
113
113
  .pict-inline-doc-nav-item {
114
114
  display: block;
115
- padding: 0.25em 0.8em 0.25em 1.6em;
115
+ padding: 0.25em 0.8em 0.25em calc(1.6em + var(--doc-nav-level, 0) * 0.85em);
116
116
  color: var(--theme-color-text-secondary, #5E5549);
117
117
  text-decoration: none;
118
118
  font-size: 0.85em;
@@ -123,6 +123,25 @@ const _ViewConfiguration =
123
123
  .pict-inline-doc-nav-item:hover {
124
124
  background: var(--theme-color-background-tertiary, #EDE8DF);
125
125
  }
126
+ .pict-inline-doc-nav-folder {
127
+ font-weight: 600;
128
+ color: var(--theme-color-text-primary, #3D3229);
129
+ }
130
+ .pict-inline-doc-nav-folder-icon {
131
+ margin-right: 0.4em;
132
+ vertical-align: -0.12em;
133
+ opacity: 0.65;
134
+ }
135
+ .pict-inline-doc-nav-file-icon {
136
+ margin-right: 0.4em;
137
+ vertical-align: -0.12em;
138
+ opacity: 0.55;
139
+ }
140
+ .pict-inline-doc-nav-section-dot {
141
+ margin-right: 0.4em;
142
+ vertical-align: -0.12em;
143
+ opacity: 0.3;
144
+ }
126
145
  .pict-inline-doc-nav-item.active {
127
146
  background: var(--theme-color-background-hover, #E8E3D8);
128
147
  color: var(--theme-color-brand-primary, #2E7D74);
@@ -131,7 +150,7 @@ const _ViewConfiguration =
131
150
  }
132
151
  .pict-inline-doc-nav-heading {
133
152
  display: block;
134
- padding: 0.15em 0.8em 0.15em 2.4em;
153
+ padding: 0.15em 0.8em 0.15em calc(2.4em + var(--doc-nav-level, 0) * 0.85em);
135
154
  color: var(--theme-color-text-muted, #8A7F72);
136
155
  font-size: 0.78em;
137
156
  cursor: pointer;
@@ -143,7 +162,7 @@ const _ViewConfiguration =
143
162
  color: var(--theme-color-text-secondary, #5E5549);
144
163
  }
145
164
  .pict-inline-doc-nav-heading.h3 {
146
- padding-left: 3.2em;
165
+ padding-left: calc(3.2em + var(--doc-nav-level, 0) * 0.85em);
147
166
  font-size: 0.72em;
148
167
  }
149
168
  /* Search icon in collapsed header */
@@ -339,6 +358,16 @@ class InlineDocumentationNavView extends libPictView
339
358
  let tmpFilterText = tmpState.NavFilterText || '';
340
359
  let tmpCurrentDocName = this._resolveCurrentDocName(tmpState, tmpCurrentPath);
341
360
 
361
+ // Reflect the collapsed state on the nav CONTAINER (the layout owns its
362
+ // width). Collapsing then shrinks the whole panel to a thin strip and hands
363
+ // the freed width back to the content, instead of hiding the outline while
364
+ // the container stays full-width.
365
+ let tmpNavContainer = document.getElementById('InlineDoc-Nav-Container');
366
+ if (tmpNavContainer)
367
+ {
368
+ tmpNavContainer.classList.toggle('pict-inline-doc-nav-collapsed', tmpIsCollapsed);
369
+ }
370
+
342
371
  let tmpHTML = '';
343
372
 
344
373
  let tmpSearchQuery = tmpState.SearchQuery || '';
@@ -624,22 +653,44 @@ class InlineDocumentationNavView extends libPictView
624
653
  let tmpActive = (pCurrentPath === tmpGroup.Path) ? ' active' : '';
625
654
  tmpHTML += '<a class="pict-inline-doc-nav-item' + tmpActive
626
655
  + '" data-doc-path="' + this._escapeHTML(tmpGroup.Path) + '">'
656
+ + this._fileIconSVG()
627
657
  + this._escapeHTML(tmpGroup.Name)
628
658
  + '</a>';
629
659
 
630
660
  // If this is the active item, render heading sub-items
631
661
  if (pCurrentPath === tmpGroup.Path)
632
662
  {
633
- tmpHTML += this._renderHeadingSubItems(pHeadings, tmpFilterLower);
663
+ tmpHTML += this._renderHeadingSubItems(pHeadings, tmpFilterLower, 0);
634
664
  }
635
665
  }
636
666
 
637
667
  for (let j = 0; j < tmpGroupItems.length; j++)
638
668
  {
639
669
  let tmpItem = tmpGroupItems[j];
670
+ let tmpIndent = ' style="--doc-nav-level:' + (tmpItem.Level || 0) + '"';
671
+
672
+ // Sub-folder node (no document of its own). Indent it by its
673
+ // level and, when it has descendants, make it clickable so it
674
+ // opens its first child document; mark it with a folder glyph.
640
675
  if (!tmpItem.Path)
641
676
  {
642
- tmpHTML += '<span class="pict-inline-doc-nav-item">' + this._escapeHTML(tmpItem.Name) + '</span>';
677
+ if (tmpItem.FirstChildPath)
678
+ {
679
+ // Folders are navigational shortcuts to their first child; the
680
+ // child item carries the active highlight, so the folder doesn't.
681
+ tmpHTML += '<a class="pict-inline-doc-nav-item pict-inline-doc-nav-folder'
682
+ + '" data-doc-path="' + this._escapeHTML(tmpItem.FirstChildPath) + '"' + tmpIndent + '>'
683
+ + this._folderIconSVG()
684
+ + this._escapeHTML(tmpItem.Name)
685
+ + '</a>';
686
+ }
687
+ else
688
+ {
689
+ tmpHTML += '<span class="pict-inline-doc-nav-item pict-inline-doc-nav-folder"' + tmpIndent + '>'
690
+ + this._folderIconSVG()
691
+ + this._escapeHTML(tmpItem.Name)
692
+ + '</span>';
693
+ }
643
694
  continue;
644
695
  }
645
696
 
@@ -647,7 +698,8 @@ class InlineDocumentationNavView extends libPictView
647
698
  {
648
699
  // External link — opens in a new tab
649
700
  tmpHTML += '<a class="pict-inline-doc-nav-item pict-inline-doc-nav-item-external'
650
- + '" data-external-url="' + this._escapeHTML(tmpItem.ExternalURL) + '">'
701
+ + '" data-external-url="' + this._escapeHTML(tmpItem.ExternalURL) + '"' + tmpIndent + '>'
702
+ + this._fileIconSVG()
651
703
  + this._escapeHTML(tmpItem.Name)
652
704
  + '<svg class="pict-inline-doc-nav-external-icon" width="0.75em" height="0.75em" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 9v4a1 1 0 01-1 1H3a1 1 0 01-1-1V5a1 1 0 011-1h4"/><polyline points="8,2 14,2 14,8"/><line x1="14" y1="2" x2="7" y2="9"/></svg>'
653
705
  + '</a>';
@@ -656,14 +708,15 @@ class InlineDocumentationNavView extends libPictView
656
708
  {
657
709
  let tmpActive = (pCurrentPath === tmpItem.Path) ? ' active' : '';
658
710
  tmpHTML += '<a class="pict-inline-doc-nav-item' + tmpActive
659
- + '" data-doc-path="' + this._escapeHTML(tmpItem.Path) + '">'
711
+ + '" data-doc-path="' + this._escapeHTML(tmpItem.Path) + '"' + tmpIndent + '>'
712
+ + this._fileIconSVG()
660
713
  + this._escapeHTML(tmpItem.Name)
661
714
  + '</a>';
662
715
 
663
716
  // If this is the active item, render heading sub-items
664
717
  if (pCurrentPath === tmpItem.Path)
665
718
  {
666
- tmpHTML += this._renderHeadingSubItems(pHeadings, tmpFilterLower);
719
+ tmpHTML += this._renderHeadingSubItems(pHeadings, tmpFilterLower, tmpItem.Level || 0);
667
720
  }
668
721
  }
669
722
  }
@@ -675,6 +728,40 @@ class InlineDocumentationNavView extends libPictView
675
728
  return tmpHTML;
676
729
  }
677
730
 
731
+ /**
732
+ * A small folder glyph for sub-folder nav nodes. Uses currentColor so it
733
+ * follows the theme like the other inline nav icons.
734
+ *
735
+ * @returns {string} inline SVG HTML
736
+ */
737
+ _folderIconSVG()
738
+ {
739
+ return '<svg class="pict-inline-doc-nav-folder-icon" width="0.85em" height="0.85em" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M1.5 5a1 1 0 0 1 1-1h3.2l1.3 1.4h5.5a1 1 0 0 1 1 1V12a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1z"/></svg>';
740
+ }
741
+
742
+ /**
743
+ * A small document glyph (a page with a folded corner) for markdown file
744
+ * nodes. Same footprint as the folder glyph so files and folders line up at
745
+ * the same depth.
746
+ *
747
+ * @returns {string} inline SVG HTML
748
+ */
749
+ _fileIconSVG()
750
+ {
751
+ return '<svg class="pict-inline-doc-nav-file-icon" width="0.85em" height="0.85em" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 2.5H9L12 5.5V13.5H4Z"/><polyline points="9 2.5 9 5.5 12 5.5"/></svg>';
752
+ }
753
+
754
+ /**
755
+ * A small filled dot marking an in-document section (a heading sub-item shown
756
+ * beneath the active document). currentColor + low opacity keeps it quiet.
757
+ *
758
+ * @returns {string} inline SVG HTML
759
+ */
760
+ _sectionDotSVG()
761
+ {
762
+ return '<svg class="pict-inline-doc-nav-section-dot" width="0.85em" height="0.85em" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="2.3" fill="currentColor"/></svg>';
763
+ }
764
+
678
765
  /**
679
766
  * Render heading sub-items (h2 and h3) beneath the active nav item.
680
767
  *
@@ -682,13 +769,16 @@ class InlineDocumentationNavView extends libPictView
682
769
  * @param {string} pFilterText - Lowercase filter text
683
770
  * @returns {string} HTML string for heading sub-items
684
771
  */
685
- _renderHeadingSubItems(pHeadings, pFilterText)
772
+ _renderHeadingSubItems(pHeadings, pFilterText, pBaseLevel)
686
773
  {
687
774
  if (!pHeadings || pHeadings.length < 1)
688
775
  {
689
776
  return '';
690
777
  }
691
778
 
779
+ // Indent the headings relative to their (possibly nested) parent
780
+ // document item, so they line up one notch under it at any tree depth.
781
+ let tmpLevelStyle = ' style="--doc-nav-level:' + (pBaseLevel || 0) + '"';
692
782
  let tmpHTML = '';
693
783
 
694
784
  for (let i = 0; i < pHeadings.length; i++)
@@ -704,7 +794,8 @@ class InlineDocumentationNavView extends libPictView
704
794
 
705
795
  let tmpLevelClass = (tmpHeading.Level === 3) ? ' h3' : '';
706
796
  tmpHTML += '<a class="pict-inline-doc-nav-heading' + tmpLevelClass
707
- + '" data-heading-slug="' + this._escapeHTML(tmpHeading.Slug) + '">'
797
+ + '" data-heading-slug="' + this._escapeHTML(tmpHeading.Slug) + '"' + tmpLevelStyle + '>'
798
+ + this._sectionDotSVG()
708
799
  + this._escapeHTML(tmpText)
709
800
  + '</a>';
710
801
  }