hdoc-tools 0.62.4 → 0.63.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.
- package/editor/dist/assets/{index-BtxvGZHW.js → index-Dknn5g5A.js} +5 -4
- package/editor/dist/index.html +13 -13
- package/hdoc-build.js +5 -3
- package/hdoc-content-routes.js +136 -4
- package/hdoc-serve.js +26 -3
- package/hdoc-validate-interbook.js +8 -0
- package/hdoc-validate.js +92 -35
- package/package.json +1 -1
- package/ui/css/theme-default/styles/base.css +86 -21
- package/ui/css/theme-default/styles/components/api-doc.css +6 -3
- package/ui/css/theme-default/styles/components/content.css +140 -39
- package/ui/css/theme-default/styles/components/custom-block.css +1 -1
- package/ui/css/theme-default/styles/components/htl-doc.css +243 -64
- package/ui/css/theme-default/styles/components/htl-library.css +152 -0
- package/ui/css/theme-default/styles/components/htl-search.css +267 -0
- package/ui/css/theme-default/styles/components/sidebar.css +59 -16
- package/ui/css/theme-default/styles/fonts.css +17 -3
- package/ui/css/theme-default/styles/htldoc.layouts.css +756 -87
- package/ui/css/theme-default/styles/vars.css +40 -35
- package/ui/favicon.svg +64 -0
- package/ui/images/hornbill-logo-full-reversed.svg +92 -0
- package/ui/images/hornbill-logo-full.svg +92 -0
- package/ui/images/hug_library.jpg +0 -0
- package/ui/images/mcp-catalog.svg +9 -0
- package/ui/images/products/hornbill-square.svg +1 -0
- package/ui/index.html +1106 -342
- package/ui/js/bootstrap.js +89 -0
- package/ui/js/doc.hornbill.js +3156 -751
- package/ui/js/hb.vue.js +121 -0
- package/ui/js/highlightjs/highlight.pack.js +1513 -2
- package/ui/js/highlightjs/styles/vs2015-accessible.css +141 -0
- package/ui/js/highlightjs-badge.js +41 -58
- package/ui/js/mermaid.min.js +1447 -1295
- package/ui/js/webcomponents/hdocApprove.js +115 -0
- package/ui/js/highlightjs/styles/brown-paper.css +0 -64
- package/ui/js/highlightjs/styles/brown-papersq.png +0 -0
- package/ui/js/highlightjs/styles/codepen-embed.css +0 -60
- package/ui/js/highlightjs/styles/color-brewer.css +0 -71
- package/ui/js/highlightjs/styles/darcula.css +0 -77
- package/ui/js/highlightjs/styles/dark.css +0 -63
- package/ui/js/highlightjs/styles/darkula.css +0 -6
- package/ui/js/highlightjs/styles/default.css +0 -99
- package/ui/js/highlightjs/styles/dracula.css +0 -76
- package/ui/js/highlightjs/styles/far.css +0 -71
- package/ui/js/highlightjs/styles/foundation.css +0 -88
- package/ui/js/highlightjs/styles/github-gist.css +0 -71
- package/ui/js/highlightjs/styles/github-mm.css +0 -71
- package/ui/js/highlightjs/styles/github.css +0 -99
- package/ui/js/highlightjs/styles/googlecode.css +0 -89
- package/ui/js/highlightjs/styles/grayscale.css +0 -101
- package/ui/js/highlightjs/styles/idea.css +0 -97
- package/ui/js/highlightjs/styles/ir-black.css +0 -73
- package/ui/js/highlightjs/styles/kavadocs.css +0 -71
- package/ui/js/highlightjs/styles/kavadocsdark.css +0 -120
- package/ui/js/highlightjs/styles/kimbie.dark.css +0 -74
- package/ui/js/highlightjs/styles/kimbie.light.css +0 -74
- package/ui/js/highlightjs/styles/magula.css +0 -70
- package/ui/js/highlightjs/styles/mono-blue.css +0 -59
- package/ui/js/highlightjs/styles/monokai-sublime.css +0 -83
- package/ui/js/highlightjs/styles/monokai.css +0 -70
- package/ui/js/highlightjs/styles/obsidian.css +0 -88
- package/ui/js/highlightjs/styles/paraiso-dark.css +0 -72
- package/ui/js/highlightjs/styles/paraiso-light.css +0 -72
- package/ui/js/highlightjs/styles/railscasts.css +0 -106
- package/ui/js/highlightjs/styles/rainbow.css +0 -85
- package/ui/js/highlightjs/styles/solarized-dark.css +0 -84
- package/ui/js/highlightjs/styles/solarized-light.css +0 -84
- package/ui/js/highlightjs/styles/sunburst.css +0 -102
- package/ui/js/highlightjs/styles/twilight.css +0 -97
- package/ui/js/highlightjs/styles/vs.css +0 -68
- package/ui/js/highlightjs/styles/vs2015.css +0 -117
- package/ui/js/highlightjs/styles/xcode.css +0 -104
- package/ui/js/highlightjs/styles/zenburn.css +0 -80
package/ui/index.html
CHANGED
|
@@ -1,52 +1,130 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
1
2
|
<html lang="en-US">
|
|
2
|
-
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
3
|
<head>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<!-- Those two are FIRST, ahead of this comment and of everything else, because both are
|
|
7
|
+
position-sensitive and both were broken by sitting further down the file:
|
|
8
|
+
|
|
9
|
+
charset - the spec requires the encoding declaration inside the first 1024 bytes. The
|
|
10
|
+
inline script and stylesheet below had pushed it to byte 15013, so the browser was
|
|
11
|
+
guessing the encoding for the whole document and could in principle restart the parse.
|
|
12
|
+
Even a comment above it eats into that budget; that is why this text is here and not
|
|
13
|
+
above.
|
|
14
|
+
|
|
15
|
+
viewport - until this is parsed, a mobile browser lays the page out in its DEFAULT 980px
|
|
16
|
+
layout viewport. Anything painted before that point is laid out for 980px and then shown
|
|
17
|
+
zoomed into the real screen, so a position:fixed/inset:0 element centred in it appears
|
|
18
|
+
offset right and down by the zoom ratio, anchored top-left - and then jumps back to the
|
|
19
|
+
middle when the meta is finally applied. That was a real reported defect against the boot
|
|
20
|
+
splash on a throttled mobile connection, where the parse is slow enough for a paint to
|
|
21
|
+
land in that window.
|
|
22
|
+
|
|
23
|
+
Keep these two above everything. //-- SG -->
|
|
24
|
+
|
|
25
|
+
<base href="/"><!-- literal root base: the preload scanner resolves <link>/<script> hrefs
|
|
26
|
+
before the inline script below can rewrite it, so this must already be correct. The script
|
|
27
|
+
still runs - siteBaseLocation is read by js/doc.hornbill.js - it just is not load-bearing
|
|
28
|
+
for URL resolution any more. -->
|
|
29
|
+
<script>
|
|
30
|
+
var siteBaseLocation = window.location.protocol + '//' + window.location.host + "/";
|
|
31
|
+
var baseTag = document.getElementsByTagName('base')[0];
|
|
32
|
+
baseTag.href = siteBaseLocation;
|
|
33
|
+
|
|
34
|
+
//-- THEME, resolved here at PARSE time and nowhere else. This used to live at the top of
|
|
35
|
+
//-- js/bootstrap.js, which is injected on DOMContentLoaded and then has to be fetched -
|
|
36
|
+
//-- so the html.dark class landed a whole network round trip after the first paint and
|
|
37
|
+
//-- every dark-mode user got a WHITE page for the entire boot window. Must stay in this
|
|
38
|
+
//-- inline block, before any stylesheet or body content, or the flash comes back.
|
|
39
|
+
//-- bootstrap.js keeps a copy of this logic as a fallback, guarded on hbThemeResolved,
|
|
40
|
+
//-- for any page that loads it without this script. //-- SG
|
|
41
|
+
var ThemePreference = localStorage.getItem('hdocbook-theme-appearance') || '';
|
|
42
|
+
var hbUrlTheme = new URLSearchParams(window.location.search).get('theme');
|
|
43
|
+
if (hbUrlTheme === 'dark' || hbUrlTheme === 'light') ThemePreference = hbUrlTheme;
|
|
44
|
+
if (!ThemePreference || ThemePreference === 'auto'
|
|
45
|
+
? window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
46
|
+
: ThemePreference === 'dark')
|
|
47
|
+
{
|
|
48
|
+
ThemePreference = "dark";
|
|
49
|
+
document.documentElement.classList.add('dark');
|
|
50
|
+
}
|
|
51
|
+
window.hbThemeResolved = true;
|
|
52
|
+
|
|
53
|
+
//-- Start the boot-splash logo download NOW, keyed on the theme just resolved above.
|
|
54
|
+
//-- The splash logo is a class-driven CSS background (see .hbBootLogo) because <img src>
|
|
55
|
+
//-- cannot follow html.dark - but background images are only discovered once style
|
|
56
|
+
//-- resolves, which waits on every render-blocking stylesheet. Measured on Slow 3G: the
|
|
57
|
+
//-- last of 15 stylesheets finished at 13405ms and the logo was not even REQUESTED until
|
|
58
|
+
//-- 13608ms, so the splash painted with an empty logo box and the logo popped in after.
|
|
59
|
+
//-- This preload starts the fetch in parallel with the CSS instead, which is what the
|
|
60
|
+
//-- preload scanner used to do for the <img> - only theme-correct. //-- SG
|
|
61
|
+
var hbLogoPreload = document.createElement('link');
|
|
62
|
+
hbLogoPreload.rel = 'preload';
|
|
63
|
+
hbLogoPreload.as = 'image';
|
|
64
|
+
hbLogoPreload.href = document.documentElement.classList.contains('dark')
|
|
65
|
+
? 'images/hornbill-logo-full-reversed.svg'
|
|
66
|
+
: 'images/hornbill-logo-full.svg';
|
|
67
|
+
document.head.appendChild(hbLogoPreload);
|
|
68
|
+
|
|
69
|
+
//-- ===================== NON-BLOCKING CSS =====================
|
|
70
|
+
//-- Every stylesheet below is loaded with media="print" and flipped to media="all" on
|
|
71
|
+
//-- load, which takes it off the critical path: the browser no longer withholds the
|
|
72
|
+
//-- first paint until all 15 have arrived. That is what lets the boot splash - which is
|
|
73
|
+
//-- styled purely from the inline <style> in this file and needs no external CSS at all -
|
|
74
|
+
//-- appear while they are still in flight.
|
|
75
|
+
//--
|
|
76
|
+
//-- The catch, and the reason for the gate below: the APP must not paint unstyled. It is
|
|
77
|
+
//-- hidden by .hb-hidden until revealApp(), so the fix is to make revealApp() wait for
|
|
78
|
+
//-- the stylesheets rather than to make the browser wait. Nothing else in the boot cares.
|
|
79
|
+
//--
|
|
80
|
+
//-- Ready is declared on whichever comes first:
|
|
81
|
+
//-- - every marked <link> has fired load or error, or
|
|
82
|
+
//-- - window load (guarantees every subresource has resolved either way), or
|
|
83
|
+
//-- - a 30s absolute backstop, so a stylesheet that hangs forever cannot strand the
|
|
84
|
+
//-- splash on screen permanently.
|
|
85
|
+
//-- The count cannot simply be hardcoded: onload can fire from cache before the counting
|
|
86
|
+
//-- script at the end of <head> has run, so the check needs both halves and runs again
|
|
87
|
+
//-- when the total becomes known. //-- SG
|
|
88
|
+
window.hbCssApplied = 0;
|
|
89
|
+
window.hbCssTotal = 0;
|
|
90
|
+
window.hbCssTotalKnown = false;
|
|
91
|
+
window.hbCssReady = false;
|
|
92
|
+
window.hbCssWaiters = [];
|
|
93
|
+
|
|
94
|
+
function hbCssMarkReady()
|
|
95
|
+
{
|
|
96
|
+
if (window.hbCssReady) return;
|
|
97
|
+
window.hbCssReady = true;
|
|
98
|
+
var waiters = window.hbCssWaiters;
|
|
99
|
+
window.hbCssWaiters = [];
|
|
100
|
+
for (var i = 0; i < waiters.length; i++) waiters[i]();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function hbCssCheck()
|
|
104
|
+
{
|
|
105
|
+
if (window.hbCssTotalKnown && window.hbCssApplied >= window.hbCssTotal) hbCssMarkReady();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//-- called from the onload/onerror attribute on every marked <link>
|
|
109
|
+
function hbOnCssLoad(link)
|
|
110
|
+
{
|
|
111
|
+
if (link && link.media === 'print') link.media = 'all';
|
|
112
|
+
window.hbCssApplied++;
|
|
113
|
+
hbCssCheck();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
window.addEventListener('load', hbCssMarkReady);
|
|
117
|
+
setTimeout(hbCssMarkReady, 30000);
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
document.addEventListener("DOMContentLoaded", function (event)
|
|
121
|
+
{
|
|
122
|
+
var element = document.createElement("script");
|
|
123
|
+
element.src = "js/bootstrap.js";
|
|
124
|
+
document.body.appendChild(element);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
|
|
50
128
|
class ApiParamOptional extends HTMLElement {
|
|
51
129
|
connectedCallback() {
|
|
52
130
|
const text = (this.textContent || '').trim();
|
|
@@ -58,363 +136,1049 @@
|
|
|
58
136
|
}
|
|
59
137
|
customElements.define('api-param-optional', ApiParamOptional);
|
|
60
138
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<style>
|
|
142
|
+
|
|
143
|
+
/* ===================== BOOT STATE =====================
|
|
144
|
+
Everything in this block runs before any external stylesheet is guaranteed to have
|
|
145
|
+
arrived, so it uses LITERAL colours, not var(--htl-*) - a var() that resolves to
|
|
146
|
+
nothing paints black-on-black. The literals mirror vars.css: --htl-c-white /
|
|
147
|
+
--htl-c-black-soft for the grounds, --htl-c-indigo / --htl-c-text-dark-2 for text.
|
|
148
|
+
Keep them in step by hand if the palette moves.
|
|
149
|
+
|
|
150
|
+
Paint the page ground immediately. Without this the UA white shows through until
|
|
151
|
+
base.css lands, which is a white flash for dark-mode users even with the theme class
|
|
152
|
+
now resolved at parse time. */
|
|
153
|
+
html { background-color: #ffffff; }
|
|
154
|
+
html.dark { background-color: #242424; }
|
|
155
|
+
|
|
156
|
+
#hbBootSplash {
|
|
157
|
+
position: fixed;
|
|
158
|
+
inset: 0;
|
|
159
|
+
z-index: 20000;
|
|
160
|
+
display: flex;
|
|
161
|
+
flex-direction: column;
|
|
162
|
+
align-items: center;
|
|
163
|
+
justify-content: center;
|
|
164
|
+
gap: 28px;
|
|
165
|
+
background-color: #ffffff;
|
|
166
|
+
color: #213547;
|
|
167
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
168
|
+
/* Deliberately NOT visible on arrival. A splash that flashes for 80ms on a warm
|
|
169
|
+
cache looks worse than no splash at all, so it fades in only once the boot has
|
|
170
|
+
demonstrably taken longer than a fast load: opacity 0 held for 400ms, then a
|
|
171
|
+
200ms fade. Cached loads reveal the app inside that hold and the user never sees
|
|
172
|
+
this element. Doing the delay in CSS rather than a setTimeout keeps it working
|
|
173
|
+
even if the JS wave is what is slow.
|
|
174
|
+
|
|
175
|
+
400ms is measured, not guessed: a warm-cache local load reveals at ~285ms, and
|
|
176
|
+
at the original 250ms hold that got 35ms into the fade - a faint but real ghost
|
|
177
|
+
at opacity 0.134. Do not lower it without re-measuring; the cost of being too
|
|
178
|
+
early (a flash on every fast load) is worse than the cost of being too late
|
|
179
|
+
(400ms more blank on a slow one, which reads as normal latency). */
|
|
180
|
+
opacity: 0;
|
|
181
|
+
animation: hbBootSplashIn 200ms ease-out 400ms forwards;
|
|
182
|
+
}
|
|
183
|
+
html.dark #hbBootSplash { background-color: #242424; color: #DEDEDE; }
|
|
184
|
+
|
|
185
|
+
@keyframes hbBootSplashIn { to { opacity: 1; } }
|
|
186
|
+
|
|
187
|
+
/* Reserved box: width/height fixed to the logo's own 2053.3 x 283.5 aspect ratio
|
|
188
|
+
(7.24:1), so the row does not reflow when the image lands.
|
|
189
|
+
|
|
190
|
+
A class-driven background-image, NOT an <img>. This was an <img> inside a <picture>
|
|
191
|
+
keyed on prefers-color-scheme, which is wrong: the effective theme is the html.dark
|
|
192
|
+
CLASS (localStorage or ?theme= can override the OS setting), and <img src> cannot be
|
|
193
|
+
driven by a class. Anyone whose OS prefers dark but who has chosen the light theme
|
|
194
|
+
got the reversed logo - white wordmark on a white ground, i.e. an invisible logo
|
|
195
|
+
next to a floating icon.
|
|
196
|
+
A background-image costs nothing extra: only the URL in the rule that MATCHES is
|
|
197
|
+
fetched, so this is still one file, and the rule is in this inline <style> in <head>
|
|
198
|
+
so the fetch starts at parse time just as the preload scanner would have started
|
|
199
|
+
it. Mirrors how .toolbar-logo-image does it in htldoc.layouts.css. //-- SG */
|
|
200
|
+
#hbBootSplash .hbBootLogo {
|
|
201
|
+
width: 260px;
|
|
202
|
+
height: 36px;
|
|
203
|
+
display: block;
|
|
204
|
+
background: url(images/hornbill-logo-full.svg) no-repeat center / contain;
|
|
205
|
+
}
|
|
206
|
+
html.dark #hbBootSplash .hbBootLogo {
|
|
207
|
+
background-image: url(images/hornbill-logo-full-reversed.svg);
|
|
66
208
|
}
|
|
67
209
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
for (let x = 0; x < arrFiles.length; x++) {
|
|
75
|
-
var script = document.createElement('script');
|
|
76
|
-
script.src = arrFiles[x];
|
|
77
|
-
script.type = 'text/javascript';
|
|
78
|
-
script.async = false;
|
|
79
|
-
|
|
80
|
-
script.onload = function () {
|
|
81
|
-
loadedCount++;
|
|
82
|
-
if (loadedCount === arrFiles.length) callback();
|
|
83
|
-
}
|
|
210
|
+
#hbBootSplash .hbBootLabel {
|
|
211
|
+
font-size: 15px;
|
|
212
|
+
letter-spacing: 0.08em;
|
|
213
|
+
text-transform: uppercase;
|
|
214
|
+
opacity: 0.75;
|
|
215
|
+
}
|
|
84
216
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
217
|
+
/* LOCAL PREVIEW PATCH: amber "Preview" pill next to the toolbar's
|
|
218
|
+
Documentation label, so a local hdoc serve can't be mistaken for
|
|
219
|
+
the live site. Re-apply on re-sync from the htdocs viewer. */
|
|
220
|
+
.toolbar-layout .hb-preview-badge {
|
|
221
|
+
display: inline-block;
|
|
222
|
+
margin-left: 8px;
|
|
223
|
+
padding: 0 10px;
|
|
224
|
+
border-radius: 999px;
|
|
225
|
+
font-size: 11px;
|
|
226
|
+
font-weight: 700;
|
|
227
|
+
letter-spacing: 0.08em;
|
|
228
|
+
text-transform: uppercase;
|
|
229
|
+
line-height: 18px;
|
|
230
|
+
vertical-align: 2px;
|
|
231
|
+
color: #6b4400;
|
|
232
|
+
background: #ffd777;
|
|
233
|
+
}
|
|
234
|
+
html.dark .toolbar-layout .hb-preview-badge {
|
|
235
|
+
color: #ffe2a0;
|
|
236
|
+
background: #7a5200;
|
|
88
237
|
}
|
|
89
238
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
239
|
+
/* Indeterminate, because there is nothing real to measure here - the boot is a chain
|
|
240
|
+
of fetches of unknown length, and a fake percentage would be a lie. */
|
|
241
|
+
#hbBootSplash .hbBootBar {
|
|
242
|
+
position: relative;
|
|
243
|
+
width: 200px;
|
|
244
|
+
height: 3px;
|
|
245
|
+
border-radius: 3px;
|
|
246
|
+
overflow: hidden;
|
|
247
|
+
background-color: #d1d1d1;
|
|
248
|
+
}
|
|
249
|
+
html.dark #hbBootSplash .hbBootBar { background-color: #3a3a3a; }
|
|
250
|
+
|
|
251
|
+
#hbBootSplash .hbBootBar::after {
|
|
252
|
+
content: "";
|
|
253
|
+
position: absolute;
|
|
254
|
+
top: 0;
|
|
255
|
+
bottom: 0;
|
|
256
|
+
width: 40%;
|
|
257
|
+
border-radius: 3px;
|
|
258
|
+
background-color: #00527f;
|
|
259
|
+
animation: hbBootBar 1.1s ease-in-out infinite;
|
|
260
|
+
}
|
|
261
|
+
html.dark #hbBootSplash .hbBootBar::after { background-color: #7cc4f5; }
|
|
108
262
|
|
|
109
|
-
|
|
263
|
+
@keyframes hbBootBar {
|
|
264
|
+
0% { left: -40%; }
|
|
265
|
+
100% { left: 100%; }
|
|
266
|
+
}
|
|
110
267
|
|
|
111
|
-
|
|
268
|
+
/* Stall message. Pure CSS on a long animation-delay rather than a setTimeout, so it
|
|
269
|
+
still appears when the thing that failed IS the JS - a splash that spins forever
|
|
270
|
+
with no explanation is worse than the blank page this replaced. */
|
|
271
|
+
#hbBootSplash .hbBootStalled {
|
|
272
|
+
max-width: 320px;
|
|
273
|
+
text-align: center;
|
|
274
|
+
font-size: 13px;
|
|
275
|
+
line-height: 1.5;
|
|
276
|
+
opacity: 0;
|
|
277
|
+
animation: hbBootSplashIn 400ms ease-out 20s forwards;
|
|
112
278
|
}
|
|
113
279
|
|
|
280
|
+
@media (prefers-reduced-motion: reduce) {
|
|
281
|
+
/* No sliding bar and no fades - but keep the delays, so a fast load still does not
|
|
282
|
+
flash the splash, and keep the bar itself as a static track so the layout holds. */
|
|
283
|
+
#hbBootSplash { animation: hbBootSplashIn 1ms linear 400ms forwards; }
|
|
284
|
+
#hbBootSplash .hbBootBar::after { animation: none; left: 0; width: 100%; opacity: 0.5; }
|
|
285
|
+
#hbBootSplash .hbBootStalled { animation: hbBootSplashIn 1ms linear 20s forwards; }
|
|
286
|
+
}
|
|
114
287
|
|
|
115
|
-
|
|
288
|
+
/* Removal is a class, not a node removal, so the fade can run. hbHideBootSplash()
|
|
289
|
+
in js/doc.hornbill.js adds this and then display:none's the element on transitionend
|
|
290
|
+
(with a timer fallback), because a splash left at opacity 0 over the whole viewport
|
|
291
|
+
would eat every click. */
|
|
292
|
+
#hbBootSplash.hbBootSplashGone {
|
|
293
|
+
opacity: 0;
|
|
294
|
+
animation: none;
|
|
295
|
+
transition: opacity 200ms ease-out;
|
|
296
|
+
pointer-events: none;
|
|
297
|
+
}
|
|
116
298
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
299
|
+
/* opacity is NOT overridable by a descendant - visibility is. A single
|
|
300
|
+
visibility:visible anywhere in the subtree used to punch a hole straight
|
|
301
|
+
through this guard and paint the raw uncompiled template ({{mustaches}},
|
|
302
|
+
every v-if branch at once) until Vue mounted. Keep both: visibility also
|
|
303
|
+
takes the subtree out of the a11y tree and hit-testing. //-- SG */
|
|
304
|
+
.hb-hidden{visibility:hidden !important;opacity:0 !important;}
|
|
305
|
+
/* Distinct from hb-hidden above, deliberately. hb-hidden is the pre-Vue boot
|
|
306
|
+
guard and is removed once, from the mount host. This one is view state -
|
|
307
|
+
content hidden while the next document loads - so it is driven by a Vue
|
|
308
|
+
binding on the elements themselves. Keeping one class for both jobs is what
|
|
309
|
+
made the reveal a global $(".hb-hidden") sweep. */
|
|
310
|
+
.hb-content-loading{visibility:hidden;}
|
|
311
|
+
|
|
312
|
+
.skip-link {
|
|
313
|
+
position: absolute;
|
|
314
|
+
top: -40px;
|
|
315
|
+
left: 0;
|
|
316
|
+
background: var(--htl-c-brand, #0066cc);
|
|
317
|
+
color: #fff;
|
|
318
|
+
padding: 8px 16px;
|
|
319
|
+
z-index: 10000;
|
|
320
|
+
text-decoration: none;
|
|
321
|
+
font-weight: 600;
|
|
322
|
+
}
|
|
323
|
+
.skip-link:focus {
|
|
324
|
+
top: 0;
|
|
325
|
+
}
|
|
129
326
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
</a>
|
|
135
|
-
</div>
|
|
327
|
+
/* .caveat-text (the hdoc-approve signature style) moved to
|
|
328
|
+
css/theme-default/styles/components/htl-doc.css so the inline/embedded view -
|
|
329
|
+
which can also mount hdoc-approve - gets it too. */
|
|
330
|
+
</style>
|
|
136
331
|
|
|
137
|
-
|
|
332
|
+
<!-- charset and viewport used to be here; moved to the very top of <head> - see the note
|
|
333
|
+
there. Do not add them back in this position. -->
|
|
334
|
+
<link rel="shortcut icon" type="image/svg+xml" href="/favicon.svg">
|
|
335
|
+
<!--[[META-LINKS]]-->
|
|
336
|
+
|
|
337
|
+
<!-- ALL stylesheets below carry data-hb-css + media="print" + the onload/onerror flip, which
|
|
338
|
+
is what makes them non-render-blocking. See the NON-BLOCKING CSS note in the inline
|
|
339
|
+
<script> at the top of this <head>: paint no longer waits for them, and revealApp() does
|
|
340
|
+
instead, so the app still never appears unstyled.
|
|
341
|
+
media="print" is the mechanism, not a mistake - a print stylesheet does not block the
|
|
342
|
+
screen render, and hbOnCssLoad() flips it to "all" the moment it lands.
|
|
343
|
+
Adding a stylesheet here? Copy the whole attribute set. Miss it and that file goes back
|
|
344
|
+
to blocking the first paint; miss only data-hb-css and the reveal gate stops counting
|
|
345
|
+
it, which is the safer half of the failure. Cascade order is unaffected: it follows
|
|
346
|
+
document order, not load order. -->
|
|
347
|
+
|
|
348
|
+
<!-- caveat stylesheet - for doc signee signatures -->
|
|
349
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
350
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
351
|
+
<link href="https://fonts.googleapis.com/css2?family=Caveat&display=swap" rel="stylesheet" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
352
|
+
|
|
353
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.css" integrity="sha384-Bk5cbLkZQ5raZ0+H2/+VbfYx3WpvxvQK4zqXZr7sYODuaX7bKXoSOnipQxkaS8sv" crossorigin="anonymous" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
354
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
355
|
+
|
|
356
|
+
<!-- Theme CSS, static rather than injected by js/bootstrap.js.
|
|
357
|
+
No leading slash, so these resolve against the LITERAL <base href="/"> at the top of this
|
|
358
|
+
<head>. The literal is load-bearing: the HTML preload scanner resolves these hrefs before
|
|
359
|
+
any inline script has run, so while <base> was script-assigned every one of them 404'd
|
|
360
|
+
against the document's own directory first and only succeeded when the parser
|
|
361
|
+
re-resolved them - twelve wasted requests per page load.
|
|
362
|
+
Must stay AFTER the Bootstrap stylesheet above to keep the cascade order the JS loader
|
|
363
|
+
produced, and in exactly this order: fonts -> vars -> base -> layouts -> components. -->
|
|
364
|
+
<link rel="stylesheet" href="css/theme-default/styles/fonts.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
365
|
+
<link rel="stylesheet" href="css/theme-default/styles/vars.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
366
|
+
<link rel="stylesheet" href="css/theme-default/styles/base.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
367
|
+
<link rel="stylesheet" href="css/theme-default/styles/htldoc.layouts.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
368
|
+
<link rel="stylesheet" href="css/theme-default/styles/components/htl-search.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
369
|
+
<link rel="stylesheet" href="css/theme-default/styles/components/htl-library.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
370
|
+
<link rel="stylesheet" href="css/theme-default/styles/components/htl-doc.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
371
|
+
<link rel="stylesheet" href="css/theme-default/styles/components/api-doc.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
372
|
+
<link rel="stylesheet" href="css/theme-default/styles/components/sidebar.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
373
|
+
<link rel="stylesheet" href="css/theme-default/styles/components/content.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
374
|
+
<link rel="stylesheet" href="css/theme-default/styles/components/custom-block.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
375
|
+
<link rel="stylesheet" href="js/highlightjs/styles/vs2015-accessible.css" data-hb-css media="print" onload="hbOnCssLoad(this)" onerror="hbOnCssLoad(this)">
|
|
376
|
+
|
|
377
|
+
<!-- Counted from the DOM rather than hardcoded, so adding or removing a stylesheet above
|
|
378
|
+
cannot silently break the reveal gate. Must stay AFTER the last data-hb-css link.
|
|
379
|
+
hbCssCheck() is called again here because a cached stylesheet can fire onload before this
|
|
380
|
+
script runs, in which case the count was already complete but the total was not yet
|
|
381
|
+
known. //-- SG -->
|
|
382
|
+
<script>
|
|
383
|
+
window.hbCssTotal = document.querySelectorAll('link[data-hb-css]').length;
|
|
384
|
+
window.hbCssTotalKnown = true;
|
|
385
|
+
hbCssCheck();
|
|
386
|
+
</script>
|
|
387
|
+
|
|
388
|
+
<!-- defer on all three, and it is load-bearing for the boot splash. Without it these are
|
|
389
|
+
PARSER-blocking: the parser stops dead at the first one and does not reach <body> until
|
|
390
|
+
all three have come down from the CDN, so the splash markup does not exist yet and there
|
|
391
|
+
is nothing to paint. Measured on Slow 3G: first-paint 8548ms.
|
|
392
|
+
defer is safe for the ordering this site needs - deferred scripts run in document order
|
|
393
|
+
and all of them finish before DOMContentLoaded, which is when js/bootstrap.js is
|
|
394
|
+
injected. So Popper, Bootstrap and Vue are all present before loadJS() runs, exactly as
|
|
395
|
+
before. Do not swap defer for async: async would let Vue execute after
|
|
396
|
+
js/doc.hornbill.js. //-- SG -->
|
|
397
|
+
<script defer src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
|
|
398
|
+
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js" integrity="sha384-G/EV+4j2dNv+tEPo3++6LCgdCROaejBqfUeNjuKAiuXbjrxilcCdDz6ZAVfHWe1Y" crossorigin="anonymous"></script>
|
|
399
|
+
<!-- Vue 3 global PRODUCTION build. Still the full build: vue.global.prod.js keeps the runtime
|
|
400
|
+
template compiler, which this site needs because every template is in-DOM markup or a
|
|
401
|
+
<script type="text/x-template"> block. It only drops dev warnings and dev-only proxies.
|
|
402
|
+
165KB vs 588KB for vue.global.js.
|
|
403
|
+
To debug Vue itself, swap .prod.js back to .js AND set integrity to
|
|
404
|
+
sha384-xZGBZ8/+QRtMg/kJMPyo4v1otVc8kxjAeiKnaCg+yBlIxeE+11fWfYLJAM6BdWbc -->
|
|
405
|
+
<script defer src="https://cdn.jsdelivr.net/npm/vue@3.5.40/dist/vue.global.prod.js" integrity="sha384-yjSqnKX+01fhYzLUMRlCkMrHMTdKxwYcGIk5HZ6ug+4nfJn10+F7YsKRuGR5kQac" crossorigin="anonymous"></script>
|
|
406
|
+
|
|
407
|
+
<!-- template for nav item section -->
|
|
408
|
+
<script type="text/x-template" id="nav-section-template">
|
|
409
|
+
<section class="DocSidebarGroup" v-bind:class="{collapsible: asection.items && asection.items.length}">
|
|
410
|
+
<div class="navSectionGroupItem title" v-on:click="toggleNavCollapse(asection)">
|
|
411
|
+
<div class="action">
|
|
412
|
+
<div v-if="asection.items && asection.items.length" class="icon">
|
|
413
|
+
<i v-bind:class="{'bi bi-chevron-right': !asection.expand,'bi bi-chevron-down': asection.expand}" class="bi"></i>
|
|
414
|
+
</div>
|
|
415
|
+
</div>
|
|
416
|
+
<span class="title-text">{{asection.text}}</span>
|
|
417
|
+
</div>
|
|
418
|
+
<!-- A collapsed group now renders NOTHING instead of hidden DOM. With v-show, every
|
|
419
|
+
descendant of every collapsed group stayed in the document: on a large book that
|
|
420
|
+
was 205 nav links of which only 21 were visible, and the sidebar accounted for
|
|
421
|
+
~81% of all elements on the page.
|
|
422
|
+
The <template> wrapper is deliberate and safe *because it carries v-if* - Vue
|
|
423
|
+
treats it as a fragment and emits no element of its own, so the
|
|
424
|
+
.DocSidebarGroup .items structure the CSS targets is byte-for-byte unchanged.
|
|
425
|
+
Never make this a bare <template>; without a directive it compiles to a real
|
|
426
|
+
element that the UA stylesheet hides. //-- SG -->
|
|
427
|
+
<template v-if="asection.expand && asection.items">
|
|
428
|
+
<div v-for="(navSectionItem, index) in asection.items" class="items">
|
|
429
|
+
|
|
430
|
+
<div v-if="!navSectionItem.items">
|
|
431
|
+
<a class="DocLink link noitems" v-bind:href="navSectionItem.link" v-bind:target="navSectionItem.target || ''" style="padding-left: 0px;">
|
|
432
|
+
<span class="link-text">{{navSectionItem.text}} <i v-if="navSectionItem.target" class="bi bi-box-arrow-up-right"></i> </span>
|
|
433
|
+
</a>
|
|
434
|
+
</div>
|
|
435
|
+
|
|
436
|
+
<nav-section-component v-if="navSectionItem.items" v-bind:asection="navSectionItem"></nav-section-component>
|
|
437
|
+
|
|
438
|
+
</div>
|
|
439
|
+
</template>
|
|
440
|
+
</section>
|
|
441
|
+
</script>
|
|
442
|
+
|
|
443
|
+
<!--[[ANALYTICS]]-->
|
|
444
|
+
|
|
445
|
+
<!-- eof template-->
|
|
446
|
+
|
|
447
|
+
</head>
|
|
448
|
+
|
|
449
|
+
<body>
|
|
450
|
+
|
|
451
|
+
<!-- ===================== BOOT SPLASH =====================
|
|
452
|
+
Covers the gap between first paint and revealApp(). The app template below is hidden
|
|
453
|
+
by .hb-hidden until Vue mounts, which on a slow link left a blank page for the whole
|
|
454
|
+
chain: document -> bootstrap.js (DOMContentLoaded) -> 5 JS files -> book info fetch.
|
|
455
|
+
|
|
456
|
+
FIRST element in <body> on purpose, so the preload scanner sees the logo before any of
|
|
457
|
+
that. Styled entirely from the inline <style> above - it must not depend on a
|
|
458
|
+
stylesheet that may still be in flight.
|
|
459
|
+
|
|
460
|
+
aria-live is deliberately absent; role="status" alone announces the label once. The
|
|
461
|
+
whole thing is removed from the a11y tree by hbHideBootSplash(). -->
|
|
462
|
+
<div id="hbBootSplash" role="status" aria-label="Loading Hornbill Documentation">
|
|
463
|
+
<!-- role="img" + aria-label, because the logo is a CSS background and so carries no
|
|
464
|
+
alt text of its own. See the .hbBootLogo rule above for why it is a background and
|
|
465
|
+
not an <img>. -->
|
|
466
|
+
<div class="hbBootLogo" role="img" aria-label="Hornbill"></div>
|
|
467
|
+
<span class="hbBootLabel">Documentation</span>
|
|
468
|
+
<div class="hbBootBar"></div>
|
|
469
|
+
<p class="hbBootStalled">This is taking longer than usual. If nothing appears, refresh the page.</p>
|
|
470
|
+
</div>
|
|
138
471
|
|
|
472
|
+
<a href="#DocContent" class="skip-link">Skip to main content</a>
|
|
473
|
+
|
|
474
|
+
<!-- data-root-click, not v-on:click: Vue 3 compiles this element's innerHTML as the app
|
|
475
|
+
template and does not compile the element itself, so a directive here would be
|
|
476
|
+
ignored. hbBindRootHandler() in js/doc.hornbill.js attaches it after mount. -->
|
|
477
|
+
<div id="vDocDevApp" class="hb-hidden Layout vh-100 hb-container-vertical overflow-hidden" data-root-click="resetMobileMenu">
|
|
478
|
+
<!-- top toolbar -->
|
|
479
|
+
<div class="toolbar-layout hb-container-horizontal" v-if="!docApp.loadingBookInfo">
|
|
480
|
+
|
|
481
|
+
<!-- LOCAL PREVIEW PATCH: logo is inert (no library home locally), and the
|
|
482
|
+
toolbar is labelled as a preview so it can't be mistaken for the live
|
|
483
|
+
site. Re-apply on re-sync from the htdocs viewer. -->
|
|
484
|
+
<div class="toolbar-logo">
|
|
485
|
+
<a class="toolbar-logo-image" style="cursor: default;" aria-label="Hornbill Documentation"></a>
|
|
486
|
+
</div>
|
|
487
|
+
|
|
488
|
+
<div class="hb-center-v">
|
|
489
|
+
<h1 class="documentation-home">Documentation <span class="hb-preview-badge">Preview</span></h1>
|
|
490
|
+
</div>
|
|
491
|
+
<div class="toolbar-middle hb-center-v hb-container-expand"></div>
|
|
492
|
+
<div v-if="docApp.book.docId" class="toolbar-right toolbar-search-bar hb-center-v">
|
|
493
|
+
|
|
494
|
+
<div class="position-relative">
|
|
495
|
+
<!-- LOCAL PREVIEW PATCH: hdoc serve has no search backend, so the box is
|
|
496
|
+
disabled (production htdocs copy keeps it live). Re-apply on re-sync. -->
|
|
497
|
+
<input disabled title="Search is not available in the hdoc serve local preview" v-model="docApp.searchText" placeholder="Search" type="search" maxlength="200" aria-label="Search documentation (not available in local preview)" autocomplete="off" class="form-control shadow-none rounded-pill ps-3 pe-4" v-on:keyup.enter="toolbarSearch()">
|
|
498
|
+
<i v-if="!docApp.searchText" class="text-secondary position-absolute bi bi-search" style="display: flex;top: 10px;right: 12px;"></i>
|
|
499
|
+
<!-- Enter on a single character sends nothing (length gate in searchDocuments)
|
|
500
|
+
and deliberately leaves the article on screen, so this is the only
|
|
501
|
+
feedback there is. Self-clearing after a few seconds - see
|
|
502
|
+
showSearchTooShortHint(). role=status so screen readers announce it
|
|
503
|
+
without stealing focus. -->
|
|
504
|
+
<div v-if="docApp.searchTooShortHint" class="search-tooshort-hint" role="status">Enter at least two characters to search</div>
|
|
505
|
+
</div>
|
|
506
|
+
|
|
507
|
+
</div>
|
|
508
|
+
<div class="toolbar-right hb-center-v">
|
|
509
|
+
<!-- narrow-viewport search: the inline search box (.toolbar-search-bar above) is
|
|
510
|
+
hidden below 602px, so this button takes the user to the search page instead.
|
|
511
|
+
Hidden at wider widths by .show-on-mobile-only (htldoc.layouts.css).
|
|
512
|
+
LOCAL PREVIEW PATCH: hidden entirely — no search backend in hdoc serve. -->
|
|
513
|
+
<button v-if="false" class="show-on-mobile-only me-2 btn btn-sm rounded-circle toolbar-icon-btn" v-on:click="openSearchView()" aria-label="Search documentation" :class="{'btn-light':docApp.ThemePreference!=='dark','btn-secondary':docApp.ThemePreference=='dark'}"><i class="bi bi-search"></i></button>
|
|
514
|
+
|
|
515
|
+
<!-- MCP server catalog. Not admin-gated: the endpoints it lists are
|
|
516
|
+
open, so the catalog is useful to anyone wiring up an agent.
|
|
517
|
+
LOCAL PREVIEW PATCH: disabled — no MCP catalog backend in hdoc serve. -->
|
|
518
|
+
<button disabled class="me-2 btn btn-sm rounded-circle toolbar-icon-btn" aria-label="Connect an AI assistant (not available in local preview)" title="Not available in the hdoc serve local preview" :class="{'btn-light':docApp.ThemePreference!=='dark','btn-secondary':docApp.ThemePreference=='dark'}">
|
|
519
|
+
<span class="hb-icon-mcp" aria-hidden="true"></span>
|
|
520
|
+
</button>
|
|
521
|
+
|
|
522
|
+
<!-- theme switch -->
|
|
523
|
+
<button v-if="docApp.ThemePreference!=='dark'" class="btn btn-sm btn-light rounded-circle toolbar-icon-btn" v-on:click="switchViewTheme()" aria-label="Switch to dark theme"><i class="bi bi-moon-fill"></i></button>
|
|
524
|
+
<button v-if="docApp.ThemePreference==='dark'" class="btn btn-sm btn-secondary rounded-circle toolbar-icon-btn" v-on:click="switchViewTheme()" aria-label="Switch to light theme"><i class="bi bi-brightness-high"></i></button>
|
|
525
|
+
|
|
526
|
+
<!-- settings (admin/persona). No "close settings" twin here: the /settings
|
|
527
|
+
page is served by settings/index.html, so this app is never on it -->
|
|
528
|
+
<button v-if="docApp.userSession && docApp.userSession.isAdmin" class="ms-2 btn btn-sm rounded-circle toolbar-icon-btn" v-on:click="switchSettingsView()" aria-label="Open settings" :class="{'btn-light':docApp.ThemePreference!=='dark','btn-secondary':docApp.ThemePreference=='dark'}">
|
|
529
|
+
<i class="bi bi-gear"></i>
|
|
530
|
+
</button>
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
</div>
|
|
534
|
+
<div v-if="docApp.userSession && docApp.userSession.profileImage" class="toolbar-right hb-center-v">
|
|
535
|
+
|
|
536
|
+
<!-- user info -->
|
|
537
|
+
<div class="userprofile dropdown">
|
|
538
|
+
|
|
539
|
+
<span class="dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
540
|
+
<img class="svg-prof" v-bind:src="docApp.userSession.profileImage" :alt="docApp.userSession.userName + ' profile'" style="width:32px;height:32px;">
|
|
541
|
+
</span>
|
|
542
|
+
<ul class="dropdown-menu p-0">
|
|
543
|
+
<div class="panel-header border-0 position-relative">
|
|
544
|
+
<div class="position-relative">
|
|
545
|
+
<div class="text-center fw-bolder lh-3">{{docApp.userSession.userId}}</div>
|
|
546
|
+
<div style="height:20px"></div>
|
|
547
|
+
</div>
|
|
548
|
+
<div class="position-absolute top-100 start-50 translate-middle">
|
|
549
|
+
<img class="svg-prof" v-bind:src="docApp.userSession.profileImage" :alt="docApp.userSession.userName + ' profile'" style="width:64px;height:64px;">
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
<div class="panel-content border-0 border-top p-3 pt-5 fw-light lh-normal">
|
|
553
|
+
<div class="text-center fw-bolder p-1">{{docApp.userSession.userName}}</div>
|
|
554
|
+
<div class="text-center fw-bolder p-1 fs-12">{{docApp.userSession.email}}</div>
|
|
555
|
+
</div>
|
|
556
|
+
<div class="panel-content p-3 border-0 border-top fw-light lh-normal">
|
|
557
|
+
<div class="lh-normal fs-12"><i class="bi bi-database-lock"></i> <span style="display:inline-block;width:48px">Issuer</span> : {{docApp.userSession.issuerInstance}}</div>
|
|
558
|
+
<div class="lh-normal fs-12" v-bind:title="docApp.userSession.expiresDateTime"><i class="bi bi-hourglass-split"></i> <span style="display:inline-block;width:48px">Expires</span> : {{new Date(docApp.userSession.validUntil).toLocaleDateString()}}</div>
|
|
559
|
+
</div>
|
|
560
|
+
</ul>
|
|
561
|
+
</div>
|
|
562
|
+
</div>
|
|
563
|
+
</div>
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
<!-- book title band. The mobile burger lives HERE, not in the top toolbar - it
|
|
568
|
+
toggles the BOOK page navigation, so it belongs with the book context. CSS
|
|
569
|
+
hides it at >=1020px (sidebar.css). resetMobileMenu still matches it by class
|
|
570
|
+
to decide whether a click should dismiss the nav, so the class matters, but its
|
|
571
|
+
position in the DOM does not. The open state itself is docApp.bookNavOpen. -->
|
|
572
|
+
<div v-if="docApp.book.docId" class="title-band hb-bg-mute p-3 border-bottom">
|
|
573
|
+
<button class="mobile-menu-btn" aria-label="Toggle book navigation" :aria-expanded="docApp.bookNavOpen ? 'true' : 'false'" v-on:click="toggleMobileMenu()"><i class="bi bi-list-ul"></i></button>
|
|
574
|
+
<div class="hb-container-expand">
|
|
575
|
+
<h2 class="title">{{docApp.title}}</h2>
|
|
576
|
+
<span class="description2">{{docApp.description}}</span>
|
|
577
|
+
</div>
|
|
139
578
|
</div>
|
|
140
|
-
</section>
|
|
141
|
-
</script>
|
|
142
|
-
<!-- eof template-->
|
|
143
579
|
|
|
144
|
-
|
|
580
|
+
<div class="main-layout hb-container-vertical hb-container-expand">
|
|
145
581
|
|
|
146
|
-
|
|
582
|
+
<!-- ============ MCP server catalog (/mcp) ============ -->
|
|
583
|
+
<!-- Rendered inside .HTL-doc, the same typography scope a book page uses,
|
|
584
|
+
with plain semantic markup and no component classes. Headings, body
|
|
585
|
+
text, code spans and tables then come from the article styles the
|
|
586
|
+
rest of the site already has - this page IS documentation, so it
|
|
587
|
+
should read as a page of it rather than carry its own look -->
|
|
588
|
+
<div v-if="docApp.mcpCatalogMode" class="McpCatalog hb-container-expand hb-scroll hb-container-vertical">
|
|
589
|
+
<div class="HTL-doc content-wrapper">
|
|
147
590
|
|
|
148
|
-
|
|
149
|
-
<!-- top toolbar -->
|
|
150
|
-
<div class="toolbar-layout hb-container-horizontal">
|
|
591
|
+
<h1>Connect an AI assistant</h1>
|
|
151
592
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
<div class="toolbar-split"></div>
|
|
159
|
-
<div class="toolbar-middle hb-center-v hb-container-expand">
|
|
160
|
-
|
|
161
|
-
<h2 class="title">{{docApp.title}}</h2>
|
|
162
|
-
<h5 class="description">{{docApp.description}}</h5>
|
|
163
|
-
|
|
164
|
-
<!-- could generate nav bar based on library settings -->
|
|
165
|
-
<!--
|
|
166
|
-
<ul class="nav-bar-nav-list">
|
|
167
|
-
<li class="nav-bar-item active">
|
|
168
|
-
<a>Documentation</a>
|
|
169
|
-
</li>
|
|
170
|
-
</ul>
|
|
171
|
-
-->
|
|
172
|
-
</div>
|
|
173
|
-
<div class="toolbar-right hb-center-v">
|
|
174
|
-
|
|
175
|
-
<!-- theme switch -->
|
|
176
|
-
<label class="theme-switch">
|
|
177
|
-
<input class="theme-switch-checkbox" type="checkbox" v-on:change="switchViewTheme()">
|
|
178
|
-
<span class="slider round">
|
|
179
|
-
</span>
|
|
180
|
-
</label>
|
|
181
|
-
</div>
|
|
182
|
-
<div v-if="docApp.userSession" class="toolbar-right hb-center-v">
|
|
183
|
-
<!-- user info -->
|
|
184
|
-
<div class="userprofile dropdown">
|
|
185
|
-
<span class="dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
186
|
-
<img class="svg-prof" v-bind:src="docApp.userSession.profileImage"
|
|
187
|
-
style="width:32;height:32;"></img>
|
|
188
|
-
</span>
|
|
189
|
-
<ul class="dropdown-menu p-0">
|
|
593
|
+
<p>
|
|
594
|
+
Your AI assistant can read this documentation directly, so it answers from
|
|
595
|
+
the current content instead of guessing. Add one of the servers below to your
|
|
596
|
+
client — they use the Model Context Protocol (MCP). Each one covers a
|
|
597
|
+
different body of content, so add the one you need.
|
|
598
|
+
</p>
|
|
190
599
|
|
|
600
|
+
<p v-if="docApp.mcpCatalog.error">
|
|
601
|
+
The catalog did not load. Refresh the page to try again.
|
|
602
|
+
</p>
|
|
191
603
|
|
|
604
|
+
<p v-if="!docApp.mcpCatalog.error && !docApp.mcpCatalog.servers.length">
|
|
605
|
+
Loading…
|
|
606
|
+
</p>
|
|
192
607
|
|
|
193
|
-
<
|
|
194
|
-
<div class="position-relative">
|
|
195
|
-
<div class="text-center fw-bolder lh-3">{{docApp.userSession.userId}}</div>
|
|
196
|
-
<div style="height:20px"></div>
|
|
197
|
-
</div>
|
|
198
|
-
<div class="position-absolute top-100 start-50 translate-middle">
|
|
199
|
-
<img class="svg-prof" v-bind:src="docApp.userSession.profileImage"
|
|
200
|
-
style="width:64px;height:64px;"></img>
|
|
201
|
-
</div>
|
|
202
|
-
</div>
|
|
203
|
-
<div class="panel-content border-0 border-top p-3 pt-5 fw-light lh-normal">
|
|
204
|
-
<div class="text-center fw-bolder p-1">{{docApp.userSession.userName}}</div>
|
|
205
|
-
<div class="text-center fw-bolder p-1 fs-12">{{docApp.userSession.email}}</div>
|
|
206
|
-
</div>
|
|
207
|
-
<div class="panel-content p-3 border-0 border-top fw-light lh-normal">
|
|
208
|
-
<div class="lh-normal fs-12"><i class="bi bi-database-lock"></i> <span
|
|
209
|
-
style="display:inline-block;width:48px">Issuer</span> :
|
|
210
|
-
{{docApp.userSession.issuerInstance}}</div>
|
|
211
|
-
<div class="lh-normal fs-12" v-bind:title="docApp.userSession.expiresDateTime"><i
|
|
212
|
-
class="bi bi-hourglass-split"></i> <span
|
|
213
|
-
style="display:inline-block;width:48px">Expires</span> : {{new
|
|
214
|
-
Date(docApp.userSession.validUntil).toLocaleDateString()}}</div>
|
|
215
|
-
</div>
|
|
216
|
-
</ul>
|
|
217
|
-
</div>
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
608
|
+
<template v-for="server in docApp.mcpCatalog.servers" :key="server.name">
|
|
220
609
|
|
|
221
|
-
|
|
222
|
-
<div class="main-layout hb-container-horizontal hb-container-expand hb-hidden">
|
|
610
|
+
<h2>{{server.name}}</h2>
|
|
223
611
|
|
|
224
|
-
|
|
225
|
-
<aside class="DocSidebar hb-container-vertical hb-hidden">
|
|
612
|
+
<p>{{server.summary}}</p>
|
|
226
613
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
614
|
+
<p>
|
|
615
|
+
<span class="mcp-server-count">
|
|
616
|
+
<span v-if="server.documentCount">{{server.documentCount.toLocaleString()}} pages</span>
|
|
617
|
+
<span v-if="!server.documentCount">Not published to this site</span>
|
|
618
|
+
</span>
|
|
619
|
+
</p>
|
|
620
|
+
|
|
621
|
+
<!-- The two fields every MCP client asks for. The name is the
|
|
622
|
+
server's own identifier, so it is the one to use - saying so
|
|
623
|
+
saves the reader inventing one and keeps names consistent
|
|
624
|
+
between people setting this up -->
|
|
625
|
+
<dl class="mcp-connect">
|
|
626
|
+
<dt>Name</dt>
|
|
627
|
+
<dd>
|
|
628
|
+
<code>{{server.name}}</code>
|
|
629
|
+
<button type="button" class="mcp-copy" title="Copy to clipboard"
|
|
630
|
+
:aria-label="'Copy the name ' + server.name + ' to the clipboard'"
|
|
631
|
+
v-on:click="mcpCopy(server.path + ':name', server.name)">
|
|
632
|
+
<i class="bi" :class="docApp.mcpCopied[server.path + ':name'] ? 'bi-check2' : 'bi-clipboard'"></i>
|
|
633
|
+
<span class="visually-hidden" v-if="docApp.mcpCopied[server.path + ':name']">Copied</span>
|
|
634
|
+
</button>
|
|
635
|
+
</dd>
|
|
636
|
+
|
|
637
|
+
<dt>URL</dt>
|
|
638
|
+
<dd>
|
|
639
|
+
<code>{{mcpServerUrl(server.path)}}</code>
|
|
640
|
+
<button type="button" class="mcp-copy" title="Copy to clipboard"
|
|
641
|
+
:aria-label="'Copy the URL for ' + server.name + ' to the clipboard'"
|
|
642
|
+
v-on:click="mcpCopy(server.path + ':url', mcpServerUrl(server.path))">
|
|
643
|
+
<i class="bi" :class="docApp.mcpCopied[server.path + ':url'] ? 'bi-check2' : 'bi-clipboard'"></i>
|
|
644
|
+
<span class="visually-hidden" v-if="docApp.mcpCopied[server.path + ':url']">Copied</span>
|
|
645
|
+
</button>
|
|
646
|
+
</dd>
|
|
647
|
+
</dl>
|
|
648
|
+
|
|
649
|
+
<table>
|
|
650
|
+
<thead>
|
|
651
|
+
<tr><th>Tool</th><th>What it does</th></tr>
|
|
652
|
+
</thead>
|
|
653
|
+
<tbody>
|
|
654
|
+
<template v-for="tool in server.tools" :key="tool.name">
|
|
655
|
+
<tr>
|
|
656
|
+
<td><code>{{tool.name}}</code></td>
|
|
657
|
+
<td><template v-for="(part,pi) in mcpToolDescriptionParts(tool.description)" :key="pi"><code v-if="part.code">{{part.text}}</code><template v-else>{{part.text}}</template></template></td>
|
|
658
|
+
</tr>
|
|
659
|
+
</template>
|
|
660
|
+
</tbody>
|
|
661
|
+
</table>
|
|
662
|
+
|
|
663
|
+
</template>
|
|
230
664
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
v-bind:target="navSection.target || ''" style="padding-left: 0px;">
|
|
234
|
-
<span class="link-text">{{navSection.text}} <i v-if="navSection.target"
|
|
235
|
-
class="bi bi-box-arrow-up-right"></i> </span>
|
|
236
|
-
</a>
|
|
237
|
-
</div>
|
|
665
|
+
</div>
|
|
666
|
+
</div>
|
|
238
667
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
668
|
+
<div v-if="!docApp.mcpCatalogMode && !docApp.loadingBookInfo && (docApp.searchMode || (!docApp.book.docId && docApp.selectedLibraryGroup))">
|
|
669
|
+
<!-- library banner - can make this a search and hero menu -->
|
|
670
|
+
<div class="library-banner-hug hb-bg-mute pad-20">
|
|
671
|
+
|
|
672
|
+
<div class="mt-4 mb-4">
|
|
673
|
+
<h1 class="display-4">How can we help?</h1>
|
|
242
674
|
</div>
|
|
243
|
-
</div>
|
|
244
675
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
676
|
+
<div class="mt-4 mw-800">
|
|
677
|
+
|
|
678
|
+
<div class="hb-container-horizontal">
|
|
679
|
+
<div class="hb-container-expand">
|
|
680
|
+
<div class="input-group input-group-lg">
|
|
681
|
+
<span class="input-group-text white"><i class="bi bi-search"></i></span>
|
|
682
|
+
<input v-model="docApp.altSearchText" placeholder="Try 'workflow', 'assets', 'API keys'..." type="search" maxlength="200" aria-label="Search documentation" autocomplete="off" class="form-control border-start-0 shadow-none" v-on:keyup="searchDocuments(docApp.altSearchText,false,$event)" v-on:search="searchDocuments(docApp.altSearchText,false,$event)">
|
|
683
|
+
</div>
|
|
684
|
+
</div>
|
|
252
685
|
</div>
|
|
253
686
|
</div>
|
|
254
687
|
|
|
255
|
-
|
|
688
|
+
</div>
|
|
689
|
+
</div>
|
|
256
690
|
|
|
257
|
-
|
|
691
|
+
<div class="hb-container-expand hb-container-horizontal">
|
|
258
692
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
<div class="HTL-doc content-wrapper hb-container-expand">
|
|
693
|
+
<!-- container showing search results -->
|
|
694
|
+
<div v-if="docApp.searchMode && !docApp.loadingBookInfo" class="LibrarySearch hb-container-expand hb-container-vertical">
|
|
262
695
|
|
|
263
|
-
|
|
696
|
+
|
|
697
|
+
<div class="hb-container-expand hb-container-horizontal">
|
|
698
|
+
|
|
699
|
+
<!-- left hannd filter options on product (and other options like tags?) -->
|
|
700
|
+
<!-- No hb-hidden here, and none anywhere else inside this template either:
|
|
701
|
+
the boot guard lives only on the #vDocDevApp mount host, which is the one
|
|
702
|
+
element Vue does not own. Vue rewrites the class attribute of anything it
|
|
703
|
+
renders, so a static hb-hidden in here is put straight back on the next
|
|
704
|
+
re-render and the removeClass never sticks - which is precisely how this
|
|
705
|
+
panel came to open at visibility:hidden and look dead. It is redundant in
|
|
706
|
+
any case: visibility inherits from the host. See revealApp(). -->
|
|
707
|
+
<!-- is-searching: dim the facet lists while a query is in flight - their counts
|
|
708
|
+
belong to the previous response. Unlike the results list this keeps pointer
|
|
709
|
+
events: picking another filter mid-flight is valid (the abort controller
|
|
710
|
+
supersedes the in-flight request). -->
|
|
711
|
+
<aside class="DocSidebar hb-container-vertical p-3 overflow-auto" :class="{'open': docApp.searchFiltersOpen, 'is-searching': docApp.boolSearchInProgress}">
|
|
712
|
+
|
|
713
|
+
<div class="d-flex align-items-center justify-content-between">
|
|
714
|
+
<h2 class="fw-bold fs-5 mb-0">Filter</h2>
|
|
715
|
+
<!-- only rendered below 1020px, where this panel is an overlay -->
|
|
716
|
+
<button type="button" class="search-filter-close btn btn-sm btn-light rounded-circle toolbar-icon-btn" v-on:click="closeSearchFilters()" aria-label="Close filters"><i class="bi bi-x-lg"></i></button>
|
|
717
|
+
</div>
|
|
718
|
+
<div v-if="docApp.searchFilterBySpecificBookId || docApp.hasFilterByProducts || docApp.searchAudienceFilter" class="pt-2 ">
|
|
719
|
+
<span class="HTL-actionlink fw-normal fs-6 c-pointer" v-on:click="clearSearchFilters($event)">Clear All</span>
|
|
720
|
+
</div>
|
|
264
721
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
722
|
+
<!-- audience filter. Internal/dev only (docApp.showAudienceFilter) - never
|
|
723
|
+
shown on the public site, which hosts public books only. Server-side
|
|
724
|
+
filter: re-runs the search with &audience= -->
|
|
725
|
+
<div v-if="docApp.showAudienceFilter" class="border-top mt-4">
|
|
726
|
+
<h2 class="mt-3 fw-bold fs-5"><label class="fw-bold" for="audience_filter_select">Audience</label></h2>
|
|
727
|
+
<select v-model="docApp.searchAudienceFilter" v-on:change="filterSearchByAudience($event)" class="form-select form-select-sm mt-2 fs-6" id="audience_filter_select">
|
|
728
|
+
<option v-for="aud in docApp.searchAudienceOptions" :key="aud.value" :value="aud.value">{{aud.label}}</option>
|
|
729
|
+
</select>
|
|
730
|
+
</div>
|
|
268
731
|
|
|
269
|
-
<!--
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
732
|
+
<!-- content type filter. NOT gated like Audience: reference material
|
|
733
|
+
outnumbers prose everywhere (about 85% of this corpus), so the
|
|
734
|
+
public site needs this as much as the internal one. Server-side
|
|
735
|
+
SOFT filter - re-runs the search with &dt=, and docTypesInfo keeps
|
|
736
|
+
reporting whole-corpus counts so the options below always show what
|
|
737
|
+
is being hidden -->
|
|
738
|
+
<!-- no border-top here: this sits directly beneath the Audience
|
|
739
|
+
select, whose own bottom edge already reads as a rule, and two
|
|
740
|
+
lines that close together look like a mistake -->
|
|
741
|
+
<!-- shown whenever any doc type is present, even a single one - a
|
|
742
|
+
facet that appears and disappears between searches reads as
|
|
743
|
+
broken, and with the filter active (dt defaults to
|
|
744
|
+
Documentation) the single entry still tells the user what they
|
|
745
|
+
are looking at -->
|
|
746
|
+
<div v-if="docApp.searchResponse && docApp.searchResponse.docTypesInfo && docApp.searchResponse.docTypesInfo.length" class="mt-4">
|
|
747
|
+
<h2 class="mt-3 fw-bold fs-5">Information type</h2>
|
|
748
|
+
</div>
|
|
749
|
+
<ul v-if="docApp.searchResponse && docApp.searchResponse.docTypesInfo && docApp.searchResponse.docTypesInfo.length">
|
|
750
|
+
<li class="searchFilterProducts mt-2">
|
|
751
|
+
<div class="form-check fs-5">
|
|
752
|
+
<input v-model="docApp.searchDocTypeFilter" class="form-check-input" v-on:change="filterSearchByDocType('',$event)" type="radio" value="" id="all_doctypes">
|
|
753
|
+
<label class="form-check-label fs-6 c-pointer" for="all_doctypes">All</label>
|
|
754
|
+
</div>
|
|
755
|
+
</li>
|
|
756
|
+
<template v-for="dt in docApp.searchResponse.docTypesInfo" :key="dt.docType">
|
|
757
|
+
<li class="searchFilterProducts mt-2">
|
|
758
|
+
<div class="form-check fs-5">
|
|
759
|
+
<input v-model="docApp.searchDocTypeFilter" class="form-check-input" v-on:change="filterSearchByDocType(String(dt.docType),$event)" type="radio" :value="String(dt.docType)" :id="'doctype_'+dt.docType">
|
|
760
|
+
<div style="width: 85%;display: inline-flex;">
|
|
761
|
+
<label class="form-check-label fs-6 c-pointer text-truncate" :for="'doctype_'+dt.docType" :title="dt.name">{{dt.name}}</label>
|
|
762
|
+
</div>
|
|
763
|
+
<span class="badge rounded-pill hb-bg-mute text-body float-end">{{dt.foundCount}}</span>
|
|
764
|
+
</div>
|
|
765
|
+
</li>
|
|
766
|
+
</template>
|
|
767
|
+
</ul>
|
|
768
|
+
|
|
769
|
+
<!-- book filter -->
|
|
770
|
+
<div v-if="docApp.searchResponse && docApp.searchResponse.booksInfo && docApp.searchResponse.booksInfo.length" class="border-top mt-4">
|
|
771
|
+
<h2 class="mt-3 fw-bold fs-5">Books</h2>
|
|
772
|
+
</div>
|
|
773
|
+
<ul v-if="docApp.searchResponse && docApp.searchResponse.booksInfo && docApp.searchResponse.booksInfo.length">
|
|
774
|
+
<li class="searchFilterProducts mt-2">
|
|
775
|
+
<div class="form-check fs-5">
|
|
776
|
+
<input v-model="docApp.searchFilterBySpecificBookId" class="form-check-input" v-on:change="filterSearchByBook({bookId:'',title:''},$event)" type="radio" value="" id="all_books">
|
|
777
|
+
<label class="form-check-label fs-6 c-pointer" for="all_books">
|
|
778
|
+
All
|
|
779
|
+
</label>
|
|
780
|
+
</div>
|
|
781
|
+
</li>
|
|
782
|
+
<template v-for="(book,index) in docApp.searchResponse.booksInfo" :key="book.bookId">
|
|
783
|
+
<li v-if="!book.productFilterDisable || book.bookId===docApp.searchFilterBySpecificBookId" class="searchFilterProducts mt-2">
|
|
784
|
+
<div class="form-check fs-5">
|
|
785
|
+
<input v-model="docApp.searchFilterBySpecificBookId" class="form-check-input" v-on:change="filterSearchByBook(book,$event)" :disabled="book.productFilterDisable" type="radio" :value="book.bookId" :id="book.bookId">
|
|
786
|
+
<div style="width: 85%;display: inline-flex;">
|
|
787
|
+
<label :title="book.title" class="overflow-ellipsis form-check-label fs-6 c-pointer" :for="book.bookId">
|
|
788
|
+
{{book.title}}
|
|
789
|
+
</label>
|
|
790
|
+
</div>
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
<span class="badge rounded-pill fw-light fs-7 float-end" :class="{'bg-primary':docApp.searchFilterBySpecificBookId===book.bookId,'bg-secondary':docApp.searchFilterBySpecificBookId!==book.bookId}">
|
|
794
|
+
<span v-if="!book.productFilterDisable">{{book.foundCount}}</span>
|
|
795
|
+
<span v-if="book.productFilterDisable">0</span>
|
|
796
|
+
</span>
|
|
797
|
+
|
|
798
|
+
</div>
|
|
799
|
+
</li>
|
|
800
|
+
</template>
|
|
801
|
+
</ul>
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
<!-- product filter -->
|
|
805
|
+
<div class="mt-3 border-top">
|
|
806
|
+
<h2 class="mt-3 fw-bold fs-5">Products</h2>
|
|
807
|
+
</div>
|
|
808
|
+
<ul>
|
|
809
|
+
<template v-for="(product,index) in docApp.products" :key="product.id">
|
|
810
|
+
<li v-if="docApp.searchFilteredProducts[product.id] && docApp.searchFilteredProducts[product.id].returnedCount" class="searchFilterProducts mt-2">
|
|
811
|
+
|
|
812
|
+
<div class="form-check fs-5">
|
|
813
|
+
<input class="form-check-input" v-on:change="filterSearchByProducts(product,$event)" type="checkbox" :value="product.id" :checked="product.checked" :id="product.id">
|
|
814
|
+
<div style="width: 85%;display: inline-flex;">
|
|
815
|
+
<label :title="product.name" class="overflow-ellipsis form-check-label fs-6 c-pointer" :for="product.id">
|
|
816
|
+
{{product.name}}
|
|
817
|
+
</label>
|
|
818
|
+
</div>
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
<span v-if="docApp.searchFilteredProducts[product.id]"
|
|
822
|
+
class="badge rounded-pill fw-light fs-7 float-end"
|
|
823
|
+
:class="{'bg-primary':docApp.searchFilteredProducts[product.id].checked,'bg-secondary':!docApp.searchFilteredProducts[product.id].checked}">{{docApp.searchFilteredProducts[product.id].returnedCount}}
|
|
824
|
+
</span>
|
|
825
|
+
|
|
826
|
+
</div>
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
</li>
|
|
830
|
+
</template>
|
|
831
|
+
</ul>
|
|
832
|
+
</aside><!-- left hannd filter options on product (and other options like tags?) -->
|
|
833
|
+
|
|
834
|
+
<!-- right hand search results-->
|
|
835
|
+
<div class="hb-container-expand hb-scroll">
|
|
836
|
+
|
|
837
|
+
<div v-if="docApp.searchError" class="p-3 searchResultsContainer">
|
|
838
|
+
<div class="mb-2 alert alert-danger">{{docApp.searchError}}</div>
|
|
839
|
+
</div>
|
|
840
|
+
|
|
841
|
+
<div v-if="docApp.altSearchText" class="p-3 searchResultsContainer">
|
|
842
|
+
<div>
|
|
843
|
+
<div v-if="docApp.hasFilterByProducts" class="mb-2">
|
|
844
|
+
<template v-for="(product,index) in docApp.products" :key="product.id"><span v-if="docApp.searchFilteredProducts[product.id] && docApp.searchFilteredProducts[product.id].checked" v-on:click="filterSearchByProducts(product,$event)" class="fs-7 p-2 me-2 badge bg-secondary c-pointer fw-normal">{{product.name}} <i class="ml-2 pt-4 bi bi-x"></i></span></template>
|
|
845
|
+
</div>
|
|
846
|
+
|
|
847
|
+
<!-- one-character query: nothing was sent (a single unquoted character can
|
|
848
|
+
only match noise, so searchDocuments gates on length) and the previous
|
|
849
|
+
answer has been cleared, so say why the results area is empty instead
|
|
850
|
+
of showing a blank panel -->
|
|
851
|
+
<h3 class="pb-4 border-bottom fw-bold fs-5" v-if="docApp.searchTooShort && !docApp.boolSearchInProgress">
|
|
852
|
+
Keep typing
|
|
853
|
+
<span class="fw-lighter"> - a search needs at least two characters</span>
|
|
854
|
+
</h3>
|
|
855
|
+
|
|
856
|
+
<h3 class="pb-4 border-bottom fw-bold fs-5" v-if="docApp.boolSearchInProgress">
|
|
857
|
+
Searching
|
|
858
|
+
<span v-if="docApp.searchFilterBySpecificBookId && docApp.searchFilterBySpecificBookTitle"> in {{docApp.searchFilterBySpecificBookTitle}}</span>
|
|
859
|
+
</h3>
|
|
860
|
+
|
|
861
|
+
<!-- narrow viewports only: the filter sidebar is hidden below
|
|
862
|
+
1020px and this view has no burger, so without this the
|
|
863
|
+
filters cannot be reached at all -->
|
|
864
|
+
<button type="button" class="search-filter-btn btn btn-sm btn-outline-secondary mb-3" v-on:click="toggleSearchFilters()" aria-label="Show search filters">
|
|
865
|
+
<i class="bi bi-funnel"></i> Filters
|
|
866
|
+
</button>
|
|
867
|
+
|
|
868
|
+
<h3 class="pb-4 border-bottom fw-bold fs-5" v-if="docApp.searchResponse && docApp.currentResultsSearchText" :class="{'is-searching': docApp.boolSearchInProgress}">
|
|
869
|
+
<!-- while a query is in flight the list below still holds the PREVIOUS
|
|
870
|
+
results, so don't put a count against the new search term -->
|
|
871
|
+
<template v-if="docApp.boolSearchInProgress">Searching for:</template>
|
|
872
|
+
<template v-else><span>{{docApp.searchResultFilteredItems.length}}</span> results for:</template>
|
|
873
|
+
<!-- the leading entities are load-bearing: Vue 3's whitespace
|
|
874
|
+
condensing strips the newline-indent between these elements, which
|
|
875
|
+
rendered as "for:email" -->
|
|
876
|
+
<span class="fw-lighter"> {{docApp.currentResultsSearchText}}</span>
|
|
877
|
+
<span v-if="docApp.searchFilterBySpecificBookId && docApp.searchFilterBySpecificBookTitle"> in {{docApp.searchFilterBySpecificBookTitle}}</span>
|
|
878
|
+
<!-- informational, not an error - themed text-3 rather than bootstrap's text-danger red -->
|
|
879
|
+
<div class="fw-normal pt-2 HTL-fs-sm" style="color:var(--htl-c-text-3)" v-if="docApp.searchResponse && docApp.searchResponse.returnedResultsLimited">Found {{docApp.searchResponse.totalResultsAvailable}} matches. Showing the top {{docApp.searchResponse.results ? docApp.searchResponse.results.length : 0}}. Use a more specific search to further narrow down the results.</div>
|
|
880
|
+
<!-- content-type rescue. The filter defaults to Documentation, so it
|
|
881
|
+
must never dead-end a search that has an answer elsewhere: when the
|
|
882
|
+
filter would show nothing we widen automatically and say so. No
|
|
883
|
+
"show all" offer beyond that - the server caps results at 300, so a
|
|
884
|
+
link promising the full hidden count could never deliver it; the
|
|
885
|
+
Information type facet already shows what is being hidden -->
|
|
886
|
+
<div class="fw-normal pt-2 HTL-fs-sm" style="color:var(--htl-c-text-3)" v-if="docApp.searchDocTypeRescue && docApp.searchDocTypeRescue.auto">
|
|
887
|
+
No documentation pages match this search, so all content types are shown.
|
|
888
|
+
<span v-if="docApp.searchDocTypeRescue.best">Most matches are in {{docApp.searchDocTypeRescue.best.name}}.</span>
|
|
889
|
+
</div>
|
|
890
|
+
<!-- unknown bid in a pasted/stale url: the filter was cleared
|
|
891
|
+
automatically (see evaluateBookFilterRescue) - explain, and show
|
|
892
|
+
the bad id so the user can see what was wrong with the link -->
|
|
893
|
+
<div class="fw-normal pt-2 HTL-fs-sm" style="color:var(--htl-c-text-3)" v-if="docApp.searchBookRescue">
|
|
894
|
+
There is no book with the ID "{{docApp.searchBookRescue.bid}}", so the book filter from this link was ignored and results from all books are shown.
|
|
895
|
+
</div>
|
|
896
|
+
</h3>
|
|
897
|
+
|
|
898
|
+
<span class="text-danger" v-if="!docApp.boolSearchInProgress && docApp.searchResultFilteredItems.length == 0 && docApp.validSearch">You have an odd number of " characters in your search terms - each one needs closing with a matching " character!</span>
|
|
899
|
+
</div>
|
|
900
|
+
|
|
901
|
+
<!-- Search-in-progress placeholder. Shown only when there is nothing to keep on
|
|
902
|
+
screen - a first search, or one that returned nothing. It used to replace the
|
|
903
|
+
results list on EVERY query, which destroyed and rebuilt all 300 rows per
|
|
904
|
+
search (3700-5800 elements churned) and made the :key on the results list
|
|
905
|
+
worthless, since no row ever survived to be reused. The list now stays
|
|
906
|
+
mounted and is dimmed via .is-searching instead. -->
|
|
907
|
+
<ul class="searchResults mb-4" v-if="docApp.boolSearchInProgress && !docApp.searchResultFilteredItems.length">
|
|
908
|
+
<!-- was two blocks gated on ThemePreference=='dark' / !=='dark' with byte-identical
|
|
909
|
+
markup, so the condition never changed the output - the skeleton is themed
|
|
910
|
+
purely by CSS. Collapsed to one loop, which also removes a v-if and v-for on
|
|
911
|
+
the same element (Vue 3 evaluates v-if first). -->
|
|
912
|
+
<li class="p-3" v-for="index in 8" :key="index">
|
|
913
|
+
<h2 class="title fs-5 mb-2 col-5"><div class="HTL-skeleton HTL-skeleton-text HTL-skeleton-text-100"></div></h2>
|
|
914
|
+
<div class="HTL-fs-sm mb-1 col-7"><div class="HTL-skeleton HTL-skeleton-text HTL-skeleton-text-100"></div></div>
|
|
277
915
|
</li>
|
|
278
916
|
</ul>
|
|
279
|
-
|
|
280
|
-
|
|
917
|
+
|
|
918
|
+
<!-- actual search results -->
|
|
919
|
+
<ul v-if="docApp.currentResultsSearchText && docApp.searchResultFilteredItems && docApp.searchResultFilteredItems.length"
|
|
920
|
+
class="searchResults searchResultsList"
|
|
921
|
+
:class="{'is-searching': docApp.boolSearchInProgress}"
|
|
922
|
+
:aria-busy="docApp.boolSearchInProgress ? 'true' : 'false'">
|
|
923
|
+
<li class="p-3" v-for="(resultItem,index) in docApp.searchResultFilteredItems" :key="resultItem.url">
|
|
924
|
+
<h2 class="title fs-5 mb-2"><a v-bind:href="resultItem.url" :bookid="resultItem.bookId" class="searchresult-href">{{resultItem.title}}</a>
|
|
925
|
+
<!-- which search channel(s) matched this result: keyword (FTS5), semantic (vector), or both.
|
|
926
|
+
Internal/dev only (docApp.showSearchMatchBadges) - never shown on the public site -->
|
|
927
|
+
<span v-if="docApp.showSearchMatchBadges && resultItem.matchedBy" class="search-match-badge" :class="'search-match-' + resultItem.matchedBy"
|
|
928
|
+
:title="resultItem.matchedBy === 'semantic' ? 'Found by semantic (meaning-based) search' : (resultItem.matchedBy === 'both' ? 'Found by both keyword and semantic search' : 'Found by keyword search')"
|
|
929
|
+
>{{resultItem.matchedBy === 'semantic' ? 'semantic' : (resultItem.matchedBy === 'both' ? 'keyword + semantic' : 'keyword')}}</span>
|
|
930
|
+
</h2>
|
|
931
|
+
<div class="text-success HTL-fs-sm">{{resultItem.url}}</div>
|
|
932
|
+
<p class="HTL-fs-sm" v-html="resultItem.content"></p>
|
|
281
933
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
934
|
+
</li>
|
|
935
|
+
</ul>
|
|
936
|
+
</div>
|
|
937
|
+
</div><!-- right hand search results-->
|
|
286
938
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
939
|
+
</div>
|
|
940
|
+
</div>
|
|
941
|
+
|
|
942
|
+
<!-- container showing full library of contents -->
|
|
943
|
+
<div v-if="!docApp.mcpCatalogMode && !docApp.searchMode && !docApp.loadingBookInfo && !docApp.book.docId && docApp.selectedLibraryGroup" class="LibraryContent hb-container-expand hb-container-vertical" id="LibraryContent">
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
<div class="hb-container-expand hb-scroll">
|
|
947
|
+
|
|
948
|
+
<div class="position-relative">
|
|
949
|
+
<div v-if="!docApp.libraryHomeViewProduct" class="position-absolute top-0 end-0 mt-2 me-2">
|
|
950
|
+
<div class="dropdown">
|
|
951
|
+
<button class="btn btn btn-light dropdown-toggle" type="button" id="dropdownMenu2" data-bs-toggle="dropdown" aria-expanded="false">
|
|
952
|
+
<i class="bi bi-filter"></i> {{docApp.selectedLibraryGroup.title}}
|
|
953
|
+
</button>
|
|
954
|
+
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
|
955
|
+
<li v-for="(groupMode,index) in docApp.groupModes " :key="groupMode.mode"><button v-on:click="organiseLibraryByGroup(groupMode,true)" class="dropdown-item" type="button">{{groupMode.title}}</button></li>
|
|
956
|
+
</ul>
|
|
290
957
|
</div>
|
|
291
958
|
</div>
|
|
959
|
+
</div>
|
|
292
960
|
|
|
293
|
-
<div class="
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
</
|
|
309
|
-
|
|
310
|
-
<a class="link c-pointer">(n) contributors</a>
|
|
311
|
-
</li>
|
|
312
|
-
</ul>
|
|
961
|
+
<div class="library-content-panel">
|
|
962
|
+
<!-- show specific product books in columns (when have more than 8 books in a pproduct)-->
|
|
963
|
+
<div v-if="docApp.libraryHomeViewProduct">
|
|
964
|
+
<div v-if="docApp.libraryHomeViewProduct.books.length" class="pad-20 mt-3">
|
|
965
|
+
|
|
966
|
+
<div class="lib-header mb-4">
|
|
967
|
+
<h1 class="mb-2">{{docApp.libraryHomeViewProduct.title || docApp.libraryHomeViewProduct.id}}</h1>
|
|
968
|
+
<p>{{docApp.libraryHomeViewProduct.description}}</p>
|
|
969
|
+
</div>
|
|
970
|
+
|
|
971
|
+
<!-- break list of books into 3 columns -->
|
|
972
|
+
<ol class="document-list list-unstyled card-columns">
|
|
973
|
+
<li v-for="(book, index) in docApp.libraryHomeViewProduct.books" :key="book.docId">
|
|
974
|
+
<a :href="book.docId" class="title" v-on:click="loadLibraryBook(book,false,$event)" style="display:inline-block">{{book.title}}</a>
|
|
975
|
+
</li>
|
|
976
|
+
</ol>
|
|
977
|
+
</div>
|
|
313
978
|
</div>
|
|
314
|
-
|
|
979
|
+
|
|
980
|
+
<!-- group lib items by list columns of 3 - will shrink to 2 and then 1 on mobile -->
|
|
981
|
+
<div v-if="!docApp.libraryHomeViewProduct && docApp.selectedLibraryGroup.layout=='column-list'">
|
|
982
|
+
<template v-for="(group, groupKey) in docApp.libraryGroups" :key="groupKey">
|
|
983
|
+
<div v-if="group.books.length" class="pad-20 mt-3">
|
|
984
|
+
|
|
985
|
+
<div class="lib-header mb-4">
|
|
986
|
+
<h1 class="mb-2">{{group.title || group.id}}</h1>
|
|
987
|
+
<p>{{group.description}}</p>
|
|
988
|
+
</div>
|
|
989
|
+
|
|
990
|
+
<!-- break list of books into 3 columns -->
|
|
991
|
+
<ol class="document-list list-unstyled card-columns">
|
|
992
|
+
<li v-for="(book, index) in group.books" :key="book.docId">
|
|
993
|
+
<a :href="book.docId" class="title" v-on:click="loadLibraryBook(book,false,$event)" style="display:inline-block">{{book.title}}</a>
|
|
994
|
+
</li>
|
|
995
|
+
</ol>
|
|
996
|
+
</div>
|
|
997
|
+
</template>
|
|
315
998
|
</div>
|
|
999
|
+
|
|
1000
|
+
<!-- grouping by tiles -->
|
|
1001
|
+
<div v-if="!docApp.libraryHomeViewProduct && docApp.selectedLibraryGroup.layout=='tiles'" class="columns-container mt-5">
|
|
1002
|
+
|
|
1003
|
+
<div class="columns">
|
|
1004
|
+
|
|
1005
|
+
<template v-for="(group, groupKey) in docApp.libraryGroups" :key="groupKey">
|
|
1006
|
+
<div v-if="group.books.length" class="column pad-20">
|
|
1007
|
+
<div class="h-100 w-100 d-inline-block shadow-sm border pad-20">
|
|
1008
|
+
|
|
1009
|
+
<!-- flex header: icon and title share a centreline, divider separates the
|
|
1010
|
+
header from the book list (product-tile-* in htl-library.css). The
|
|
1011
|
+
background shorthand must include "/ contain" - the shorthand resets
|
|
1012
|
+
background-size, so a stylesheet rule cannot win against this inline
|
|
1013
|
+
!important -->
|
|
1014
|
+
<div class="product-tile-header">
|
|
1015
|
+
<i class="product-tile-icon" :style="'background: url('+group.icon+') no-repeat left center / contain !important'"></i>
|
|
1016
|
+
<h2 class="mb-0">{{group.title}}</h2>
|
|
1017
|
+
</div>
|
|
1018
|
+
|
|
1019
|
+
<template v-for="(book, index) in group.books" :key="book.docId">
|
|
1020
|
+
<div v-if="index<8" class="d-block">
|
|
1021
|
+
<div class="hb-container-horizontal">
|
|
1022
|
+
<!-- hb-center-v, not a magic margin-top: centres the chevron on
|
|
1023
|
+
the row whatever the text size -->
|
|
1024
|
+
<span class="hb-center-v">
|
|
1025
|
+
<i class="bi bi-chevron-right ms-2 book-link-chevron"></i>
|
|
1026
|
+
</span>
|
|
1027
|
+
<span class="align-middle hb-container-expand">
|
|
1028
|
+
<a :href="book.docId" class="title" v-on:click="loadLibraryBook(book,false,$event)">{{book.title}}</a>
|
|
1029
|
+
</span>
|
|
1030
|
+
|
|
1031
|
+
</div>
|
|
1032
|
+
</div>
|
|
1033
|
+
</template>
|
|
1034
|
+
<div class="d-block" v-if="group.books.length>7">
|
|
1035
|
+
<div class="hb-container-horizontal">
|
|
1036
|
+
<span class="hb-center-v">
|
|
1037
|
+
<i class="bi bi-chevron-right ms-2 book-link-chevron"></i>
|
|
1038
|
+
</span>
|
|
1039
|
+
<span class="align-middle hb-container-expand">
|
|
1040
|
+
<a class="title" v-on:click="loadProductHome(group,true)" :aria-label="'View all ' + group.title + ' documentation'">More...</a>
|
|
1041
|
+
</span>
|
|
1042
|
+
</div>
|
|
1043
|
+
</div>
|
|
1044
|
+
|
|
1045
|
+
</div>
|
|
1046
|
+
|
|
1047
|
+
</div>
|
|
1048
|
+
</template>
|
|
1049
|
+
|
|
1050
|
+
</div>
|
|
1051
|
+
</div>
|
|
316
1052
|
</div>
|
|
317
1053
|
|
|
318
|
-
|
|
319
|
-
<div class="document-body"></div>
|
|
1054
|
+
|
|
320
1055
|
</div>
|
|
1056
|
+
</div>
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
<!-- document view -->
|
|
1060
|
+
<!-- the left hand nav col -->
|
|
1061
|
+
<aside v-show="docApp.book.docId" class="DocSidebar hb-container-vertical" :class="{'open': docApp.bookNavOpen}">
|
|
1062
|
+
|
|
1063
|
+
<nav class="hdoc-nav hb-container-expand hb-scroll" id="DocSidebarNav" aria-label="Documentation navigation" tabindex="-1">
|
|
1064
|
+
<div v-for="(navSection,index) in docApp.navSections" v-bind:class="{'group':navSection.items, 'nogrouplink':!navSection.items}">
|
|
1065
|
+
|
|
1066
|
+
<!-- v-if/v-else, not two v-shows: these branches are mutually exclusive,
|
|
1067
|
+
so v-show rendered BOTH for every top-level nav entry and hid one. //-- SG -->
|
|
1068
|
+
<div v-if="!navSection.items">
|
|
1069
|
+
<a class="DocLink link noitems" v-bind:href="navSection.link" v-bind:target="navSection.target || ''" style="padding-left: 0px;">
|
|
1070
|
+
<span class="link-text">{{navSection.text}} <i v-if="navSection.target" class="bi bi-box-arrow-up-right"></i> </span>
|
|
1071
|
+
</a>
|
|
1072
|
+
</div>
|
|
321
1073
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
1074
|
+
<!-- No :key="updateCounter" here any more. Changing that key destroyed and
|
|
1075
|
+
recreated the ENTIRE recursive nav tree on every navigation - measured at
|
|
1076
|
+
1026 elements torn down and 1026 rebuilt, ~13ms, producing a tree identical
|
|
1077
|
+
to the one just discarded. It existed because `expand` used to be
|
|
1078
|
+
non-reactive; it is seeded reactively in renderNavigation() now, so
|
|
1079
|
+
expanding a group patches in place. //-- SG -->
|
|
1080
|
+
<div v-else>
|
|
1081
|
+
<nav-section-component v-bind:asection="navSection"></nav-section-component>
|
|
1082
|
+
</div>
|
|
327
1083
|
</div>
|
|
328
|
-
</
|
|
329
|
-
|
|
330
|
-
</
|
|
331
|
-
|
|
1084
|
+
</nav>
|
|
1085
|
+
|
|
1086
|
+
</aside>
|
|
1087
|
+
|
|
1088
|
+
<div v-show="docApp.book.docId" class="DocContent hb-container-expand hb-scroll hb-container-vertical" id="DocContent" :class="docApp.layoutClass" tabindex="-1">
|
|
1089
|
+
|
|
1090
|
+
<div class="HTL-doc content-wrapper hb-container-expand" :class="{'hb-content-loading': docApp.contentLoading}">
|
|
1091
|
+
|
|
1092
|
+
<div class="injected-document-container hb-container-horizontal">
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
<div class="injected-document-content">
|
|
1096
|
+
<div class="vue-document-body"></div>
|
|
1097
|
+
<div class="document-body"><!--[[HDOC-CONTENT]]--></div>
|
|
1098
|
+
<div class="document-footer" :class="{'hb-content-loading': docApp.contentLoading}">
|
|
1099
|
+
<div class="hb-center-v">
|
|
1100
|
+
<small>
|
|
1101
|
+
<ul class="ps-0 nav-bar-nav-list noeffects after-bullets overflow-ellipsis text-light-2">
|
|
1102
|
+
<li class="ps-0 mt-0 nav-bar-item" aria-label="Version">Version {{docApp.book.version}}</li>
|
|
1103
|
+
<li class="ps-0 mt-0 nav-bar-item" aria-label="Node/Build">Node {{docApp.node}} / {{docApp.build}}</li>
|
|
1104
|
+
</ul>
|
|
1105
|
+
</small>
|
|
1106
|
+
</div>
|
|
1107
|
+
</div>
|
|
1108
|
+
</div>
|
|
1109
|
+
|
|
1110
|
+
<div v-if="docApp.keepTocLayout" class="injected-document-toc">
|
|
1111
|
+
<div v-if="docApp.tableOfContents.length" class="panel-header">
|
|
1112
|
+
<span class="icon">
|
|
1113
|
+
<i class="bi bi-list-nested"></i>
|
|
1114
|
+
</span>
|
|
1115
|
+
<span class="title">In This Document</span>
|
|
1116
|
+
</div>
|
|
1117
|
+
<div v-if="docApp.tableOfContents.length" class="panel-content">
|
|
1118
|
+
<div v-for="tocItem in docApp.tableOfContents" :key="tocItem.href" v-bind:class="{'ms-3':tocItem.tagName==='H3'}">
|
|
1119
|
+
<a :href="tocItem.href">{{tocItem.eleText}}</a>
|
|
1120
|
+
</div>
|
|
1121
|
+
</div>
|
|
1122
|
+
</div>
|
|
332
1123
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
1124
|
+
<!-- narrow-screen TOC: floating button + slide-in panel (shown below the inline-TOC breakpoint) -->
|
|
1125
|
+
<div v-if="docApp.keepTocLayout && docApp.tableOfContents.length" class="toc-mobile">
|
|
1126
|
+
<button type="button" class="toc-mobile-btn" v-on:click.stop="toggleTocMenu()" :aria-expanded="docApp.tocMobileOpen ? 'true' : 'false'" aria-controls="tocMobilePanel" aria-label="Toggle table of contents">
|
|
1127
|
+
<i class="bi" v-bind:class="docApp.tocMobileOpen ? 'bi-x-lg' : 'bi-list-nested'" aria-hidden="true"></i>
|
|
1128
|
+
</button>
|
|
1129
|
+
<div class="toc-mobile-backdrop" v-show="docApp.tocMobileOpen" v-on:click="closeTocMenu()"></div>
|
|
1130
|
+
<nav id="tocMobilePanel" class="toc-mobile-panel" v-bind:class="{open:docApp.tocMobileOpen}" aria-label="Table of contents">
|
|
1131
|
+
<div class="panel-header">
|
|
1132
|
+
<span class="icon">
|
|
1133
|
+
<i class="bi bi-list-nested" aria-hidden="true"></i>
|
|
1134
|
+
</span>
|
|
1135
|
+
<span class="title">In This Document</span>
|
|
1136
|
+
</div>
|
|
1137
|
+
<div class="panel-content">
|
|
1138
|
+
<div v-for="tocItem in docApp.tableOfContents" :key="tocItem.href" v-bind:class="{'ms-3':tocItem.tagName==='H3'}">
|
|
1139
|
+
<a :href="tocItem.href" v-on:click="closeTocMenu()">{{tocItem.eleText}}</a>
|
|
1140
|
+
</div>
|
|
1141
|
+
</div>
|
|
1142
|
+
</nav>
|
|
1143
|
+
</div>
|
|
345
1144
|
</div>
|
|
346
1145
|
</div>
|
|
347
1146
|
</div>
|
|
1147
|
+
|
|
1148
|
+
</div>
|
|
1149
|
+
</div>
|
|
348
1150
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
</div>
|
|
1151
|
+
|
|
1152
|
+
<!-- Contributors modal. The trigger is NOT in this file - the SERVER-RENDERED article
|
|
1153
|
+
header emits <a data-bs-target="#contributersModal">N contributors</a> inside
|
|
1154
|
+
.document-body, so a grep of this tree finds no opener. Do not remove this as
|
|
1155
|
+
"dead"; it looked exactly that way once already. aria-labelledby points at the
|
|
1156
|
+
TITLE's id - the h1 must not reuse the dialog's own id. -->
|
|
1157
|
+
<div v-if="docApp.bookInfo && docApp.bookInfo.contributors && docApp.bookInfo.contributors.length" class="modal fade" id="contributersModal" tabindex="-1" aria-labelledby="contributersModalTitle" aria-hidden="true">
|
|
1158
|
+
<div class="modal-dialog modal-dialog-scrollable">
|
|
1159
|
+
<div class="modal-content">
|
|
1160
|
+
<div class="modal-header border-bottom">
|
|
1161
|
+
<h1 class="modal-title fs-5" id="contributersModalTitle">Contributors to this article</h1>
|
|
361
1162
|
</div>
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
<div class="p-2">
|
|
371
|
-
<div v-for="(navSectionItem,index) in docApp.inlineDocumentItems">
|
|
372
|
-
|
|
373
|
-
<a class="DocLink link noitems" href="#" v-on:click="popupInlineDocument($event,navSectionItem)" style="padding-left: 0px;">
|
|
374
|
-
<span class="link-text"><i v-if="navSectionItem.draft" class="bi bi-exclamation-triangle-fill text-danger" title="This page is set to draft."></i><i class="bi bi-files-alt text-warning"></i> {{navSectionItem.text}}</span>
|
|
1163
|
+
<div class="modal-body">
|
|
1164
|
+
<ul class="border rounder is-vertically-scrollable">
|
|
1165
|
+
<li v-for="(person,$index) in docApp.bookInfo.contributors" :key="person.html_url || person.name" class="display-flex p-2 border-bottom">
|
|
1166
|
+
<a aria-hidden="true" class="rounded-circle" tabindex="-1" v-bind:href="person.html_url" v-bind:title="person.name">
|
|
1167
|
+
<div class="d-inline-block">
|
|
1168
|
+
<img width="32px" alt="" v-bind:src="person.avatar_url+'&size=32'">
|
|
1169
|
+
</div>
|
|
375
1170
|
</a>
|
|
376
|
-
|
|
377
|
-
</
|
|
378
|
-
</
|
|
1171
|
+
<a class="align-self-center padding-left-xs ms-2" v-bind:href="person.html_url">{{person.name}}</a>
|
|
1172
|
+
</li>
|
|
1173
|
+
</ul>
|
|
379
1174
|
</div>
|
|
380
1175
|
</div>
|
|
381
|
-
|
|
382
|
-
|
|
383
1176
|
</div>
|
|
384
1177
|
</div>
|
|
385
1178
|
|
|
386
|
-
</div>
|
|
387
|
-
|
|
388
|
-
</div> <!-- <div class="main hb-vertical-expand"> -->
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
</body>
|
|
393
|
-
<script>
|
|
394
|
-
|
|
395
|
-
//-- we load these like this as we have set the BASE location of the document - so have to load these after doc
|
|
396
|
-
loadCss([
|
|
397
|
-
"css/theme-default/styles/fonts.css",
|
|
398
|
-
"css/theme-default/styles/vars.css",
|
|
399
|
-
"css/theme-default/styles/base.css",
|
|
400
|
-
"css/theme-default/styles/htldoc.layouts.css",
|
|
401
|
-
|
|
402
|
-
"css/theme-default/styles/components/htl-doc.css",
|
|
403
|
-
"css/theme-default/styles/components/sidebar.css",
|
|
404
|
-
"css/theme-default/styles/components/content.css",
|
|
405
|
-
"css/theme-default/styles/components/custom-block.css",
|
|
406
|
-
"css/theme-default/styles/components/api-doc.css",
|
|
407
|
-
|
|
408
|
-
"js/highlightjs/styles/vs2015.css"
|
|
409
|
-
], function () {
|
|
410
|
-
//-- required js + 3rd party libs
|
|
411
|
-
setTimeout(function () {
|
|
412
|
-
loadJS(["js/doc.hornbill.js", "js/highlightjs/highlight.pack.js", "js/highlightjs-badge.js"], function () {
|
|
413
|
-
intialiseApp();
|
|
414
|
-
});
|
|
415
|
-
}, 50);
|
|
416
|
-
});
|
|
1179
|
+
</div> <!-- <div vDocDevApp> -->
|
|
1180
|
+
|
|
417
1181
|
|
|
418
|
-
</script>
|
|
419
1182
|
|
|
420
|
-
</
|
|
1183
|
+
</body>
|
|
1184
|
+
</html>
|