Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 14f3a57

Browse files
author
Emir Aganovic
committedAug 14, 2024·
feat: site test
1 parent 3a416ae commit 14f3a57

27 files changed

+6588
-4
lines changed
 

‎.gitignore

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*
2-
3-
!.gitignore
4-
!README.md
1+
/README.md
2+
/diago-site
3+
*.svg
4+
*.png

‎404.html

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<div style='font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; height:100vh; text-align:center; display:flex; flex-direction:column; align-items:center; justify-content:center'>
2+
<div>
3+
<style>
4+
body {
5+
color: #000;
6+
background: #fff;
7+
margin: 0;
8+
}
9+
.next-error-h1 {
10+
border-right: 1px solid rgba(0, 0, 0, 0.3);
11+
}
12+
@media (prefers-color-scheme: dark) {
13+
body {
14+
color: #fff;
15+
background: #000;
16+
}
17+
.next-error-h1 {
18+
border-right: 1px solid rgba(255, 255, 255, 0.3);
19+
}
20+
}
21+
</style>
22+
<h1 class="next-error-h1" style='display: inline-block; margin: 0 20px 0 0; padding-right: 23px; font-size: 24px; font-weight: 500; vertical-align: top; line-height: 49px; font-feature-settings: "rlig" 1,"calt" 1,"ss01" 1,"ss06" 1 !important;'>404</h1>
23+
<div style="display: inline-block; text-align: left">
24+
<h2 style="font-size: 14px; font-weight: 400; line-height: 49px; margin: 0">This page could not be found.</h2>
25+
</div>
26+
</div>
27+
</div>

