@xuda.io/ai_module 1.1.5617 → 1.1.5619
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/.env +1 -0
- package/.sitework/boaz-wiki/assets/boaz-avrahami.png +0 -0
- package/.sitework/boaz-wiki/assets/favicon-180.png +0 -0
- package/.sitework/boaz-wiki/assets/favicon-32.png +0 -0
- package/.sitework/boaz-wiki/assets/favicon.ico +0 -0
- package/.sitework/boaz-wiki/assets/site.js +47 -0
- package/.sitework/boaz-wiki/assets/styles.css +563 -0
- package/.sitework/boaz-wiki/index.html +847 -0
- package/.tmp_boaz_site/assets/boaz-avrahami.png +0 -0
- package/.tmp_boaz_site/assets/site.js +47 -0
- package/.tmp_boaz_site/assets/styles.css +278 -0
- package/.tmp_boaz_site/index.html +845 -0
- package/build_boaz_wiki.py +601 -0
- package/globals.mjs +6 -0
- package/gpt.mjs +155 -0
- package/index copy 2.js +2129 -0
- package/index copy.js +1455 -0
- package/index old.mjs +423 -0
- package/index.js +3751 -0
- package/index_ms.mjs +8 -8
- package/index_msa.mjs +12 -8
- package/package.json +1 -1
- package/test.mjs +2 -0
- package/un-used.mjs +1437 -0
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import html
|
|
4
|
+
import re
|
|
5
|
+
import shutil
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
ROOT = Path("/var/xuda/tmp/codex-test/boaz-wiki")
|
|
10
|
+
SOURCE = ROOT / "boaz_avrahami_updated_with_academic_recognition.md"
|
|
11
|
+
IMAGE = ROOT / "BOAZ_AVRAHAMI_67.png"
|
|
12
|
+
PDF = Path("/var/xuda/tmp/codex-test/boaz-wiki-attachments/Boaz_Globes_CRM_2004.pdf")
|
|
13
|
+
OUT = Path("/tmp/boaz-wiki-site")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def slugify(index: int) -> str:
|
|
17
|
+
return f"section-{index:02d}"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def inline_markup(text: str) -> str:
|
|
21
|
+
text = html.unescape(text)
|
|
22
|
+
links: list[tuple[str, str]] = []
|
|
23
|
+
|
|
24
|
+
def replace_link(match: re.Match[str]) -> str:
|
|
25
|
+
token = f"__LINK_{len(links)}__"
|
|
26
|
+
label = html.escape(match.group(1), quote=False)
|
|
27
|
+
url = html.escape(match.group(2), quote=True)
|
|
28
|
+
links.append(
|
|
29
|
+
(
|
|
30
|
+
token,
|
|
31
|
+
f'<a class="inline-link" href="{url}" target="_blank" rel="noopener noreferrer">{label}</a>',
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
return token
|
|
35
|
+
|
|
36
|
+
text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", replace_link, text)
|
|
37
|
+
text = html.escape(text, quote=False)
|
|
38
|
+
text = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", text)
|
|
39
|
+
text = re.sub(r"`(.+?)`", r"<code>\1</code>", text)
|
|
40
|
+
for token, anchor in links:
|
|
41
|
+
text = text.replace(token, anchor)
|
|
42
|
+
return text
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def paragraph(lines: list[str]) -> str:
|
|
46
|
+
return f"<p>{inline_markup(' '.join(line.strip() for line in lines))}</p>"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def markdown_to_html(markdown: str) -> tuple[str, list[dict[str, str]]]:
|
|
50
|
+
lines = markdown.splitlines()
|
|
51
|
+
first_title = next((i for i, line in enumerate(lines) if line.strip().startswith("# ")), 0)
|
|
52
|
+
lines = lines[first_title:]
|
|
53
|
+
|
|
54
|
+
html_parts: list[str] = []
|
|
55
|
+
headings: list[dict[str, str]] = []
|
|
56
|
+
paragraph_lines: list[str] = []
|
|
57
|
+
list_items: list[str] = []
|
|
58
|
+
section_index = 0
|
|
59
|
+
|
|
60
|
+
def flush_paragraph() -> None:
|
|
61
|
+
nonlocal paragraph_lines
|
|
62
|
+
if paragraph_lines:
|
|
63
|
+
html_parts.append(paragraph(paragraph_lines))
|
|
64
|
+
paragraph_lines = []
|
|
65
|
+
|
|
66
|
+
def flush_list() -> None:
|
|
67
|
+
nonlocal list_items
|
|
68
|
+
if list_items:
|
|
69
|
+
html_parts.append("<ul>" + "".join(f"<li>{item}</li>" for item in list_items) + "</ul>")
|
|
70
|
+
list_items = []
|
|
71
|
+
|
|
72
|
+
for raw in lines:
|
|
73
|
+
line = raw.strip()
|
|
74
|
+
|
|
75
|
+
if not line:
|
|
76
|
+
flush_paragraph()
|
|
77
|
+
flush_list()
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
heading = re.match(r"^(#{1,4})\s+(.+)$", line)
|
|
81
|
+
bullet = re.match(r"^\*\s+(.+)$", line)
|
|
82
|
+
|
|
83
|
+
if heading:
|
|
84
|
+
flush_paragraph()
|
|
85
|
+
flush_list()
|
|
86
|
+
level = len(heading.group(1))
|
|
87
|
+
title = html.unescape(heading.group(2)).strip()
|
|
88
|
+
if level == 1:
|
|
89
|
+
html_parts.append(f'<h1 id="top">{inline_markup(title)}</h1>')
|
|
90
|
+
else:
|
|
91
|
+
section_index += 1
|
|
92
|
+
sid = slugify(section_index)
|
|
93
|
+
if level in (2, 3):
|
|
94
|
+
headings.append({"level": str(level), "title": title, "id": sid})
|
|
95
|
+
html_parts.append(f'<h{level} id="{sid}">{inline_markup(title)}</h{level}>')
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
if bullet:
|
|
99
|
+
flush_paragraph()
|
|
100
|
+
list_items.append(inline_markup(bullet.group(1)))
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
flush_list()
|
|
104
|
+
paragraph_lines.append(line)
|
|
105
|
+
|
|
106
|
+
flush_paragraph()
|
|
107
|
+
flush_list()
|
|
108
|
+
return "\n".join(html_parts), headings
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def build() -> None:
|
|
112
|
+
OUT.mkdir(parents=True, exist_ok=True)
|
|
113
|
+
assets = OUT / "assets"
|
|
114
|
+
assets.mkdir(exist_ok=True)
|
|
115
|
+
shutil.copyfile(IMAGE, assets / "boaz-avrahami.png")
|
|
116
|
+
shutil.copyfile(PDF, assets / "boaz-globes-crm-2004.pdf")
|
|
117
|
+
|
|
118
|
+
source = SOURCE.read_text(encoding="utf-8")
|
|
119
|
+
note = ""
|
|
120
|
+
body_lines = []
|
|
121
|
+
for line in source.splitlines():
|
|
122
|
+
if line.startswith(">"):
|
|
123
|
+
note = line[1:].strip()
|
|
124
|
+
else:
|
|
125
|
+
body_lines.append(line)
|
|
126
|
+
|
|
127
|
+
article_html, headings = markdown_to_html("\n".join(body_lines))
|
|
128
|
+
toc_items = "\n".join(
|
|
129
|
+
f'<a class="toc-link level-{item["level"]}" href="#{item["id"]}">{html.escape(item["title"])}</a>'
|
|
130
|
+
for item in headings
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
(assets / "styles.css").write_text(CSS, encoding="utf-8")
|
|
134
|
+
(assets / "site.js").write_text(JS, encoding="utf-8")
|
|
135
|
+
|
|
136
|
+
page = HTML_TEMPLATE.format(
|
|
137
|
+
article=article_html,
|
|
138
|
+
toc=toc_items,
|
|
139
|
+
note=inline_markup(note),
|
|
140
|
+
)
|
|
141
|
+
(OUT / "index.html").write_text(page, encoding="utf-8")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
HTML_TEMPLATE = """<!doctype html>
|
|
145
|
+
<html lang="he" dir="ltr">
|
|
146
|
+
<head>
|
|
147
|
+
<meta charset="utf-8">
|
|
148
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
149
|
+
<title>בועז אברהמי | ערך ויקי</title>
|
|
150
|
+
<meta name="description" content="ערך ביוגרפי בסגנון ויקי על בועז אברהמי: יזמות טכנולוגית, מערכות מידע, מוזיקה, XUDA, NewDeal ופעילות מקצועית.">
|
|
151
|
+
<meta property="og:title" content="בועז אברהמי | ערך ויקי">
|
|
152
|
+
<meta property="og:description" content="ביוגרפיה אנציקלופדית של בועז אברהמי ופעילותו בתחומי התוכנה, היזמות, המוזיקה והבינה המלאכותית.">
|
|
153
|
+
<meta property="og:image" content="/assets/boaz-avrahami.png">
|
|
154
|
+
<link rel="stylesheet" href="/assets/styles.css">
|
|
155
|
+
<script src="/assets/site.js" defer></script>
|
|
156
|
+
</head>
|
|
157
|
+
<body>
|
|
158
|
+
<div class="progress" aria-hidden="true"><span></span></div>
|
|
159
|
+
<header class="site-header">
|
|
160
|
+
<a class="brand" href="#top" aria-label="בועז אברהמי">
|
|
161
|
+
<span class="brand-mark">BA</span>
|
|
162
|
+
<span>בועז אברהמי</span>
|
|
163
|
+
</a>
|
|
164
|
+
<nav aria-label="ניווט ראשי">
|
|
165
|
+
<a href="#section-04">טכנולוגיה</a>
|
|
166
|
+
<a href="#section-13">XUDA</a>
|
|
167
|
+
<a href="#section-18">השפעה</a>
|
|
168
|
+
</nav>
|
|
169
|
+
</header>
|
|
170
|
+
|
|
171
|
+
<main class="page-shell">
|
|
172
|
+
<aside class="toc-panel" aria-label="תוכן עניינים">
|
|
173
|
+
<div class="toc-tools">
|
|
174
|
+
<label for="toc-search">חיפוש בערך</label>
|
|
175
|
+
<input id="toc-search" type="search" placeholder="חפש נושא, חברה או שנה">
|
|
176
|
+
</div>
|
|
177
|
+
<div class="toc-list">
|
|
178
|
+
{toc}
|
|
179
|
+
</div>
|
|
180
|
+
</aside>
|
|
181
|
+
|
|
182
|
+
<article class="article">
|
|
183
|
+
<div class="source-note">{note}</div>
|
|
184
|
+
{article}
|
|
185
|
+
</article>
|
|
186
|
+
|
|
187
|
+
<aside class="infobox" aria-label="פרטים ביוגרפיים">
|
|
188
|
+
<img src="/assets/boaz-avrahami.png" alt="בועז אברהמי">
|
|
189
|
+
<h2>בועז אברהמי</h2>
|
|
190
|
+
<dl>
|
|
191
|
+
<div><dt>נולד</dt><dd>7 במרץ 1967, ירושלים</dd></div>
|
|
192
|
+
<div><dt>תחומי פעילות</dt><dd>תוכנה, מערכות מידע, יזמות, מוזיקה, AI</dd></div>
|
|
193
|
+
<div><dt>חברות מרכזיות</dt><dd>שיגרה, CouchYou, DocxSite, Ioshka Media, XUDA</dd></div>
|
|
194
|
+
<div><dt>מומחיות</dt><dd>CRM, Workflow, אוטומציית תהליכים, Enterprise AI</dd></div>
|
|
195
|
+
<div><dt>הכרה מקצועית</dt><dd>Seattle Pacific University, 2011</dd></div>
|
|
196
|
+
</dl>
|
|
197
|
+
</aside>
|
|
198
|
+
</main>
|
|
199
|
+
</body>
|
|
200
|
+
</html>
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
CSS = """
|
|
205
|
+
:root {
|
|
206
|
+
--ink: #202225;
|
|
207
|
+
--muted: #5d6672;
|
|
208
|
+
--line: #d8dee7;
|
|
209
|
+
--paper: #fbfbf8;
|
|
210
|
+
--panel: #ffffff;
|
|
211
|
+
--blue: #245f8f;
|
|
212
|
+
--red: #9b3c32;
|
|
213
|
+
--green: #406d54;
|
|
214
|
+
--gold: #a2772c;
|
|
215
|
+
--shadow: 0 18px 45px rgba(32, 34, 37, .08);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
* { box-sizing: border-box; }
|
|
219
|
+
html { scroll-behavior: smooth; }
|
|
220
|
+
body {
|
|
221
|
+
margin: 0;
|
|
222
|
+
background: var(--paper);
|
|
223
|
+
color: var(--ink);
|
|
224
|
+
font-family: Arial, "Noto Sans Hebrew", "Segoe UI", sans-serif;
|
|
225
|
+
line-height: 1.72;
|
|
226
|
+
overflow-x: hidden;
|
|
227
|
+
direction: ltr;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.progress {
|
|
231
|
+
position: fixed;
|
|
232
|
+
inset: 0 0 auto;
|
|
233
|
+
height: 4px;
|
|
234
|
+
z-index: 20;
|
|
235
|
+
background: transparent;
|
|
236
|
+
}
|
|
237
|
+
.progress span {
|
|
238
|
+
display: block;
|
|
239
|
+
width: 0;
|
|
240
|
+
height: 100%;
|
|
241
|
+
background: linear-gradient(90deg, var(--blue), var(--green), var(--gold));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.site-header {
|
|
245
|
+
direction: rtl;
|
|
246
|
+
position: sticky;
|
|
247
|
+
top: 0;
|
|
248
|
+
z-index: 10;
|
|
249
|
+
display: flex;
|
|
250
|
+
align-items: center;
|
|
251
|
+
justify-content: space-between;
|
|
252
|
+
gap: 24px;
|
|
253
|
+
padding: 12px 32px;
|
|
254
|
+
border-bottom: 1px solid var(--line);
|
|
255
|
+
background: rgba(251, 251, 248, .95);
|
|
256
|
+
backdrop-filter: blur(12px);
|
|
257
|
+
}
|
|
258
|
+
.brand {
|
|
259
|
+
display: inline-flex;
|
|
260
|
+
align-items: center;
|
|
261
|
+
gap: 10px;
|
|
262
|
+
color: var(--ink);
|
|
263
|
+
text-decoration: none;
|
|
264
|
+
font-weight: 700;
|
|
265
|
+
}
|
|
266
|
+
.brand-mark {
|
|
267
|
+
direction: ltr;
|
|
268
|
+
display: grid;
|
|
269
|
+
place-items: center;
|
|
270
|
+
width: 38px;
|
|
271
|
+
height: 38px;
|
|
272
|
+
border: 1px solid var(--ink);
|
|
273
|
+
background: var(--ink);
|
|
274
|
+
color: #fff;
|
|
275
|
+
font-size: 13px;
|
|
276
|
+
}
|
|
277
|
+
.site-header nav {
|
|
278
|
+
display: flex;
|
|
279
|
+
gap: 18px;
|
|
280
|
+
flex-wrap: wrap;
|
|
281
|
+
}
|
|
282
|
+
.site-header nav a {
|
|
283
|
+
color: var(--muted);
|
|
284
|
+
text-decoration: none;
|
|
285
|
+
font-size: 14px;
|
|
286
|
+
}
|
|
287
|
+
.site-header nav a:hover { color: var(--blue); }
|
|
288
|
+
|
|
289
|
+
.page-shell {
|
|
290
|
+
direction: rtl;
|
|
291
|
+
display: grid;
|
|
292
|
+
grid-template-columns: minmax(210px, 280px) minmax(0, 860px) minmax(260px, 330px);
|
|
293
|
+
gap: 28px;
|
|
294
|
+
align-items: start;
|
|
295
|
+
max-width: 1540px;
|
|
296
|
+
margin: 0 auto;
|
|
297
|
+
padding: 28px 28px 64px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.toc-panel,
|
|
301
|
+
.infobox {
|
|
302
|
+
position: sticky;
|
|
303
|
+
top: 86px;
|
|
304
|
+
border: 1px solid var(--line);
|
|
305
|
+
background: var(--panel);
|
|
306
|
+
box-shadow: var(--shadow);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.toc-panel {
|
|
310
|
+
max-height: calc(100vh - 108px);
|
|
311
|
+
overflow: auto;
|
|
312
|
+
padding: 16px;
|
|
313
|
+
}
|
|
314
|
+
.toc-tools label {
|
|
315
|
+
display: block;
|
|
316
|
+
margin-bottom: 8px;
|
|
317
|
+
color: var(--muted);
|
|
318
|
+
font-size: 13px;
|
|
319
|
+
font-weight: 700;
|
|
320
|
+
}
|
|
321
|
+
.toc-tools input {
|
|
322
|
+
width: 100%;
|
|
323
|
+
border: 1px solid var(--line);
|
|
324
|
+
padding: 10px 11px;
|
|
325
|
+
background: #f7f8fa;
|
|
326
|
+
color: var(--ink);
|
|
327
|
+
font: inherit;
|
|
328
|
+
}
|
|
329
|
+
.toc-list {
|
|
330
|
+
display: grid;
|
|
331
|
+
gap: 2px;
|
|
332
|
+
margin-top: 14px;
|
|
333
|
+
}
|
|
334
|
+
.toc-link {
|
|
335
|
+
border-right: 3px solid transparent;
|
|
336
|
+
padding: 7px 9px;
|
|
337
|
+
color: var(--ink);
|
|
338
|
+
text-decoration: none;
|
|
339
|
+
font-size: 14px;
|
|
340
|
+
}
|
|
341
|
+
.toc-link.level-3 {
|
|
342
|
+
padding-right: 22px;
|
|
343
|
+
color: var(--muted);
|
|
344
|
+
font-size: 13px;
|
|
345
|
+
}
|
|
346
|
+
.toc-link:hover,
|
|
347
|
+
.toc-link.active {
|
|
348
|
+
border-color: var(--blue);
|
|
349
|
+
background: #eef4f8;
|
|
350
|
+
color: var(--blue);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.article {
|
|
354
|
+
min-width: 0;
|
|
355
|
+
border: 1px solid var(--line);
|
|
356
|
+
background: var(--panel);
|
|
357
|
+
padding: 38px 48px 52px;
|
|
358
|
+
box-shadow: var(--shadow);
|
|
359
|
+
}
|
|
360
|
+
.source-note {
|
|
361
|
+
border-right: 4px solid var(--gold);
|
|
362
|
+
margin-bottom: 24px;
|
|
363
|
+
padding: 10px 14px;
|
|
364
|
+
background: #faf6ec;
|
|
365
|
+
color: #65502a;
|
|
366
|
+
font-size: 14px;
|
|
367
|
+
}
|
|
368
|
+
.article h1 {
|
|
369
|
+
margin: 0 0 16px;
|
|
370
|
+
padding-bottom: 16px;
|
|
371
|
+
border-bottom: 1px solid var(--line);
|
|
372
|
+
font-size: 42px;
|
|
373
|
+
line-height: 1.15;
|
|
374
|
+
}
|
|
375
|
+
.article h2 {
|
|
376
|
+
margin: 42px 0 14px;
|
|
377
|
+
padding-bottom: 8px;
|
|
378
|
+
border-bottom: 1px solid var(--line);
|
|
379
|
+
color: var(--blue);
|
|
380
|
+
font-size: 26px;
|
|
381
|
+
line-height: 1.3;
|
|
382
|
+
}
|
|
383
|
+
.article h3 {
|
|
384
|
+
margin: 30px 0 10px;
|
|
385
|
+
color: var(--red);
|
|
386
|
+
font-size: 20px;
|
|
387
|
+
line-height: 1.35;
|
|
388
|
+
}
|
|
389
|
+
.article p,
|
|
390
|
+
.article li {
|
|
391
|
+
font-size: 17px;
|
|
392
|
+
}
|
|
393
|
+
.article a {
|
|
394
|
+
color: var(--blue);
|
|
395
|
+
text-decoration: underline;
|
|
396
|
+
text-underline-offset: 2px;
|
|
397
|
+
text-decoration-thickness: 1px;
|
|
398
|
+
}
|
|
399
|
+
.article a:hover {
|
|
400
|
+
color: var(--red);
|
|
401
|
+
}
|
|
402
|
+
.article p {
|
|
403
|
+
margin: 0 0 15px;
|
|
404
|
+
}
|
|
405
|
+
.article ul {
|
|
406
|
+
margin: 0 0 18px;
|
|
407
|
+
padding: 0 24px 0 0;
|
|
408
|
+
}
|
|
409
|
+
.article strong { color: #14171a; }
|
|
410
|
+
|
|
411
|
+
.infobox {
|
|
412
|
+
overflow: hidden;
|
|
413
|
+
}
|
|
414
|
+
.infobox img {
|
|
415
|
+
display: block;
|
|
416
|
+
width: 100%;
|
|
417
|
+
max-width: 100%;
|
|
418
|
+
aspect-ratio: 4 / 5;
|
|
419
|
+
object-fit: cover;
|
|
420
|
+
object-position: center top;
|
|
421
|
+
border-bottom: 1px solid var(--line);
|
|
422
|
+
}
|
|
423
|
+
.infobox h2 {
|
|
424
|
+
margin: 0;
|
|
425
|
+
padding: 16px 18px 6px;
|
|
426
|
+
font-size: 22px;
|
|
427
|
+
}
|
|
428
|
+
.infobox dl {
|
|
429
|
+
margin: 0;
|
|
430
|
+
padding: 8px 18px 18px;
|
|
431
|
+
}
|
|
432
|
+
.infobox div {
|
|
433
|
+
display: grid;
|
|
434
|
+
grid-template-columns: 82px minmax(0, 1fr);
|
|
435
|
+
gap: 12px;
|
|
436
|
+
padding: 10px 0;
|
|
437
|
+
border-top: 1px solid #eef1f4;
|
|
438
|
+
}
|
|
439
|
+
.infobox dt {
|
|
440
|
+
color: var(--muted);
|
|
441
|
+
font-weight: 700;
|
|
442
|
+
font-size: 13px;
|
|
443
|
+
}
|
|
444
|
+
.infobox dd {
|
|
445
|
+
margin: 0;
|
|
446
|
+
font-size: 14px;
|
|
447
|
+
min-width: 0;
|
|
448
|
+
overflow-wrap: anywhere;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
mark {
|
|
452
|
+
background: #ffe28a;
|
|
453
|
+
color: var(--ink);
|
|
454
|
+
padding: 0 2px;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
@media (max-width: 1180px) {
|
|
458
|
+
.page-shell {
|
|
459
|
+
grid-template-columns: minmax(0, 1fr) minmax(250px, 310px);
|
|
460
|
+
}
|
|
461
|
+
.toc-panel {
|
|
462
|
+
position: relative;
|
|
463
|
+
top: 0;
|
|
464
|
+
grid-column: 1 / -1;
|
|
465
|
+
max-height: none;
|
|
466
|
+
}
|
|
467
|
+
.toc-list {
|
|
468
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
@media (max-width: 820px) {
|
|
473
|
+
.site-header {
|
|
474
|
+
display: grid;
|
|
475
|
+
grid-template-columns: 1fr;
|
|
476
|
+
align-items: flex-start;
|
|
477
|
+
padding: 10px 72px 10px 16px;
|
|
478
|
+
overflow: hidden;
|
|
479
|
+
}
|
|
480
|
+
.brand {
|
|
481
|
+
justify-self: start;
|
|
482
|
+
}
|
|
483
|
+
.brand-mark {
|
|
484
|
+
display: none;
|
|
485
|
+
}
|
|
486
|
+
.site-header nav {
|
|
487
|
+
display: grid;
|
|
488
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
489
|
+
width: 100%;
|
|
490
|
+
gap: 8px;
|
|
491
|
+
}
|
|
492
|
+
.site-header nav a {
|
|
493
|
+
min-width: 0;
|
|
494
|
+
text-align: center;
|
|
495
|
+
}
|
|
496
|
+
.page-shell {
|
|
497
|
+
display: flex;
|
|
498
|
+
flex-direction: column;
|
|
499
|
+
width: 100%;
|
|
500
|
+
max-width: 100%;
|
|
501
|
+
padding: 16px 72px 42px 14px;
|
|
502
|
+
overflow: hidden;
|
|
503
|
+
direction: ltr;
|
|
504
|
+
}
|
|
505
|
+
.article,
|
|
506
|
+
.toc-panel,
|
|
507
|
+
.infobox {
|
|
508
|
+
width: auto;
|
|
509
|
+
min-width: 0;
|
|
510
|
+
max-width: 100%;
|
|
511
|
+
align-self: stretch;
|
|
512
|
+
position: static;
|
|
513
|
+
direction: rtl;
|
|
514
|
+
}
|
|
515
|
+
.article {
|
|
516
|
+
order: 3;
|
|
517
|
+
padding: 26px 20px 34px;
|
|
518
|
+
}
|
|
519
|
+
.infobox {
|
|
520
|
+
order: 1;
|
|
521
|
+
}
|
|
522
|
+
.infobox div {
|
|
523
|
+
grid-template-columns: 1fr;
|
|
524
|
+
gap: 3px;
|
|
525
|
+
}
|
|
526
|
+
.toc-panel {
|
|
527
|
+
order: 2;
|
|
528
|
+
}
|
|
529
|
+
.toc-list {
|
|
530
|
+
grid-template-columns: 1fr;
|
|
531
|
+
}
|
|
532
|
+
.article h1 {
|
|
533
|
+
font-size: 34px;
|
|
534
|
+
}
|
|
535
|
+
.article h2 {
|
|
536
|
+
font-size: 23px;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
"""
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
JS = """
|
|
543
|
+
const progress = document.querySelector('.progress span');
|
|
544
|
+
const links = [...document.querySelectorAll('.toc-link')];
|
|
545
|
+
const headings = links
|
|
546
|
+
.map(link => document.querySelector(link.getAttribute('href')))
|
|
547
|
+
.filter(Boolean);
|
|
548
|
+
|
|
549
|
+
function updateProgress() {
|
|
550
|
+
const height = document.documentElement.scrollHeight - window.innerHeight;
|
|
551
|
+
const percent = height > 0 ? (window.scrollY / height) * 100 : 0;
|
|
552
|
+
progress.style.width = `${percent}%`;
|
|
553
|
+
|
|
554
|
+
let current = headings[0];
|
|
555
|
+
for (const heading of headings) {
|
|
556
|
+
if (heading.getBoundingClientRect().top < 140) current = heading;
|
|
557
|
+
}
|
|
558
|
+
links.forEach(link => {
|
|
559
|
+
link.classList.toggle('active', current && link.getAttribute('href') === `#${current.id}`);
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function clearMarks() {
|
|
564
|
+
document.querySelectorAll('mark').forEach(mark => {
|
|
565
|
+
mark.replaceWith(document.createTextNode(mark.textContent));
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
document.getElementById('toc-search').addEventListener('input', event => {
|
|
570
|
+
clearMarks();
|
|
571
|
+
const query = event.target.value.trim();
|
|
572
|
+
const article = document.querySelector('.article');
|
|
573
|
+
links.forEach(link => {
|
|
574
|
+
link.hidden = query && !link.textContent.includes(query);
|
|
575
|
+
});
|
|
576
|
+
if (!query) return;
|
|
577
|
+
|
|
578
|
+
const walker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
|
|
579
|
+
const matches = [];
|
|
580
|
+
while (walker.nextNode()) {
|
|
581
|
+
const node = walker.currentNode;
|
|
582
|
+
const index = node.nodeValue.indexOf(query);
|
|
583
|
+
if (index >= 0 && matches.length < 20) matches.push([node, index]);
|
|
584
|
+
}
|
|
585
|
+
for (const [node, index] of matches) {
|
|
586
|
+
const range = document.createRange();
|
|
587
|
+
range.setStart(node, index);
|
|
588
|
+
range.setEnd(node, index + query.length);
|
|
589
|
+
const mark = document.createElement('mark');
|
|
590
|
+
range.surroundContents(mark);
|
|
591
|
+
}
|
|
592
|
+
document.querySelector('mark')?.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
window.addEventListener('scroll', updateProgress, { passive: true });
|
|
596
|
+
updateProgress();
|
|
597
|
+
"""
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
if __name__ == "__main__":
|
|
601
|
+
build()
|