clew-code 0.2.27 → 0.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -19
- package/dist/main.js +2262 -2234
- package/docs/commands.html +3 -0
- package/docs/internals/long-term-memory-design.md +173 -89
- package/docs/js/main.js +438 -437
- package/docs/personal-profile.html +113 -0
- package/package.json +165 -165
package/docs/js/main.js
CHANGED
|
@@ -1,437 +1,438 @@
|
|
|
1
|
-
// Clew Docs — Mobile Nav, Header, Sidebar, TOC, Code Copy
|
|
2
|
-
(function () {
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
// ── Root Prefix (for script location detection) ──
|
|
6
|
-
var rootPrefix = '';
|
|
7
|
-
var scripts = document.getElementsByTagName('script');
|
|
8
|
-
for (var i = 0; i < scripts.length; i++) {
|
|
9
|
-
var src = scripts[i].getAttribute('src');
|
|
10
|
-
if (src && src.indexOf('js/main.js') !== -1) {
|
|
11
|
-
rootPrefix = src.replace('js/main.js', '');
|
|
12
|
-
break;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// ── Header Injection ──
|
|
17
|
-
function injectHeader() {
|
|
18
|
-
var header = document.querySelector('.header');
|
|
19
|
-
if (!header) return;
|
|
20
|
-
|
|
21
|
-
var path = window.location.pathname;
|
|
22
|
-
var isThai = /\.th\.html$/.test(path);
|
|
23
|
-
var isIndex = /\/index(\.th)?\.html$/.test(path) || path === '/' || path.endsWith('/docs/');
|
|
24
|
-
var currentPage = path.split('/').pop() || 'index.html';
|
|
25
|
-
|
|
26
|
-
var logoHref = isThai ? 'index.th.html' : 'index.html';
|
|
27
|
-
var docsHref = isThai ? 'quick-start.th.html' : 'quick-start.html';
|
|
28
|
-
var ariaLabel = isThai ? '\u0E40\u0E1B\u0E34\u0E14/\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39' : 'Toggle navigation';
|
|
29
|
-
|
|
30
|
-
// Language toggle
|
|
31
|
-
var langTarget = isThai
|
|
32
|
-
? currentPage.replace(/\.th\.html$/, '.html')
|
|
33
|
-
: currentPage.replace(/\.html$/, '.th.html');
|
|
34
|
-
var langLabel = isThai ? 'EN' : '\u0E44\u0E17\u0E22';
|
|
35
|
-
// If no .html page exists for the target, just use README
|
|
36
|
-
if (isIndex) langTarget = isThai ? 'index.html' : 'index.th.html';
|
|
37
|
-
|
|
38
|
-
// Build breadcrumbs from path
|
|
39
|
-
var crumbs = [];
|
|
40
|
-
var parts = path.replace(/\/$/, '').split('/').filter(Boolean);
|
|
41
|
-
if (parts[0] === 'docs') parts.shift(); // remove leading "docs"
|
|
42
|
-
if (parts.length > 0) {
|
|
43
|
-
var crumbPath = '';
|
|
44
|
-
for (var i = 0; i < parts.length; i++) {
|
|
45
|
-
crumbPath += (i === 0 ? '' : '/') + parts[i];
|
|
46
|
-
var isLast = i === parts.length - 1;
|
|
47
|
-
var label = parts[i].replace(/\.th\.html$/, '').replace(/\.html$/, '').replace(/-/g, ' ');
|
|
48
|
-
if (isLast) {
|
|
49
|
-
crumbs.push('<span class="current">' + label + '</span>');
|
|
50
|
-
} else {
|
|
51
|
-
crumbs.push('<a href="' + crumbPath + '">' + label + '</a>');
|
|
52
|
-
crumbs.push('<span class="sep">/</span>');
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
var breadcrumbsHtml = crumbs.length > 0
|
|
57
|
-
? '<nav class="header-breadcrumbs">' + crumbs.join('') + '</nav>'
|
|
58
|
-
: '';
|
|
59
|
-
|
|
60
|
-
header.innerHTML =
|
|
61
|
-
'<div class="header-inner">' +
|
|
62
|
-
' <a href="' + logoHref + '" class="logo">Clew</a>' +
|
|
63
|
-
breadcrumbsHtml +
|
|
64
|
-
' <div class="header-search">' +
|
|
65
|
-
' <input type="text" id="headerSearchInput" placeholder="' + (isThai ? '\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E40\u0E2D\u0E01\u0E2A\u0E32\u0E23...' : 'Search docs...') + '" autocomplete="off">' +
|
|
66
|
-
' <span class="search-icon">\u2315</span>' +
|
|
67
|
-
' <div class="search-results" id="headerSearchResults"></div>' +
|
|
68
|
-
' </div>' +
|
|
69
|
-
' <nav class="header-nav">' +
|
|
70
|
-
' <a href="' + logoHref + '">' + (isThai ? '\u0E2B\u0E19\u0E49\u0E32\u0E41\u0E23\u0E01' : 'Home') + '</a>' +
|
|
71
|
-
' <a href="' + docsHref + '">' + (isThai ? '\u0E40\u0E2D\u0E01\u0E2A\u0E32\u0E23' : 'Docs') + '</a>' +
|
|
72
|
-
' <a href="https://github.com/JonusNattapong/ClewCode" target="_blank">GitHub</a>' +
|
|
73
|
-
' </nav>' +
|
|
74
|
-
' <button class="lang-toggle" id="langToggle" aria-label="Switch language">' + langLabel + '</button>' +
|
|
75
|
-
' <button class="menu-btn" id="menuToggle" aria-label="' + ariaLabel + '"><span></span><span></span><span></span></button>' +
|
|
76
|
-
'</div>';
|
|
77
|
-
|
|
78
|
-
// Language toggle
|
|
79
|
-
var langToggle = document.getElementById('langToggle');
|
|
80
|
-
if (langToggle) {
|
|
81
|
-
langToggle.addEventListener('click', function () {
|
|
82
|
-
window.location.href = langTarget;
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Active nav link
|
|
87
|
-
var navLinks = header.querySelectorAll('.header-nav a');
|
|
88
|
-
navLinks.forEach(function (link) {
|
|
89
|
-
var href = link.getAttribute('href');
|
|
90
|
-
if (!href) return;
|
|
91
|
-
var hrefPage = href.split('/').pop().split('#')[0] || 'index.html';
|
|
92
|
-
if (hrefPage === currentPage) link.classList.add('active');
|
|
93
|
-
});
|
|
94
|
-
if (!isIndex) {
|
|
95
|
-
var docsLink = header.querySelector('.header-nav a[href*="quick-start"]');
|
|
96
|
-
if (docsLink) docsLink.classList.add('active');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Search functionality
|
|
100
|
-
initHeaderSearch();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function initHeaderSearch() {
|
|
104
|
-
var input = document.getElementById('headerSearchInput');
|
|
105
|
-
var results = document.getElementById('headerSearchResults');
|
|
106
|
-
if (!input || !results) return;
|
|
107
|
-
|
|
108
|
-
// Build search index from sidebar links + TOC headings
|
|
109
|
-
var searchIndex = [];
|
|
110
|
-
document.querySelectorAll('.sidebar-link').forEach(function (link) {
|
|
111
|
-
searchIndex.push({ text: link.textContent.trim(), href: link.getAttribute('href'), type: 'page' });
|
|
112
|
-
});
|
|
113
|
-
document.querySelectorAll('.content h2, .content h3').forEach(function (h) {
|
|
114
|
-
searchIndex.push({ text: h.textContent.trim(), href: '#' + h.id, type: 'section' });
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
input.addEventListener('input', function () {
|
|
118
|
-
var query = this.value.trim().toLowerCase();
|
|
119
|
-
results.innerHTML = '';
|
|
120
|
-
results.classList.remove('open');
|
|
121
|
-
if (query.length < 1) return;
|
|
122
|
-
|
|
123
|
-
var matches = [];
|
|
124
|
-
for (var i = 0; i < searchIndex.length; i++) {
|
|
125
|
-
var item = searchIndex[i];
|
|
126
|
-
if (item.text.toLowerCase().indexOf(query) !== -1) {
|
|
127
|
-
matches.push(item);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (matches.length === 0) {
|
|
132
|
-
results.innerHTML = '<div class="search-empty">No results found</div>';
|
|
133
|
-
results.classList.add('open');
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
for (var j = 0; j < Math.min(matches.length, 8); j++) {
|
|
138
|
-
var m = matches[j];
|
|
139
|
-
var highlighted = m.text.replace(
|
|
140
|
-
new RegExp('(' + query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')', 'gi'),
|
|
141
|
-
'<mark>$1</mark>'
|
|
142
|
-
);
|
|
143
|
-
results.innerHTML +=
|
|
144
|
-
'<a href="' + m.href + '" class="search-result">' +
|
|
145
|
-
'<span class="search-type">' + (m.type === 'page' ? '\u{1F4C4}' : '#') + '</span> ' +
|
|
146
|
-
highlighted +
|
|
147
|
-
'</a>';
|
|
148
|
-
}
|
|
149
|
-
results.classList.add('open');
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Close on click outside
|
|
153
|
-
document.addEventListener('click', function (e) {
|
|
154
|
-
if (!input.contains(e.target) && !results.contains(e.target)) {
|
|
155
|
-
results.classList.remove('open');
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Close on Escape
|
|
160
|
-
input.addEventListener('keydown', function (e) {
|
|
161
|
-
if (e.key === 'Escape') {
|
|
162
|
-
results.classList.remove('open');
|
|
163
|
-
input.blur();
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
injectHeader();
|
|
169
|
-
|
|
170
|
-
// ── Sidebar Injection ──
|
|
171
|
-
var sidebar = document.getElementById('sidebar');
|
|
172
|
-
if (sidebar) {
|
|
173
|
-
sidebar.innerHTML =
|
|
174
|
-
'<div class="sidebar-section">' +
|
|
175
|
-
' <div class="sidebar-label">Getting Started</div>' +
|
|
176
|
-
' <nav>' +
|
|
177
|
-
' <a href="' + rootPrefix + 'index.html" class="sidebar-link"><span class="link-icon"></span>Overview</a>' +
|
|
178
|
-
' <a href="' + rootPrefix + 'quick-start.html" class="sidebar-link"><span class="link-icon"></span>Quick Start</a>' +
|
|
179
|
-
' <a href="' + rootPrefix + 'installation.html" class="sidebar-link"><span class="link-icon"></span>Installation</a>' +
|
|
180
|
-
' <a href="' + rootPrefix + 'configuration.html" class="sidebar-link"><span class="link-icon"></span>Configuration</a>' +
|
|
181
|
-
' <a href="' + rootPrefix + 'cli-reference.html" class="sidebar-link"><span class="link-icon"></span>CLI Reference</a>' +
|
|
182
|
-
' <a href="' + rootPrefix + 'troubleshooting.html" class="sidebar-link"><span class="link-icon"></span>Troubleshooting</a>' +
|
|
183
|
-
' </nav>' +
|
|
184
|
-
'</div>' +
|
|
185
|
-
'<div class="sidebar-section">' +
|
|
186
|
-
' <div class="sidebar-label">Core Concepts</div>' +
|
|
187
|
-
' <nav>' +
|
|
188
|
-
' <a href="' + rootPrefix + 'providers.html" class="sidebar-link"><span class="link-icon"></span>Providers</a>' +
|
|
189
|
-
' <a href="' + rootPrefix + 'models.html" class="sidebar-link"><span class="link-icon"></span>Models</a>' +
|
|
190
|
-
' <a href="' + rootPrefix + 'commands.html" class="sidebar-link"><span class="link-icon"></span>Commands</a>' +
|
|
191
|
-
' <a href="' + rootPrefix + 'tools.html" class="sidebar-link"><span class="link-icon"></span>Tools</a>' +
|
|
192
|
-
' <a href="' + rootPrefix + 'permission-model.html" class="sidebar-link"><span class="link-icon"></span>Permission Model</a>' +
|
|
193
|
-
'
|
|
194
|
-
'</
|
|
195
|
-
'
|
|
196
|
-
'
|
|
197
|
-
' <
|
|
198
|
-
'
|
|
199
|
-
' <a href="' + rootPrefix + '
|
|
200
|
-
' <a href="' + rootPrefix + '
|
|
201
|
-
' <a href="' + rootPrefix + '
|
|
202
|
-
'
|
|
203
|
-
'</
|
|
204
|
-
'
|
|
205
|
-
'
|
|
206
|
-
' <
|
|
207
|
-
'
|
|
208
|
-
'
|
|
209
|
-
'</
|
|
210
|
-
'
|
|
211
|
-
'
|
|
212
|
-
' <
|
|
213
|
-
'
|
|
214
|
-
' <a href="' + rootPrefix + '
|
|
215
|
-
' <a href="' + rootPrefix + 'features/
|
|
216
|
-
' <a href="' + rootPrefix + 'features/
|
|
217
|
-
' <a href="' + rootPrefix + 'features/
|
|
218
|
-
' <a href="' + rootPrefix + '
|
|
219
|
-
' <a href="' + rootPrefix + '
|
|
220
|
-
' <a href="' + rootPrefix + '
|
|
221
|
-
'
|
|
222
|
-
'</
|
|
223
|
-
'
|
|
224
|
-
'
|
|
225
|
-
' <
|
|
226
|
-
'
|
|
227
|
-
' <a href="' + rootPrefix + 'internals/
|
|
228
|
-
'
|
|
229
|
-
'</
|
|
230
|
-
'
|
|
231
|
-
'
|
|
232
|
-
' <
|
|
233
|
-
'
|
|
234
|
-
' <a href="' + rootPrefix + '
|
|
235
|
-
' <a href="' + rootPrefix + '
|
|
236
|
-
'
|
|
237
|
-
'</
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
var
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
var
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
wrap
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
header
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
var
|
|
300
|
-
var
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
var
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
lang.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
btn
|
|
311
|
-
btn.
|
|
312
|
-
btn.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
text =
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
btn.
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
btn.
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
header.appendChild(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
wrap.appendChild(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
wrapper
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
label
|
|
368
|
-
label.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
list
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
.
|
|
380
|
-
.replace(
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
link
|
|
386
|
-
link.
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
footer
|
|
426
|
-
footer.
|
|
427
|
-
|
|
428
|
-
'<
|
|
429
|
-
'
|
|
430
|
-
' <a href="https://github.com/ClewCode/ClewCode
|
|
431
|
-
' <a href="https://
|
|
432
|
-
'</
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
1
|
+
// Clew Docs — Mobile Nav, Header, Sidebar, TOC, Code Copy
|
|
2
|
+
(function () {
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
// ── Root Prefix (for script location detection) ──
|
|
6
|
+
var rootPrefix = '';
|
|
7
|
+
var scripts = document.getElementsByTagName('script');
|
|
8
|
+
for (var i = 0; i < scripts.length; i++) {
|
|
9
|
+
var src = scripts[i].getAttribute('src');
|
|
10
|
+
if (src && src.indexOf('js/main.js') !== -1) {
|
|
11
|
+
rootPrefix = src.replace('js/main.js', '');
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ── Header Injection ──
|
|
17
|
+
function injectHeader() {
|
|
18
|
+
var header = document.querySelector('.header');
|
|
19
|
+
if (!header) return;
|
|
20
|
+
|
|
21
|
+
var path = window.location.pathname;
|
|
22
|
+
var isThai = /\.th\.html$/.test(path);
|
|
23
|
+
var isIndex = /\/index(\.th)?\.html$/.test(path) || path === '/' || path.endsWith('/docs/');
|
|
24
|
+
var currentPage = path.split('/').pop() || 'index.html';
|
|
25
|
+
|
|
26
|
+
var logoHref = isThai ? 'index.th.html' : 'index.html';
|
|
27
|
+
var docsHref = isThai ? 'quick-start.th.html' : 'quick-start.html';
|
|
28
|
+
var ariaLabel = isThai ? '\u0E40\u0E1B\u0E34\u0E14/\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39' : 'Toggle navigation';
|
|
29
|
+
|
|
30
|
+
// Language toggle
|
|
31
|
+
var langTarget = isThai
|
|
32
|
+
? currentPage.replace(/\.th\.html$/, '.html')
|
|
33
|
+
: currentPage.replace(/\.html$/, '.th.html');
|
|
34
|
+
var langLabel = isThai ? 'EN' : '\u0E44\u0E17\u0E22';
|
|
35
|
+
// If no .html page exists for the target, just use README
|
|
36
|
+
if (isIndex) langTarget = isThai ? 'index.html' : 'index.th.html';
|
|
37
|
+
|
|
38
|
+
// Build breadcrumbs from path
|
|
39
|
+
var crumbs = [];
|
|
40
|
+
var parts = path.replace(/\/$/, '').split('/').filter(Boolean);
|
|
41
|
+
if (parts[0] === 'docs') parts.shift(); // remove leading "docs"
|
|
42
|
+
if (parts.length > 0) {
|
|
43
|
+
var crumbPath = '';
|
|
44
|
+
for (var i = 0; i < parts.length; i++) {
|
|
45
|
+
crumbPath += (i === 0 ? '' : '/') + parts[i];
|
|
46
|
+
var isLast = i === parts.length - 1;
|
|
47
|
+
var label = parts[i].replace(/\.th\.html$/, '').replace(/\.html$/, '').replace(/-/g, ' ');
|
|
48
|
+
if (isLast) {
|
|
49
|
+
crumbs.push('<span class="current">' + label + '</span>');
|
|
50
|
+
} else {
|
|
51
|
+
crumbs.push('<a href="' + crumbPath + '">' + label + '</a>');
|
|
52
|
+
crumbs.push('<span class="sep">/</span>');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
var breadcrumbsHtml = crumbs.length > 0
|
|
57
|
+
? '<nav class="header-breadcrumbs">' + crumbs.join('') + '</nav>'
|
|
58
|
+
: '';
|
|
59
|
+
|
|
60
|
+
header.innerHTML =
|
|
61
|
+
'<div class="header-inner">' +
|
|
62
|
+
' <a href="' + logoHref + '" class="logo">Clew</a>' +
|
|
63
|
+
breadcrumbsHtml +
|
|
64
|
+
' <div class="header-search">' +
|
|
65
|
+
' <input type="text" id="headerSearchInput" placeholder="' + (isThai ? '\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E40\u0E2D\u0E01\u0E2A\u0E32\u0E23...' : 'Search docs...') + '" autocomplete="off">' +
|
|
66
|
+
' <span class="search-icon">\u2315</span>' +
|
|
67
|
+
' <div class="search-results" id="headerSearchResults"></div>' +
|
|
68
|
+
' </div>' +
|
|
69
|
+
' <nav class="header-nav">' +
|
|
70
|
+
' <a href="' + logoHref + '">' + (isThai ? '\u0E2B\u0E19\u0E49\u0E32\u0E41\u0E23\u0E01' : 'Home') + '</a>' +
|
|
71
|
+
' <a href="' + docsHref + '">' + (isThai ? '\u0E40\u0E2D\u0E01\u0E2A\u0E32\u0E23' : 'Docs') + '</a>' +
|
|
72
|
+
' <a href="https://github.com/JonusNattapong/ClewCode" target="_blank">GitHub</a>' +
|
|
73
|
+
' </nav>' +
|
|
74
|
+
' <button class="lang-toggle" id="langToggle" aria-label="Switch language">' + langLabel + '</button>' +
|
|
75
|
+
' <button class="menu-btn" id="menuToggle" aria-label="' + ariaLabel + '"><span></span><span></span><span></span></button>' +
|
|
76
|
+
'</div>';
|
|
77
|
+
|
|
78
|
+
// Language toggle
|
|
79
|
+
var langToggle = document.getElementById('langToggle');
|
|
80
|
+
if (langToggle) {
|
|
81
|
+
langToggle.addEventListener('click', function () {
|
|
82
|
+
window.location.href = langTarget;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Active nav link
|
|
87
|
+
var navLinks = header.querySelectorAll('.header-nav a');
|
|
88
|
+
navLinks.forEach(function (link) {
|
|
89
|
+
var href = link.getAttribute('href');
|
|
90
|
+
if (!href) return;
|
|
91
|
+
var hrefPage = href.split('/').pop().split('#')[0] || 'index.html';
|
|
92
|
+
if (hrefPage === currentPage) link.classList.add('active');
|
|
93
|
+
});
|
|
94
|
+
if (!isIndex) {
|
|
95
|
+
var docsLink = header.querySelector('.header-nav a[href*="quick-start"]');
|
|
96
|
+
if (docsLink) docsLink.classList.add('active');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Search functionality
|
|
100
|
+
initHeaderSearch();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function initHeaderSearch() {
|
|
104
|
+
var input = document.getElementById('headerSearchInput');
|
|
105
|
+
var results = document.getElementById('headerSearchResults');
|
|
106
|
+
if (!input || !results) return;
|
|
107
|
+
|
|
108
|
+
// Build search index from sidebar links + TOC headings
|
|
109
|
+
var searchIndex = [];
|
|
110
|
+
document.querySelectorAll('.sidebar-link').forEach(function (link) {
|
|
111
|
+
searchIndex.push({ text: link.textContent.trim(), href: link.getAttribute('href'), type: 'page' });
|
|
112
|
+
});
|
|
113
|
+
document.querySelectorAll('.content h2, .content h3').forEach(function (h) {
|
|
114
|
+
searchIndex.push({ text: h.textContent.trim(), href: '#' + h.id, type: 'section' });
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
input.addEventListener('input', function () {
|
|
118
|
+
var query = this.value.trim().toLowerCase();
|
|
119
|
+
results.innerHTML = '';
|
|
120
|
+
results.classList.remove('open');
|
|
121
|
+
if (query.length < 1) return;
|
|
122
|
+
|
|
123
|
+
var matches = [];
|
|
124
|
+
for (var i = 0; i < searchIndex.length; i++) {
|
|
125
|
+
var item = searchIndex[i];
|
|
126
|
+
if (item.text.toLowerCase().indexOf(query) !== -1) {
|
|
127
|
+
matches.push(item);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (matches.length === 0) {
|
|
132
|
+
results.innerHTML = '<div class="search-empty">No results found</div>';
|
|
133
|
+
results.classList.add('open');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (var j = 0; j < Math.min(matches.length, 8); j++) {
|
|
138
|
+
var m = matches[j];
|
|
139
|
+
var highlighted = m.text.replace(
|
|
140
|
+
new RegExp('(' + query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')', 'gi'),
|
|
141
|
+
'<mark>$1</mark>'
|
|
142
|
+
);
|
|
143
|
+
results.innerHTML +=
|
|
144
|
+
'<a href="' + m.href + '" class="search-result">' +
|
|
145
|
+
'<span class="search-type">' + (m.type === 'page' ? '\u{1F4C4}' : '#') + '</span> ' +
|
|
146
|
+
highlighted +
|
|
147
|
+
'</a>';
|
|
148
|
+
}
|
|
149
|
+
results.classList.add('open');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Close on click outside
|
|
153
|
+
document.addEventListener('click', function (e) {
|
|
154
|
+
if (!input.contains(e.target) && !results.contains(e.target)) {
|
|
155
|
+
results.classList.remove('open');
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Close on Escape
|
|
160
|
+
input.addEventListener('keydown', function (e) {
|
|
161
|
+
if (e.key === 'Escape') {
|
|
162
|
+
results.classList.remove('open');
|
|
163
|
+
input.blur();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
injectHeader();
|
|
169
|
+
|
|
170
|
+
// ── Sidebar Injection ──
|
|
171
|
+
var sidebar = document.getElementById('sidebar');
|
|
172
|
+
if (sidebar) {
|
|
173
|
+
sidebar.innerHTML =
|
|
174
|
+
'<div class="sidebar-section">' +
|
|
175
|
+
' <div class="sidebar-label">Getting Started</div>' +
|
|
176
|
+
' <nav>' +
|
|
177
|
+
' <a href="' + rootPrefix + 'index.html" class="sidebar-link"><span class="link-icon"></span>Overview</a>' +
|
|
178
|
+
' <a href="' + rootPrefix + 'quick-start.html" class="sidebar-link"><span class="link-icon"></span>Quick Start</a>' +
|
|
179
|
+
' <a href="' + rootPrefix + 'installation.html" class="sidebar-link"><span class="link-icon"></span>Installation</a>' +
|
|
180
|
+
' <a href="' + rootPrefix + 'configuration.html" class="sidebar-link"><span class="link-icon"></span>Configuration</a>' +
|
|
181
|
+
' <a href="' + rootPrefix + 'cli-reference.html" class="sidebar-link"><span class="link-icon"></span>CLI Reference</a>' +
|
|
182
|
+
' <a href="' + rootPrefix + 'troubleshooting.html" class="sidebar-link"><span class="link-icon"></span>Troubleshooting</a>' +
|
|
183
|
+
' </nav>' +
|
|
184
|
+
'</div>' +
|
|
185
|
+
'<div class="sidebar-section">' +
|
|
186
|
+
' <div class="sidebar-label">Core Concepts</div>' +
|
|
187
|
+
' <nav>' +
|
|
188
|
+
' <a href="' + rootPrefix + 'providers.html" class="sidebar-link"><span class="link-icon"></span>Providers</a>' +
|
|
189
|
+
' <a href="' + rootPrefix + 'models.html" class="sidebar-link"><span class="link-icon"></span>Models</a>' +
|
|
190
|
+
' <a href="' + rootPrefix + 'commands.html" class="sidebar-link"><span class="link-icon"></span>Commands</a>' +
|
|
191
|
+
' <a href="' + rootPrefix + 'tools.html" class="sidebar-link"><span class="link-icon"></span>Tools</a>' +
|
|
192
|
+
' <a href="' + rootPrefix + 'permission-model.html" class="sidebar-link"><span class="link-icon"></span>Permission Model</a>' +
|
|
193
|
+
' <a href="' + rootPrefix + 'personal-profile.html" class="sidebar-link"><span class="link-icon"></span>Personal Profile</a>' +
|
|
194
|
+
' </nav>' +
|
|
195
|
+
'</div>' +
|
|
196
|
+
'<div class="sidebar-section">' +
|
|
197
|
+
' <div class="sidebar-label">Extending</div>' +
|
|
198
|
+
' <nav>' +
|
|
199
|
+
' <a href="' + rootPrefix + 'plugins.html" class="sidebar-link"><span class="link-icon"></span>Plugins</a>' +
|
|
200
|
+
' <a href="' + rootPrefix + 'skills.html" class="sidebar-link"><span class="link-icon"></span>Skills</a>' +
|
|
201
|
+
' <a href="' + rootPrefix + 'architecture.html" class="sidebar-link"><span class="link-icon"></span>Architecture</a>' +
|
|
202
|
+
' <a href="' + rootPrefix + 'mcp.html" class="sidebar-link"><span class="link-icon"></span>MCP</a>' +
|
|
203
|
+
' </nav>' +
|
|
204
|
+
'</div>' +
|
|
205
|
+
'<div class="sidebar-section">' +
|
|
206
|
+
' <div class="sidebar-label">Autonomous</div>' +
|
|
207
|
+
' <nav>' +
|
|
208
|
+
' <a href="' + rootPrefix + 'daemon.html" class="sidebar-link"><span class="link-icon"></span>Daemon Mode</a>' +
|
|
209
|
+
' </nav>' +
|
|
210
|
+
'</div>' +
|
|
211
|
+
'<div class="sidebar-section">' +
|
|
212
|
+
' <div class="sidebar-label">Features</div>' +
|
|
213
|
+
' <nav>' +
|
|
214
|
+
' <a href="' + rootPrefix + 'research-memory.html" class="sidebar-link"><span class="link-icon"></span>Research & Memory</a>' +
|
|
215
|
+
' <a href="' + rootPrefix + 'features/searxng-search.html" class="sidebar-link"><span class="link-icon"></span>SearXNG Search</a>' +
|
|
216
|
+
' <a href="' + rootPrefix + 'features/bridge-mode.html" class="sidebar-link"><span class="link-icon"></span>Bridge Mode</a>' +
|
|
217
|
+
' <a href="' + rootPrefix + 'features/evals.html" class="sidebar-link"><span class="link-icon"></span>Evaluation Harness</a>' +
|
|
218
|
+
' <a href="' + rootPrefix + 'features/sentry-setup.html" class="sidebar-link"><span class="link-icon"></span>Sentry Setup</a>' +
|
|
219
|
+
' <a href="' + rootPrefix + 'voice-mode.html" class="sidebar-link"><span class="link-icon"></span>Voice Mode</a>' +
|
|
220
|
+
' <a href="' + rootPrefix + 'swarm.html" class="sidebar-link"><span class="link-icon"></span>Swarm System</a>' +
|
|
221
|
+
' <a href="' + rootPrefix + 'loop.html" class="sidebar-link"><span class="link-icon"></span>Agent Loop</a>' +
|
|
222
|
+
' </nav>' +
|
|
223
|
+
'</div>' +
|
|
224
|
+
'<div class="sidebar-section">' +
|
|
225
|
+
' <div class="sidebar-label">Internals</div>' +
|
|
226
|
+
' <nav>' +
|
|
227
|
+
' <a href="' + rootPrefix + 'internals/hidden-features.html" class="sidebar-link"><span class="link-icon"></span>Hidden Features</a>' +
|
|
228
|
+
' <a href="' + rootPrefix + 'internals/growthbook-ab-testing.html" class="sidebar-link"><span class="link-icon"></span>A/B Testing</a>' +
|
|
229
|
+
' </nav>' +
|
|
230
|
+
'</div>' +
|
|
231
|
+
'<div class="sidebar-section">' +
|
|
232
|
+
' <div class="sidebar-label">Community</div>' +
|
|
233
|
+
' <nav>' +
|
|
234
|
+
' <a href="' + rootPrefix + 'changelog.html" class="sidebar-link"><span class="link-icon"></span>Changelog</a>' +
|
|
235
|
+
' <a href="' + rootPrefix + 'security.html" class="sidebar-link"><span class="link-icon"></span>Security</a>' +
|
|
236
|
+
' <a href="' + rootPrefix + 'contributing.html" class="sidebar-link"><span class="link-icon"></span>Contributing</a>' +
|
|
237
|
+
' </nav>' +
|
|
238
|
+
'</div>';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ── Mobile Nav ──
|
|
242
|
+
var menuBtn = document.getElementById('menuToggle');
|
|
243
|
+
var overlay = document.getElementById('sidebarOverlay');
|
|
244
|
+
|
|
245
|
+
function openSidebar() {
|
|
246
|
+
sidebar && sidebar.classList.add('open');
|
|
247
|
+
menuBtn && menuBtn.setAttribute('aria-expanded', 'true');
|
|
248
|
+
overlay && overlay.classList.add('open');
|
|
249
|
+
document.body.style.overflow = 'hidden';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function closeSidebar() {
|
|
253
|
+
sidebar && sidebar.classList.remove('open');
|
|
254
|
+
menuBtn && menuBtn.setAttribute('aria-expanded', 'false');
|
|
255
|
+
overlay && overlay.classList.remove('open');
|
|
256
|
+
document.body.style.overflow = '';
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (menuBtn) menuBtn.addEventListener('click', function (e) {
|
|
260
|
+
e.stopPropagation();
|
|
261
|
+
sidebar && sidebar.classList.contains('open') ? closeSidebar() : openSidebar();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
if (overlay) overlay.addEventListener('click', closeSidebar);
|
|
265
|
+
|
|
266
|
+
document.addEventListener('keydown', function (e) {
|
|
267
|
+
if (e.key === 'Escape' && sidebar && sidebar.classList.contains('open')) closeSidebar();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (sidebar) {
|
|
271
|
+
sidebar.querySelectorAll('.sidebar-link').forEach(function (link) {
|
|
272
|
+
link.addEventListener('click', closeSidebar);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ── Active Sidebar Link ──
|
|
277
|
+
var currentPath = window.location.pathname;
|
|
278
|
+
var currentPage = currentPath.split('/').pop() || 'index.html';
|
|
279
|
+
if (currentPage === '') currentPage = 'index.html';
|
|
280
|
+
|
|
281
|
+
document.querySelectorAll('.sidebar-link').forEach(function (link) {
|
|
282
|
+
var href = link.getAttribute('href');
|
|
283
|
+
if (!href) return;
|
|
284
|
+
var hrefPage = href.split('/').pop().split('#')[0];
|
|
285
|
+
if (hrefPage === currentPage) link.classList.add('active');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// ── Code Copy Buttons ──
|
|
289
|
+
function addCopyButtons() {
|
|
290
|
+
document.querySelectorAll('.content pre').forEach(function (pre) {
|
|
291
|
+
if (pre.parentElement && pre.parentElement.classList.contains('code-block-wrap')) return;
|
|
292
|
+
|
|
293
|
+
var wrap = document.createElement('div');
|
|
294
|
+
wrap.className = 'code-block-wrap';
|
|
295
|
+
|
|
296
|
+
var header = document.createElement('div');
|
|
297
|
+
header.className = 'code-block-header';
|
|
298
|
+
|
|
299
|
+
var lang = document.createElement('span');
|
|
300
|
+
var code = pre.querySelector('code');
|
|
301
|
+
var langName = '';
|
|
302
|
+
if (code) {
|
|
303
|
+
var cls = code.className || '';
|
|
304
|
+
var match = cls.match(/language-(\w+)/);
|
|
305
|
+
if (match) langName = match[1];
|
|
306
|
+
}
|
|
307
|
+
lang.textContent = langName || '';
|
|
308
|
+
lang.style.textTransform = 'lowercase';
|
|
309
|
+
|
|
310
|
+
var btn = document.createElement('button');
|
|
311
|
+
btn.className = 'copy-btn';
|
|
312
|
+
btn.textContent = 'Copy';
|
|
313
|
+
btn.setAttribute('aria-label', 'Copy code to clipboard');
|
|
314
|
+
|
|
315
|
+
btn.addEventListener('click', function () {
|
|
316
|
+
var text = pre.textContent || '';
|
|
317
|
+
text = text.replace(/^\n+|\n+$/g, '');
|
|
318
|
+
navigator.clipboard.writeText(text).then(function () {
|
|
319
|
+
btn.textContent = 'Copied!';
|
|
320
|
+
btn.classList.add('copied');
|
|
321
|
+
setTimeout(function () {
|
|
322
|
+
btn.textContent = 'Copy';
|
|
323
|
+
btn.classList.remove('copied');
|
|
324
|
+
}, 2000);
|
|
325
|
+
}).catch(function () {
|
|
326
|
+
btn.textContent = 'Failed';
|
|
327
|
+
setTimeout(function () {
|
|
328
|
+
btn.textContent = 'Copy';
|
|
329
|
+
}, 2000);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
header.appendChild(lang);
|
|
334
|
+
header.appendChild(btn);
|
|
335
|
+
|
|
336
|
+
pre.parentNode.insertBefore(wrap, pre);
|
|
337
|
+
wrap.appendChild(header);
|
|
338
|
+
wrap.appendChild(pre);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
addCopyButtons();
|
|
343
|
+
|
|
344
|
+
// ── Table Wrapping ──
|
|
345
|
+
document.querySelectorAll('.content table').forEach(function (table) {
|
|
346
|
+
if (table.parentElement && table.parentElement.classList.contains('table-wrap')) return;
|
|
347
|
+
var wrapper = document.createElement('div');
|
|
348
|
+
wrapper.className = 'table-wrap';
|
|
349
|
+
table.parentNode.insertBefore(wrapper, table);
|
|
350
|
+
wrapper.appendChild(table);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// ── TOC Generation ──
|
|
354
|
+
function buildTOC() {
|
|
355
|
+
var toc = document.querySelector('.toc-sidebar');
|
|
356
|
+
if (!toc) return;
|
|
357
|
+
|
|
358
|
+
var content = document.querySelector('.content');
|
|
359
|
+
if (!content) return;
|
|
360
|
+
|
|
361
|
+
var headings = content.querySelectorAll('h2, h3');
|
|
362
|
+
if (headings.length < 2) {
|
|
363
|
+
toc.style.display = 'none';
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
var label = document.createElement('div');
|
|
368
|
+
label.className = 'toc-label';
|
|
369
|
+
label.textContent = 'On this page';
|
|
370
|
+
toc.appendChild(label);
|
|
371
|
+
|
|
372
|
+
var list = document.createElement('nav');
|
|
373
|
+
list.className = 'toc-list';
|
|
374
|
+
|
|
375
|
+
var items = [];
|
|
376
|
+
headings.forEach(function (h) {
|
|
377
|
+
if (!h.id) {
|
|
378
|
+
var text = (h.textContent || '')
|
|
379
|
+
.toLowerCase()
|
|
380
|
+
.replace(/[^a-z0-9\u0E00-\u0E7F]+/g, '-')
|
|
381
|
+
.replace(/^-|-$/g, '');
|
|
382
|
+
h.id = text || 'section-' + Math.random().toString(36).slice(2, 6);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
var link = document.createElement('a');
|
|
386
|
+
link.href = '#' + h.id;
|
|
387
|
+
link.className = 'toc-link';
|
|
388
|
+
if (h.tagName === 'H3') link.classList.add('toc-h3');
|
|
389
|
+
link.textContent = h.textContent;
|
|
390
|
+
|
|
391
|
+
list.appendChild(link);
|
|
392
|
+
items.push({ el: link, id: h.id });
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
toc.appendChild(list);
|
|
396
|
+
|
|
397
|
+
if (typeof IntersectionObserver !== 'undefined' && items.length > 0) {
|
|
398
|
+
var observeTargets = [];
|
|
399
|
+
headings.forEach(function (h) { observeTargets.push(h); });
|
|
400
|
+
|
|
401
|
+
var observer = new IntersectionObserver(function (entries) {
|
|
402
|
+
entries.forEach(function (entry) {
|
|
403
|
+
if (entry.isIntersecting) {
|
|
404
|
+
items.forEach(function (item) {
|
|
405
|
+
item.el.classList.toggle('active', item.id === entry.target.id);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}, {
|
|
410
|
+
rootMargin: '-64px 0px -60% 0px',
|
|
411
|
+
threshold: 0
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
observeTargets.forEach(function (h) { observer.observe(h); });
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
buildTOC();
|
|
419
|
+
|
|
420
|
+
// ── Footer Injection ──
|
|
421
|
+
function injectFooter() {
|
|
422
|
+
var content = document.querySelector('.content');
|
|
423
|
+
if (!content || content.querySelector('.footer')) return;
|
|
424
|
+
|
|
425
|
+
var footer = document.createElement('div');
|
|
426
|
+
footer.className = 'footer';
|
|
427
|
+
footer.innerHTML =
|
|
428
|
+
'<span>Clew Code \u2014 GPL-3.0</span>' +
|
|
429
|
+
'<div class="footer-links">' +
|
|
430
|
+
' <a href="https://github.com/ClewCode/ClewCode">GitHub</a>' +
|
|
431
|
+
' <a href="https://github.com/ClewCode/ClewCode/issues">Issues</a>' +
|
|
432
|
+
' <a href="https://www.npmjs.com/package/clew-code">npm</a>' +
|
|
433
|
+
'</div>';
|
|
434
|
+
content.appendChild(footer);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
injectFooter();
|
|
438
|
+
})();
|