‎LICENSE

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
Apache License
2+
Version 2.0, January 2004
3+
http://www.apache.org/licenses/
4+
5+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6+
7+
1. Definitions.
8+
9+
"License" shall mean the terms and conditions for use, reproduction,
10+
and distribution as defined by Sections 1 through 9 of this document.
11+
12+
"Licensor" shall mean the copyright owner or entity authorized by
13+
the copyright owner that is granting the License.
14+
15+
"Legal Entity" shall mean the union of the acting entity and all
16+
other entities that control, are controlled by, or are under common
17+
control with that entity. For the purposes of this definition,
18+
"control" means (i) the power, direct or indirect, to cause the
19+
direction or management of such entity, whether by contract or
20+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
21+
outstanding shares, or (iii) beneficial ownership of such entity.
22+
23+
"You" (or "Your") shall mean an individual or Legal Entity
24+
exercising permissions granted by this License.
25+
26+
"Source" form shall mean the preferred form for making modifications,
27+
including but not limited to software source code, documentation
28+
source, and configuration files.
29+
30+
"Object" form shall mean any form resulting from mechanical
31+
transformation or translation of a Source form, including but
32+
not limited to compiled object code, generated documentation,
33+
and conversions to other media types.
34+
35+
"Work" shall mean the work of authorship, whether in Source or
36+
Object form, made available under the License, as indicated by a
37+
copyright notice that is included in or attached to the work
38+
(an example is provided in the Appendix below).
39+
40+
"Derivative Works" shall mean any work, whether in Source or Object
41+
form, that is based on (or derived from) the Work and for which the
42+
editorial revisions, annotations, elaborations, or other modifications
43+
represent, as a whole, an original work of authorship. For the purposes
44+
of this License, Derivative Works shall not include works that remain
45+
separable from, or merely link (or bind by name) to the interfaces of,
46+
the Work and Derivative Works thereof.
47+
48+
"Contribution" shall mean any work of authorship, including
49+
the original version of the Work and any modifications or additions
50+
to that Work or Derivative Works thereof, that is intentionally
51+
submitted to Licensor for inclusion in the Work by the copyright owner
52+
or by an individual or Legal Entity authorized to submit on behalf of
53+
the copyright owner. For the purposes of this definition, "submitted"
54+
means any form of electronic, verbal, or written communication sent
55+
to the Licensor or its representatives, including but not limited to
56+
communication on electronic mailing lists, source code control systems,
57+
and issue tracking systems that are managed by, or on behalf of, the
58+
Licensor for the purpose of discussing and improving the Work, but
59+
excluding communication that is conspicuously marked or otherwise
60+
designated in writing by the copyright owner as "Not a Contribution."
61+
62+
"Contributor" shall mean Licensor and any individual or Legal Entity
63+
on behalf of whom a Contribution has been received by Licensor and
64+
subsequently incorporated within the Work.
65+
66+
2. Grant of Copyright License. Subject to the terms and conditions of
67+
this License, each Contributor hereby grants to You a perpetual,
68+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69+
copyright license to reproduce, prepare Derivative Works of,
70+
publicly display, publicly perform, sublicense, and distribute the
71+
Work and such Derivative Works in Source or Object form.
72+
73+
3. Grant of Patent License. Subject to the terms and conditions of
74+
this License, each Contributor hereby grants to You a perpetual,
75+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76+
(except as stated in this section) patent license to make, have made,
77+
use, offer to sell, sell, import, and otherwise transfer the Work,
78+
where such license applies only to those patent claims licensable
79+
by such Contributor that are necessarily infringed by their
80+
Contribution(s) alone or by combination of their Contribution(s)
81+
with the Work to which such Contribution(s) was submitted. If You
82+
institute patent litigation against any entity (including a
83+
cross-claim or counterclaim in a lawsuit) alleging that the Work
84+
or a Contribution incorporated within the Work constitutes direct
85+
or contributory patent infringement, then any patent licenses
86+
granted to You under this License for that Work shall terminate
87+
as of the date such litigation is filed.
88+
89+
4. Redistribution. You may reproduce and distribute copies of the
90+
Work or Derivative Works thereof in any medium, with or without
91+
modifications, and in Source or Object form, provided that You
92+
meet the following conditions:
93+
94+
(a) You must give any other recipients of the Work or
95+
Derivative Works a copy of this License; and
96+
97+
(b) You must cause any modified files to carry prominent notices
98+
stating that You changed the files; and
99+
100+
(c) You must retain, in the Source form of any Derivative Works
101+
that You distribute, all copyright, patent, trademark, and
102+
attribution notices from the Source form of the Work,
103+
excluding those notices that do not pertain to any part of
104+
the Derivative Works; and
105+
106+
(d) If the Work includes a "NOTICE" text file as part of its
107+
distribution, then any Derivative Works that You distribute must
108+
include a readable copy of the attribution notices contained
109+
within such NOTICE file, excluding those notices that do not
110+
pertain to any part of the Derivative Works, in at least one
111+
of the following places: within a NOTICE text file distributed
112+
as part of the Derivative Works; within the Source form or
113+
documentation, if provided along with the Derivative Works; or,
114+
within a display generated by the Derivative Works, if and
115+
wherever such third-party notices normally appear. The contents
116+
of the NOTICE file are for informational purposes only and
117+
do not modify the License. You may add Your own attribution
118+
notices within Derivative Works that You distribute, alongside
119+
or as an addendum to the NOTICE text from the Work, provided
120+
that such additional attribution notices cannot be construed
121+
as modifying the License.
122+
123+
You may add Your own copyright statement to Your modifications and
124+
may provide additional or different license terms and conditions
125+
for use, reproduction, or distribution of Your modifications, or
126+
for any such Derivative Works as a whole, provided Your use,
127+
reproduction, and distribution of the Work otherwise complies with
128+
the conditions stated in this License.
129+
130+
5. Submission of Contributions. Unless You explicitly state otherwise,
131+
any Contribution intentionally submitted for inclusion in the Work
132+
by You to the Licensor shall be under the terms and conditions of
133+
this License, without any additional terms or conditions.
134+
Notwithstanding the above, nothing herein shall supersede or modify
135+
the terms of any separate license agreement you may have executed
136+
with Licensor regarding such Contributions.
137+
138+
6. Trademarks. This License does not grant permission to use the trade
139+
names, trademarks, service marks, or product names of the Licensor,
140+
except as required for reasonable and customary use in describing the
141+
origin of the Work and reproducing the content of the NOTICE file.
142+
143+
7. Disclaimer of Warranty. Unless required by applicable law or
144+
agreed to in writing, Licensor provides the Work (and each
145+
Contributor provides its Contributions) on an "AS IS" BASIS,
146+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147+
implied, including, without limitation, any warranties or conditions
148+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149+
PARTICULAR PURPOSE. You are solely responsible for determining the
150+
appropriateness of using or redistributing the Work and assume any
151+
risks associated with Your exercise of permissions under this License.
152+
153+
8. Limitation of Liability. In no event and under no legal theory,
154+
whether in tort (including negligence), contract, or otherwise,
155+
unless required by applicable law (such as deliberate and grossly
156+
negligent acts) or agreed to in writing, shall any Contributor be
157+
liable to You for damages, including any direct, indirect, special,
158+
incidental, or consequential damages of any character arising as a
159+
result of this License or out of the use or inability to use the
160+
Work (including but not limited to damages for loss of goodwill,
161+
work stoppage, computer failure or malfunction, or any and all
162+
other commercial damages or losses), even if such Contributor
163+
has been advised of the possibility of such damages.
164+
165+
9. Accepting Warranty or Additional Liability. While redistributing
166+
the Work or Derivative Works thereof, You may choose to offer,
167+
and charge a fee for, acceptance of support, warranty, indemnity,
168+
or other liability obligations and/or rights consistent with this
169+
License. However, in accepting such obligations, You may act only
170+
on Your own behalf and on Your sole responsibility, not on behalf
171+
of any other Contributor, and only if You agree to indemnify,
172+
defend, and hold each Contributor harmless for any liability
173+
incurred by, or claims asserted against, such Contributor by reason
174+
of your accepting any such warranty or additional liability.
175+
176+
END OF TERMS AND CONDITIONS
177+
178+
APPENDIX: How to apply the Apache License to your work.
179+
180+
To apply the Apache License to your work, attach the following
181+
boilerplate notice, with the fields enclosed by brackets "[]"
182+
replaced with your own identifying information. (Don't include
183+
the brackets!) The text should be enclosed in the appropriate
184+
comment syntax for the file format. We also recommend that a
185+
file or class name and description of purpose be included on the
186+
same "printed page" as the copyright notice for easier
187+
identification within third-party archives.
188+
189+
Copyright [yyyy] [name of copyright owner]
190+
191+
Licensed under the Apache License, Version 2.0 (the "License");
192+
you may not use this file except in compliance with the License.
193+
You may obtain a copy of the License at
194+
195+
http://www.apache.org/licenses/LICENSE-2.0
196+
197+
Unless required by applicable law or agreed to in writing, software
198+
distributed under the License is distributed on an "AS IS" BASIS,
199+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200+
See the License for the specific language governing permissions and
201+
limitations under the License.

‎categories/index.html

+256
Large diffs are not rendered by default.

‎categories/index.xml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
2+
<channel>
3+
<title>Diago – Categories</title>
4+
<link>https://diago.org/categories/</link>
5+
<description>Recent content in Categories on Diago</description>
6+
<generator>Hugo -- gohugo.io</generator>
7+
<language>en-us</language>
8+
9+
<atom:link href="https://diago.org/categories/index.xml" rel="self" type="application/rss+xml" />
10+
11+
12+
13+
14+
15+
16+
17+
</channel>
18+
</rss>

‎css/compiled/main.css

+3,522
Large diffs are not rendered by default.

‎css/compiled/main.min.c1fe2c80102c9dc9ddc66184191e646769052d36a21b38ac5ab5077713cf7a81.css

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎css/custom.css

Whitespace-only changes.

‎css/custom.min.e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css

Whitespace-only changes.

‎docs/getting_started/index.html

+487
Large diffs are not rendered by default.

‎docs/guide/index.html

+278
Large diffs are not rendered by default.

‎docs/guide/index.xml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
2+
<channel>
3+
<title>Diago – Guide</title>
4+
<link>https://diago.org/docs/guide/</link>
5+
<description>Recent content in Guide on Diago</description>
6+
<generator>Hugo -- gohugo.io</generator>
7+
<language>en-us</language>
8+
9+
<atom:link href="https://diago.org/docs/guide/index.xml" rel="self" type="application/rss+xml" />
10+
11+
12+
13+
14+
15+
16+
17+
</channel>
18+
</rss>

‎docs/index.html

+275
Large diffs are not rendered by default.

‎docs/index.xml

+187
Large diffs are not rendered by default.

‎en.search-data.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"/docs/getting_started/":{"data":{"#":"Getting starteddiago acts as UAS(User Agent Server) and UAC(User Agent Client). For now it keeps abstractions only where it needs. Therefore it distincts your dialog control by:\nIt receives DialogServerSession for serving incoming sessions(Call leg) It creates DialogClientSession when it Dials outgoing session(Call leg) Further it is explicit which media stack it uses\nAnswer -\u003e RTP/UDP AnswerWebrtc -\u003e Webrtc stack SRTP/DTLS Setup diago: ua, _ := sipgo.NewUA() transportUDP := diago.Transport{ Transport: \"udp\", BindHost: \"127.0.0.1\", BindPort: 5060, } transportTCP := diago.Transport{ Transport: \"tcp\", BindHost: \"127.0.0.1\", BindPort: 5060, } d := diago.NewDiago(ua, diago.WithTransport(transportUDP), diago.WithTransport(transportTCP), ) Incoming call d.Serve(ctx, func(inDialog *diago.DialogServerSession) { // - Do your call routing. switch inDialog.ToUser() { case \"answer\": case \"123456\" } }) Outgoing dialog, err := d.Dial(ctx, recipient sip.Uri, bridge *Bridge, opts sipgo.AnswerOptions) dialog.Hangup() ","answering-call#Answering call":" func Answer(inDialog *diago.DialogServerSession) { inDialog.Progress() // Progress -\u003e 100 Trying inDialog.Ringing() // Ringing -\u003e 180 Response if err := inDialog.Answer(); err != nil { fmt.Println(\"Failed to answer\", err) return } ctx := inDialog.Context() select { case \u003c-ctx.Done(): case \u003c-time.After(1 * time.Minute): } } ","getting-started#Getting started":"","playing-file-with-playback#Playing file with Playback":"Playing file is done by playback.\nSupported formats:\nwav (PCM) func Playback(inDialog *diago.DialogServerSession) { inDialog.Ringing() playfile, err := os.Open(\"demo-instruct.wav\") if err != nil { fmt.Println(\"Failed to open file\", err) return } pb, err := inDialog.PlaybackCreate() if err != nil { fmt.Println(\"Failed to create playback\", err) return } if err := inDialog.Answer(); err != nil { fmt.Println(\"Failed to answer\", err) return } if err := pb.Play(playfile, \"audio/wav\"); err != nil { fmt.Println(\"Playing failed\", err) } } "},"title":"getting_started"}}

‎en.search.js

+398
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
// Search functionality using FlexSearch.
2+
3+
// Change shortcut key to cmd+k on Mac, iPad or iPhone.
4+
document.addEventListener("DOMContentLoaded", function () {
5+
if (/iPad|iPhone|Macintosh/.test(navigator.userAgent)) {
6+
// select the kbd element under the .search-wrapper class
7+
const keys = document.querySelectorAll(".search-wrapper kbd");
8+
keys.forEach(key => {
9+
key.innerHTML = '<span class="hx-text-xs">⌘</span>K';
10+
});
11+
}
12+
});
13+
14+
// Render the search data as JSON.
15+
//
16+
//
17+
//
18+
//
19+
20+
(function () {
21+
const searchDataURL = '/en.search-data.json';
22+
23+
const inputElements = document.querySelectorAll('.search-input');
24+
for (const el of inputElements) {
25+
el.addEventListener('focus', init);
26+
el.addEventListener('keyup', search);
27+
el.addEventListener('keydown', handleKeyDown);
28+
el.addEventListener('input', handleInputChange);
29+
}
30+
31+
const shortcutElements = document.querySelectorAll('.search-wrapper kbd');
32+
33+
function setShortcutElementsOpacity(opacity) {
34+
shortcutElements.forEach(el => {
35+
el.style.opacity = opacity;
36+
});
37+
}
38+
39+
function handleInputChange(e) {
40+
const opacity = e.target.value.length > 0 ? 0 : 100;
41+
setShortcutElementsOpacity(opacity);
42+
}
43+
44+
// Get the search wrapper, input, and results elements.
45+
function getActiveSearchElement() {
46+
const inputs = Array.from(document.querySelectorAll('.search-wrapper')).filter(el => el.clientHeight > 0);
47+
if (inputs.length === 1) {
48+
return {
49+
wrapper: inputs[0],
50+
inputElement: inputs[0].querySelector('.search-input'),
51+
resultsElement: inputs[0].querySelector('.search-results')
52+
};
53+
}
54+
return undefined;
55+
}
56+
57+
const INPUTS = ['input', 'select', 'button', 'textarea']
58+
59+
// Focus the search input when pressing ctrl+k/cmd+k or /.
60+
document.addEventListener('keydown', function (e) {
61+
const { inputElement } = getActiveSearchElement();
62+
if (!inputElement) return;
63+
64+
const activeElement = document.activeElement;
65+
const tagName = activeElement && activeElement.tagName;
66+
if (
67+
inputElement === activeElement ||
68+
!tagName ||
69+
INPUTS.includes(tagName) ||
70+
(activeElement && activeElement.isContentEditable))
71+
return;
72+
73+
if (
74+
e.key === '/' ||
75+
(e.key === 'k' &&
76+
(e.metaKey /* for Mac */ || /* for non-Mac */ e.ctrlKey))
77+
) {
78+
e.preventDefault();
79+
inputElement.focus();
80+
} else if (e.key === 'Escape' && inputElement.value) {
81+
inputElement.blur();
82+
}
83+
});
84+
85+
// Dismiss the search results when clicking outside the search box.
86+
document.addEventListener('mousedown', function (e) {
87+
const { inputElement, resultsElement } = getActiveSearchElement();
88+
if (!inputElement || !resultsElement) return;
89+
if (
90+
e.target !== inputElement &&
91+
e.target !== resultsElement &&
92+
!resultsElement.contains(e.target)
93+
) {
94+
setShortcutElementsOpacity(100);
95+
hideSearchResults();
96+
}
97+
});
98+
99+
// Get the currently active result and its index.
100+
function getActiveResult() {
101+
const { resultsElement } = getActiveSearchElement();
102+
if (!resultsElement) return { result: undefined, index: -1 };
103+
104+
const result = resultsElement.querySelector('.active');
105+
if (!result) return { result: undefined, index: -1 };
106+
107+
const index = parseInt(result.dataset.index, 10);
108+
return { result, index };
109+
}
110+
111+
// Set the active result by index.
112+
function setActiveResult(index) {
113+
const { resultsElement } = getActiveSearchElement();
114+
if (!resultsElement) return;
115+
116+
const { result: activeResult } = getActiveResult();
117+
activeResult && activeResult.classList.remove('active');
118+
const result = resultsElement.querySelector(`[data-index="${index}"]`);
119+
if (result) {
120+
result.classList.add('active');
121+
result.focus();
122+
}
123+
}
124+
125+
// Get the number of search results from the DOM.
126+
function getResultsLength() {
127+
const { resultsElement } = getActiveSearchElement();
128+
if (!resultsElement) return 0;
129+
return resultsElement.dataset.count;
130+
}
131+
132+
// Finish the search by hiding the results and clearing the input.
133+
function finishSearch() {
134+
const { inputElement } = getActiveSearchElement();
135+
if (!inputElement) return;
136+
hideSearchResults();
137+
inputElement.value = '';
138+
inputElement.blur();
139+
}
140+
141+
function hideSearchResults() {
142+
const { resultsElement } = getActiveSearchElement();
143+
if (!resultsElement) return;
144+
resultsElement.classList.add('hx-hidden');
145+
}
146+
147+
// Handle keyboard events.
148+
function handleKeyDown(e) {
149+
const { inputElement } = getActiveSearchElement();
150+
if (!inputElement) return;
151+
152+
const resultsLength = getResultsLength();
153+
const { result: activeResult, index: activeIndex } = getActiveResult();
154+
155+
switch (e.key) {
156+
case 'ArrowUp':
157+
e.preventDefault();
158+
if (activeIndex > 0) setActiveResult(activeIndex - 1);
159+
break;
160+
case 'ArrowDown':
161+
e.preventDefault();
162+
if (activeIndex + 1 < resultsLength) setActiveResult(activeIndex + 1);
163+
break;
164+
case 'Enter':
165+
e.preventDefault();
166+
if (activeResult) {
167+
activeResult.click();
168+
}
169+
finishSearch();
170+
case 'Escape':
171+
e.preventDefault();
172+
hideSearchResults();
173+
// Clear the input when pressing escape
174+
inputElement.value = '';
175+
inputElement.dispatchEvent(new Event('input'));
176+
// Remove focus from the input
177+
inputElement.blur();
178+
break;
179+
}
180+
}
181+
182+
// Initializes the search.
183+
function init(e) {
184+
e.target.removeEventListener('focus', init);
185+
if (!(window.pageIndex && window.sectionIndex)) {
186+
preloadIndex();
187+
}
188+
}
189+
190+
/**
191+
* Preloads the search index by fetching data and adding it to the FlexSearch index.
192+
* @returns {Promise<void>} A promise that resolves when the index is preloaded.
193+
*/
194+
async function preloadIndex() {
195+
const tokenize = 'forward';
196+
window.pageIndex = new FlexSearch.Document({
197+
tokenize,
198+
cache: 100,
199+
document: {
200+
id: 'id',
201+
store: ['title'],
202+
index: "content"
203+
}
204+
});
205+
206+
window.sectionIndex = new FlexSearch.Document({
207+
tokenize,
208+
cache: 100,
209+
document: {
210+
id: 'id',
211+
store: ['title', 'content', 'url', 'display'],
212+
index: "content",
213+
tag: 'pageId'
214+
}
215+
});
216+
217+
const resp = await fetch(searchDataURL);
218+
const data = await resp.json();
219+
let pageId = 0;
220+
for (const route in data) {
221+
let pageContent = '';
222+
++pageId;
223+
224+
for (const heading in data[route].data) {
225+
const [hash, text] = heading.split('#');
226+
const url = route.trimEnd('/') + (hash ? '#' + hash : '');
227+
const title = text || data[route].title;
228+
229+
const content = data[route].data[heading] || '';
230+
const paragraphs = content.split('\n').filter(Boolean);
231+
232+
sectionIndex.add({
233+
id: url,
234+
url,
235+
title,
236+
pageId: `page_${pageId}`,
237+
content: title,
238+
...(paragraphs[0] && { display: paragraphs[0] })
239+
});
240+
241+
for (let i = 0; i < paragraphs.length; i++) {
242+
sectionIndex.add({
243+
id: `${url}_${i}`,
244+
url,
245+
title,
246+
pageId: `page_${pageId}`,
247+
content: paragraphs[i]
248+
});
249+
}
250+
251+
pageContent += ` ${title} ${content}`;
252+
}
253+
254+
window.pageIndex.add({
255+
id: pageId,
256+
title: data[route].title,
257+
content: pageContent
258+
});
259+
260+
}
261+
}
262+
263+
/**
264+
* Performs a search based on the provided query and displays the results.
265+
* @param {Event} e - The event object.
266+
*/
267+
function search(e) {
268+
const query = e.target.value;
269+
if (!e.target.value) {
270+
hideSearchResults();
271+
return;
272+
}
273+
274+
const { resultsElement } = getActiveSearchElement();
275+
while (resultsElement.firstChild) {
276+
resultsElement.removeChild(resultsElement.firstChild);
277+
}
278+
resultsElement.classList.remove('hx-hidden');
279+
280+
const pageResults = window.pageIndex.search(query, 5, { enrich: true, suggest: true })[0]?.result || [];
281+
282+
const results = [];
283+
const pageTitleMatches = {};
284+
285+
for (let i = 0; i < pageResults.length; i++) {
286+
const result = pageResults[i];
287+
pageTitleMatches[i] = 0;
288+
289+
// Show the top 5 results for each page
290+
const sectionResults = window.sectionIndex.search(query, 5, { enrich: true, suggest: true, tag: `page_${result.id}` })[0]?.result || [];
291+
let isFirstItemOfPage = true
292+
const occurred = {}
293+
294+
for (let j = 0; j < sectionResults.length; j++) {
295+
const { doc } = sectionResults[j]
296+
const isMatchingTitle = doc.display !== undefined
297+
if (isMatchingTitle) {
298+
pageTitleMatches[i]++
299+
}
300+
const { url, title } = doc
301+
const content = doc.display || doc.content
302+
303+
if (occurred[url + '@' + content]) continue
304+
occurred[url + '@' + content] = true
305+
results.push({
306+
_page_rk: i,
307+
_section_rk: j,
308+
route: url,
309+
prefix: isFirstItemOfPage ? result.doc.title : undefined,
310+
children: { title, content }
311+
})
312+
isFirstItemOfPage = false
313+
}
314+
}
315+
const sortedResults = results
316+
.sort((a, b) => {
317+
// Sort by number of matches in the title.
318+
if (a._page_rk === b._page_rk) {
319+
return a._section_rk - b._section_rk
320+
}
321+
if (pageTitleMatches[a._page_rk] !== pageTitleMatches[b._page_rk]) {
322+
return pageTitleMatches[b._page_rk] - pageTitleMatches[a._page_rk]
323+
}
324+
return a._page_rk - b._page_rk
325+
})
326+
.map(res => ({
327+
id: `${res._page_rk}_${res._section_rk}`,
328+
route: res.route,
329+
prefix: res.prefix,
330+
children: res.children
331+
}));
332+
displayResults(sortedResults, query);
333+
}
334+
335+
/**
336+
* Displays the search results on the page.
337+
*
338+
* @param {Array} results - The array of search results.
339+
* @param {string} query - The search query.
340+
*/
341+
function displayResults(results, query) {
342+
const { resultsElement } = getActiveSearchElement();
343+
if (!resultsElement) return;
344+
345+
if (!results.length) {
346+
resultsElement.innerHTML = `<span class="no-result">No results found.</span>`;
347+
return;
348+
}
349+
350+
// Highlight the query in the result text.
351+
function highlightMatches(text, query) {
352+
const escapedQuery = query.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
353+
const regex = new RegExp(escapedQuery, 'gi');
354+
return text.replace(regex, (match) => `<span class="match">${match}</span>`);
355+
}
356+
357+
// Create a DOM element from the HTML string.
358+
function createElement(str) {
359+
const div = document.createElement('div');
360+
div.innerHTML = str.trim();
361+
return div.firstChild;
362+
}
363+
364+
function handleMouseMove(e) {
365+
const target = e.target.closest('a');
366+
if (target) {
367+
const active = resultsElement.querySelector('a.active');
368+
if (active) {
369+
active.classList.remove('active');
370+
}
371+
target.classList.add('active');
372+
}
373+
}
374+
375+
const fragment = document.createDocumentFragment();
376+
for (let i = 0; i < results.length; i++) {
377+
const result = results[i];
378+
if (result.prefix) {
379+
fragment.appendChild(createElement(`
380+
<div class="prefix">${result.prefix}</div>`));
381+
}
382+
let li = createElement(`
383+
<li>
384+
<a data-index="${i}" href="${result.route}" class=${i === 0 ? "active" : ""}>
385+
<div class="title">`+ highlightMatches(result.children.title, query) + `</div>` +
386+
(result.children.content ?
387+
`<div class="excerpt">` + highlightMatches(result.children.content, query) + `</div>` : '') + `
388+
</a>
389+
</li>`);
390+
li.addEventListener('mousemove', handleMouseMove);
391+
li.addEventListener('keydown', handleKeyDown);
392+
li.querySelector('a').addEventListener('click', finishSearch);
393+
fragment.appendChild(li);
394+
}
395+
resultsElement.appendChild(fragment);
396+
resultsElement.dataset.count = results.length;
397+
}
398+
})();

‎en.search.min.3db95834d7cbb513c5e7bd8ff469faec73b56798f9d73f74f2e09a725dbbeac2.js

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎favicon.ico

15 KB
Binary file not shown.

‎index.html

+259
Large diffs are not rendered by default.

‎index.xml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
2+
<channel>
3+
<title>Diago – Welcome to diago</title>
4+
<link>https://diago.org/</link>
5+
<description>Recent content in Welcome to diago on Diago</description>
6+
<generator>Hugo -- gohugo.io</generator>
7+
<language>en-us</language>
8+
9+
<atom:link href="https://diago.org/index.xml" rel="self" type="application/rss+xml" />
10+
11+
12+
13+
14+
15+
16+
</channel>
17+
</rss>

‎js/main.js

+275
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// Light / Dark theme toggle
2+
(function () {
3+
const defaultTheme = 'system'
4+
5+
const themeToggleButtons = document.querySelectorAll(".theme-toggle");
6+
7+
// Change the icons of the buttons based on previous settings or system theme
8+
if (
9+
localStorage.getItem("color-theme") === "dark" ||
10+
(!("color-theme" in localStorage) &&
11+
((window.matchMedia("(prefers-color-scheme: dark)").matches && defaultTheme === "system") || defaultTheme === "dark"))
12+
) {
13+
themeToggleButtons.forEach((el) => el.dataset.theme = "dark");
14+
} else {
15+
themeToggleButtons.forEach((el) => el.dataset.theme = "light");
16+
}
17+
18+
// Add click event handler to the buttons
19+
themeToggleButtons.forEach((el) => {
20+
el.addEventListener("click", function () {
21+
if (localStorage.getItem("color-theme")) {
22+
if (localStorage.getItem("color-theme") === "light") {
23+
setDarkTheme();
24+
localStorage.setItem("color-theme", "dark");
25+
} else {
26+
setLightTheme();
27+
localStorage.setItem("color-theme", "light");
28+
}
29+
} else {
30+
if (document.documentElement.classList.contains("dark")) {
31+
setLightTheme();
32+
localStorage.setItem("color-theme", "light");
33+
} else {
34+
setDarkTheme();
35+
localStorage.setItem("color-theme", "dark");
36+
}
37+
}
38+
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
39+
});
40+
});
41+
42+
// Listen for system theme changes
43+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
44+
if (defaultTheme === "system" && !("color-theme" in localStorage)) {
45+
e.matches ? setDarkTheme() : setLightTheme();
46+
themeToggleButtons.forEach((el) =>
47+
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light"
48+
);
49+
}
50+
});
51+
})();
52+
53+
;
54+
// Hamburger menu for mobile navigation
55+
56+
document.addEventListener('DOMContentLoaded', function () {
57+
const menu = document.querySelector('.hamburger-menu');
58+
const overlay = document.querySelector('.mobile-menu-overlay');
59+
const sidebarContainer = document.querySelector('.sidebar-container');
60+
61+
// Initialize the overlay
62+
const overlayClasses = ['hx-fixed', 'hx-inset-0', 'hx-z-10', 'hx-bg-black/80', 'dark:hx-bg-black/60'];
63+
overlay.classList.add('hx-bg-transparent');
64+
overlay.classList.remove("hx-hidden", ...overlayClasses);
65+
66+
function toggleMenu() {
67+
// Toggle the hamburger menu
68+
menu.querySelector('svg').classList.toggle('open');
69+
70+
// When the menu is open, we want to show the navigation sidebar
71+
sidebarContainer.classList.toggle('max-md:[transform:translate3d(0,-100%,0)]');
72+
sidebarContainer.classList.toggle('max-md:[transform:translate3d(0,0,0)]');
73+
74+
// When the menu is open, we want to prevent the body from scrolling
75+
document.body.classList.toggle('hx-overflow-hidden');
76+
document.body.classList.toggle('md:hx-overflow-auto');
77+
}
78+
79+
menu.addEventListener('click', (e) => {
80+
e.preventDefault();
81+
toggleMenu();
82+
83+
if (overlay.classList.contains('hx-bg-transparent')) {
84+
// Show the overlay
85+
overlay.classList.add(...overlayClasses);
86+
overlay.classList.remove('hx-bg-transparent');
87+
} else {
88+
// Hide the overlay
89+
overlay.classList.remove(...overlayClasses);
90+
overlay.classList.add('hx-bg-transparent');
91+
}
92+
});
93+
94+
overlay.addEventListener('click', (e) => {
95+
e.preventDefault();
96+
toggleMenu();
97+
98+
// Hide the overlay
99+
overlay.classList.remove(...overlayClasses);
100+
overlay.classList.add('hx-bg-transparent');
101+
});
102+
});
103+
104+
;
105+
// Copy button for code blocks
106+
107+
document.addEventListener('DOMContentLoaded', function () {
108+
const getCopyIcon = () => {
109+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
110+
svg.innerHTML = `
111+
<path stroke-linecap="round" stroke-linejoin="round" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
112+
`;
113+
svg.setAttribute('fill', 'none');
114+
svg.setAttribute('viewBox', '0 0 24 24');
115+
svg.setAttribute('stroke', 'currentColor');
116+
svg.setAttribute('stroke-width', '2');
117+
return svg;
118+
}
119+
120+
const getSuccessIcon = () => {
121+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
122+
svg.innerHTML = `
123+
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
124+
`;
125+
svg.setAttribute('fill', 'none');
126+
svg.setAttribute('viewBox', '0 0 24 24');
127+
svg.setAttribute('stroke', 'currentColor');
128+
svg.setAttribute('stroke-width', '2');
129+
return svg;
130+
}
131+
132+
document.querySelectorAll('.hextra-code-copy-btn').forEach(function (button) {
133+
// Add copy and success icons
134+
button.querySelector('.copy-icon')?.appendChild(getCopyIcon());
135+
button.querySelector('.success-icon')?.appendChild(getSuccessIcon());
136+
137+
// Add click event listener for copy button
138+
button.addEventListener('click', function (e) {
139+
e.preventDefault();
140+
// Get the code target
141+
const target = button.parentElement.previousElementSibling;
142+
let codeElement;
143+
if (target.tagName === 'CODE') {
144+
codeElement = target;
145+
} else {
146+
// Select the last code element in case line numbers are present
147+
const codeElements = target.querySelectorAll('code');
148+
codeElement = codeElements[codeElements.length - 1];
149+
}
150+
if (codeElement) {
151+
let code = codeElement.innerText;
152+
// Replace double newlines with single newlines in the innerText
153+
// as each line inside <span> has trailing newline '\n'
154+
if ("lang" in codeElement.dataset) {
155+
code = code.replace(/\n\n/g, '\n');
156+
}
157+
navigator.clipboard.writeText(code).then(function () {
158+
button.classList.add('copied');
159+
setTimeout(function () {
160+
button.classList.remove('copied');
161+
}, 1000);
162+
}).catch(function (err) {
163+
console.error('Failed to copy text: ', err);
164+
});
165+
} else {
166+
console.error('Target element not found');
167+
}
168+
});
169+
});
170+
});
171+
172+
;
173+
document.querySelectorAll('.hextra-tabs-toggle').forEach(function (button) {
174+
button.addEventListener('click', function (e) {
175+
// set parent tabs to unselected
176+
const tabs = Array.from(e.target.parentElement.querySelectorAll('.hextra-tabs-toggle'));
177+
tabs.map(tab => tab.dataset.state = '');
178+
179+
// set current tab to selected
180+
e.target.dataset.state = 'selected';
181+
182+
// set all panels to unselected
183+
const panelsContainer = e.target.parentElement.parentElement.nextElementSibling;
184+
Array.from(panelsContainer.children).forEach(function (panel) {
185+
panel.dataset.state = '';
186+
});
187+
188+
const panelId = e.target.getAttribute('aria-controls');
189+
const panel = panelsContainer.querySelector(`#${panelId}`);
190+
panel.dataset.state = 'selected';
191+
});
192+
});
193+
194+
;
195+
(function () {
196+
const languageSwitchers = document.querySelectorAll('.language-switcher');
197+
languageSwitchers.forEach((switcher) => {
198+
switcher.addEventListener('click', (e) => {
199+
e.preventDefault();
200+
switcher.dataset.state = switcher.dataset.state === 'open' ? 'closed' : 'open';
201+
const optionsElement = switcher.nextElementSibling;
202+
optionsElement.classList.toggle('hx-hidden');
203+
204+
// Calculate position of language options element
205+
const switcherRect = switcher.getBoundingClientRect();
206+
const translateY = switcherRect.top - window.innerHeight - 15;
207+
optionsElement.style.transform = `translate3d(${switcherRect.left}px, ${translateY}px, 0)`;
208+
optionsElement.style.minWidth = `${Math.max(switcherRect.width, 50)}px`;
209+
});
210+
});
211+
212+
// Dismiss language switcher when clicking outside
213+
document.addEventListener('click', (e) => {
214+
if (e.target.closest('.language-switcher') === null) {
215+
languageSwitchers.forEach((switcher) => {
216+
switcher.dataset.state = 'closed';
217+
const optionsElement = switcher.nextElementSibling;
218+
optionsElement.classList.add('hx-hidden');
219+
});
220+
}
221+
});
222+
})();
223+
224+
;
225+
// Script for filetree shortcode collapsing/expanding folders used in the theme
226+
// ======================================================================
227+
document.addEventListener("DOMContentLoaded", function () {
228+
const folders = document.querySelectorAll(".hextra-filetree-folder");
229+
folders.forEach(function (folder) {
230+
folder.addEventListener("click", function () {
231+
Array.from(folder.children).forEach(function (el) {
232+
el.dataset.state = el.dataset.state === "open" ? "closed" : "open";
233+
});
234+
folder.nextElementSibling.dataset.state = folder.nextElementSibling.dataset.state === "open" ? "closed" : "open";
235+
});
236+
});
237+
});
238+
239+
;
240+
document.addEventListener("DOMContentLoaded", function () {
241+
const buttons = document.querySelectorAll(".hextra-sidebar-collapsible-button");
242+
buttons.forEach(function (button) {
243+
button.addEventListener("click", function (e) {
244+
e.preventDefault();
245+
const list = button.parentElement.parentElement;
246+
if (list) {
247+
list.classList.toggle("open")
248+
}
249+
});
250+
});
251+
});
252+
253+
;
254+
// Back to top button
255+
256+
document.addEventListener("DOMContentLoaded", function () {
257+
const backToTop = document.querySelector("#backToTop");
258+
if (backToTop) {
259+
document.addEventListener("scroll", (e) => {
260+
if (window.scrollY > 300) {
261+
backToTop.classList.remove("hx-opacity-0");
262+
} else {
263+
backToTop.classList.add("hx-opacity-0");
264+
}
265+
});
266+
}
267+
});
268+
269+
function scrollUp() {
270+
window.scroll({
271+
top: 0,
272+
left: 0,
273+
behavior: "smooth",
274+
});
275+
}

‎js/main.min.998f7d6b2520d6d9875145cbe14b35964b365b44b3b1bfa6b797a74a9c173b07.js

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎lib/flexsearch/flexsearch.bundle.min.0425860527cc9968f9f049421c7a56b39327d475e2e3a8f550416be3a9134327.js

+39
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎site.webmanifest

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "Hextra",
3+
"short_name": "Hextra",
4+
"start_url": "index.html",
5+
"icons": [
6+
{
7+
"src": "android-chrome-192x192.png",
8+
"sizes": "192x192",
9+
"type": "image/png"
10+
},
11+
{
12+
"src": "android-chrome-512x512.png",
13+
"sizes": "512x512",
14+
"type": "image/png"
15+
}
16+
],
17+
"theme_color": "#000000",
18+
"background_color": "#000000",
19+
"display": "standalone"
20+
}

‎sitemap.xml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
2+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
3+
xmlns:xhtml="http://www.w3.org/1999/xhtml">
4+
<url>
5+
<loc>https://diago.org/docs/guide/</loc>
6+
</url><url>
7+
<loc>https://diago.org/docs/getting_started/</loc>
8+
</url><url>
9+
<loc>https://diago.org/categories/</loc>
10+
</url><url>
11+
<loc>https://diago.org/docs/</loc>
12+
</url><url>
13+
<loc>https://diago.org/tags/</loc>
14+
</url><url>
15+
<loc>https://diago.org/</loc>
16+
</url>
17+
</urlset>

‎tags/index.html

+256
Large diffs are not rendered by default.

‎tags/index.xml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
2+
<channel>
3+
<title>Diago – Tags</title>
4+
<link>https://diago.org/tags/</link>
5+
<description>Recent content in Tags on Diago</description>
6+
<generator>Hugo -- gohugo.io</generator>
7+
<language>en-us</language>
8+
9+
<atom:link href="https://diago.org/tags/index.xml" rel="self" type="application/rss+xml" />
10+
11+
12+
13+
14+
15+
16+
17+
</channel>
18+
</rss>

0 commit comments

Comments
 (0)
Please sign in to comment.