robotframework-libtoc 1.4.2__tar.gz → 1.5.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: robotframework-libtoc
3
- Version: 1.4.2
3
+ Version: 1.5.0
4
4
  Summary: Docs and TOC generator for Robot Framework resources and libs
5
5
  Home-page: https://github.com/amochin/robotframework-libtoc
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "robotframework-libtoc"
3
- version = "1.4.2"
3
+ version = "1.5.0"
4
4
  description = "Docs and TOC generator for Robot Framework resources and libs"
5
5
  authors = ["Andre Mochinin"]
6
6
  license = "Apache-2.0"
@@ -0,0 +1,31 @@
1
+ <html>
2
+ <head>
3
+ <meta charset="utf-8">
4
+ <style>
5
+ :root { --bg: white; --text: #333; --text-muted: #666; --kbd-bg: #f3f3f3; --kbd-border: #e0e0e2; }
6
+ [data-theme=dark] { --bg: #1c2227; --text: #e2e1d7; --text-muted: #a8b2b8; --kbd-bg: #002b36; --kbd-border: #4e4e4e; color-scheme: dark; }
7
+ body { font-family: system-ui, -apple-system, sans-serif; color: var(--text); background: var(--bg); max-width: 640px; margin: 60px auto; padding: 0 24px; text-align: center; }
8
+ h1 { font-weight: 600; font-size: 24px; }
9
+ p { color: var(--text-muted); font-size: 15px; line-height: 1.6; }
10
+ kbd { background: var(--kbd-bg); border: 1px solid var(--kbd-border); border-radius: 4px; padding: 2px 7px; font-family: monospace; font-size: 12px; }
11
+ </style>
12
+ <script>!function(){var t=localStorage.getItem('libtoc-theme')||(window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light');document.documentElement.setAttribute('data-theme',t);document.addEventListener('keydown',function(e){if((e.ctrlKey||e.metaKey)&&e.key==='k'){e.preventDefault();window.parent.postMessage('libtoc-open-search','*')}})}()</script>
13
+ </head>
14
+ <body>
15
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
16
+ viewBox="0 0 202.4325 202.34125" height="120" width="120">
17
+ <g transform="matrix(1.25,0,0,-1.25,0,202.34125)">
18
+ <g>
19
+ <g clip-path="none">
20
+ <g transform="translate(52.4477,88.1268)">
21
+ <path fill="#00c0b5"
22
+ d="m 0,0 c 0,7.6 6.179,13.779 13.77,13.779 7.6,0 13.779,-6.179 13.779,-13.779 0,-2.769 -2.238,-5.007 -4.998,-5.007 -2.761,0 -4.999,2.238 -4.999,5.007 0,2.078 -1.695,3.765 -3.782,3.765 C 11.693,3.765 9.997,2.078 9.997,0 9.997,-2.769 7.76,-5.007 4.999,-5.007 2.238,-5.007 0,-2.769 0,0 m 57.05,-23.153 c 0,-2.771 -2.237,-5.007 -4.998,-5.007 l -46.378,0 c -2.761,0 -4.999,2.236 -4.999,5.007 0,2.769 2.238,5.007 4.999,5.007 l 46.378,0 c 2.761,0 4.998,-2.238 4.998,-5.007 M 35.379,-2.805 c -1.545,2.291 -0.941,5.398 1.35,6.943 l 11.594,7.83 c 2.273,1.58 5.398,0.941 6.943,-1.332 1.545,-2.29 0.941,-5.398 -1.35,-6.943 l -11.594,-7.83 c -0.852,-0.586 -1.829,-0.87 -2.788,-0.87 -1.607,0 -3.187,0.781 -4.155,2.202 m 31.748,-30.786 c 0,-0.945 -0.376,-1.852 -1.045,-2.522 l -8.617,-8.617 c -0.669,-0.668 -1.576,-1.045 -2.523,-1.045 l -52.833,0 c -0.947,0 -1.854,0.377 -2.523,1.045 l -8.617,8.617 c -0.669,0.67 -1.045,1.577 -1.045,2.522 l 0,52.799 c 0,0.947 0.376,1.854 1.045,2.522 l 8.617,8.619 c 0.669,0.668 1.576,1.044 2.523,1.044 l 52.833,0 c 0.947,0 1.854,-0.376 2.523,-1.044 l 8.617,-8.619 c 0.669,-0.668 1.045,-1.575 1.045,-2.522 l 0,-52.799 z m 7.334,61.086 -11.25,11.25 c -1.705,1.705 -4.018,2.663 -6.428,2.663 l -56.523,0 c -2.412,0 -4.725,-0.959 -6.43,-2.665 L -17.412,27.494 c -1.704,-1.705 -2.661,-4.016 -2.661,-6.427 l 0,-56.515 c 0,-2.411 0.958,-4.725 2.663,-6.428 l 11.25,-11.25 c 1.705,-1.705 4.017,-2.662 6.428,-2.662 l 56.515,0 c 2.41,0 4.723,0.957 6.428,2.662 l 11.25,11.25 c 1.705,1.703 2.663,4.017 2.663,6.428 l 0,56.514 c 0,2.412 -0.958,4.724 -2.663,6.429" />
23
+ </g>
24
+ </g>
25
+ </g>
26
+ </g>
27
+ </svg>
28
+ <h1>Keyword Documentation</h1>
29
+ <p>Select a library or resource from the sidebar, or press <kbd>Ctrl+K</kbd> to search.</p>
30
+ </body>
31
+ </html>
@@ -1,6 +1,8 @@
1
1
  import argparse
2
2
  import glob
3
+ import json
3
4
  import os
5
+ import re
4
6
  import shutil
5
7
  import sys
6
8
  from datetime import datetime
@@ -15,7 +17,7 @@ class LibdocException(Exception):
15
17
  self.broken_file = broken_file
16
18
 
17
19
 
18
- def toc(links, timestamp, home_page_path, template_file=""):
20
+ def toc(links, timestamp, home_page_path, template_file="", search_index=None):
19
21
  """
20
22
  Returns a HTML source code for TOC (table of contents) page, based on the template and including
21
23
  the provided `links`, generation `timestamp` and the `home_page_path` HTML file as a landing page.
@@ -25,27 +27,31 @@ def toc(links, timestamp, home_page_path, template_file=""):
25
27
  with open(template_file, encoding="utf8") as f:
26
28
  html_template = f.read()
27
29
 
28
- # double all brackets to make the further formatting work
29
- html_with_escaped_braces = html_template.replace("{", "{{")
30
- html_with_escaped_braces = html_with_escaped_braces.replace("}", "}}")
30
+ result = html_template.replace("{}", home_page_path, 1)
31
+ result = result.replace("{}", links, 1)
32
+ result = result.replace("{}", timestamp, 1)
31
33
 
32
- # and convert the formatting brackets back
33
- html_with_escaped_braces = html_with_escaped_braces.replace("{{}}", "{}")
34
+ # inject search index data (done after format() to avoid brace escaping issues)
35
+ if search_index is not None:
36
+ search_json = json.dumps(search_index, ensure_ascii=False)
37
+ search_json = search_json.replace("</script>", r"<\/script>")
38
+ result = result.replace("SEARCH_INDEX_DATA", search_json)
39
+ else:
40
+ result = result.replace("SEARCH_INDEX_DATA", "[]")
34
41
 
35
- return html_with_escaped_braces.format(home_page_path, links, timestamp)
42
+ return result
36
43
 
37
44
 
38
- def homepage(timestamp, template_file=""):
45
+ def homepage(template_file=""):
39
46
  """
40
- Returns a HTML source code for a landing page, based on the template and includig the provided `timestamp`.
47
+ Returns a HTML source code for a landing page, based on the template.
41
48
  """
42
49
  if template_file == "":
43
50
  template_file = os.path.join(
44
51
  os.path.dirname(__file__), "homepage_template.html"
45
52
  )
46
53
  with open(template_file, encoding="utf_8") as f:
47
- html_template = f.read()
48
- return html_template.format(timestamp)
54
+ return f.read()
49
55
 
50
56
 
51
57
  def read_config(config_file):
@@ -96,6 +102,117 @@ def read_config(config_file):
96
102
  }
97
103
 
98
104
 
105
+ def extract_libdoc_data(html_file_path):
106
+ """
107
+ Extracts the libdoc JSON data from a generated libdoc HTML file.
108
+ The libdoc variable is embedded as a JSON object in a script tag.
109
+ """
110
+ with open(html_file_path, encoding="utf-8") as f:
111
+ content = f.read()
112
+ for line in content.split("\n"):
113
+ stripped = line.strip()
114
+ if stripped.startswith("libdoc") and '"specversion"' in stripped:
115
+ json_start = stripped.index("{")
116
+ json_str = stripped[json_start:]
117
+ try:
118
+ return json.loads(json_str)
119
+ except json.JSONDecodeError:
120
+ print(f"Warning: Failed to parse libdoc JSON (for global searches) in file {html_file_path}")
121
+ return None
122
+
123
+
124
+ def build_search_index(src_dir, base_dir):
125
+ """
126
+ Builds a search index from all libdoc HTML files in src_dir.
127
+ Returns a list of library/resource entries with their keywords.
128
+ """
129
+ index = []
130
+ for dirpath, dirnames, filenames in os.walk(src_dir):
131
+ dirnames.sort()
132
+ for file_name in sorted(filenames):
133
+ if file_name.endswith(".html") and file_name != "homepage.html":
134
+ file_path = os.path.join(dirpath, file_name)
135
+ data = extract_libdoc_data(file_path)
136
+ if data:
137
+ rel_path = os.path.relpath(file_path, base_dir)
138
+ rel_path = rel_path.replace("\\", "/")
139
+ keywords = []
140
+ for kw in data.get("keywords", []):
141
+ args_list = []
142
+ for a in kw.get("args", []):
143
+ if isinstance(a, str):
144
+ args_list.append(a)
145
+ elif isinstance(a, dict):
146
+ args_list.append(
147
+ a.get("repr", a.get("name", ""))
148
+ )
149
+ keywords.append(
150
+ {
151
+ "name": kw.get("name", ""),
152
+ "args": ", ".join(args_list),
153
+ "shortdoc": kw.get("shortdoc", ""),
154
+ "tags": kw.get("tags", []),
155
+ }
156
+ )
157
+ doc_text = re.sub(r"<[^>]+>", "", data.get("doc", ""))
158
+ # file_name without .html extension for file searching
159
+ name_without_ext = os.path.splitext(file_name)[0]
160
+ index.append(
161
+ {
162
+ "name": data.get("name", ""),
163
+ "fileName": name_without_ext,
164
+ "path": rel_path,
165
+ "doc": doc_text,
166
+ "type": data.get("type", ""),
167
+ "keywords": keywords,
168
+ }
169
+ )
170
+ return index
171
+
172
+
173
+ def inject_libtoc_script(src_dir):
174
+ """
175
+ Injects a small <script> into each libdoc HTML file in src_dir that:
176
+ - Reads the theme from localStorage and applies data-theme on <html> so the
177
+ page respects the libtoc theme choice even when file:// cross-origin prevents
178
+ parent frame DOM access.
179
+ - Opens all external links (http/https/ftp/protocol-relative) in a new tab via
180
+ a click event listener, so dynamically-rendered links are handled correctly.
181
+ - Forwards Ctrl+K / Cmd+K keypresses to the parent frame to open the libtoc search.
182
+ """
183
+ libtoc_script = (
184
+ '\n<script>!function(){var t=localStorage.getItem("libtoc-theme")'
185
+ '||(window.matchMedia("(prefers-color-scheme:dark)").matches?"dark":"light");'
186
+ 'document.documentElement.setAttribute("data-theme",t);'
187
+ 'document.addEventListener("DOMContentLoaded",function(){'
188
+ 'document.documentElement.setAttribute("data-theme",t)});'
189
+ 'document.addEventListener("click",function(e){'
190
+ 'var el=e.target.closest("a");'
191
+ 'if(el){var h=el.getAttribute("href");'
192
+ 'if(h&&/^(https?:|ftp:\\/\\/|\\/\\/)/i.test(h)){'
193
+ 'e.preventDefault();'
194
+ 'window.open(h,"_blank","noopener,noreferrer")}}});'
195
+ 'document.addEventListener("keydown",function(e){'
196
+ 'if((e.ctrlKey||e.metaKey)&&e.key==="k"){'
197
+ 'e.preventDefault();'
198
+ 'window.parent.postMessage("libtoc-open-search","*")}})'
199
+ '}()</script>\n'
200
+ )
201
+ for dirpath, dirnames, filenames in os.walk(src_dir):
202
+ dirnames.sort()
203
+ for file_name in sorted(filenames):
204
+ if file_name.endswith(".html") and file_name != "homepage.html":
205
+ file_path = os.path.join(dirpath, file_name)
206
+ with open(file_path, "r", encoding="utf-8") as f:
207
+ content = f.read()
208
+ # Insert before </head> - uses DOMContentLoaded + setTimeout
209
+ # to override libdoc's own theme initialization
210
+ if "libtoc-theme" not in content:
211
+ content = content.replace("</head>", libtoc_script + "</head>", 1)
212
+ with open(file_path, "w", encoding="utf-8") as f:
213
+ f.write(content)
214
+
215
+
99
216
  def add_files_from_folder(folder, base_dir_path, root=True):
100
217
  """
101
218
  Creates a HTML source code with links to all HTML files in the `folder` and all it's subfolders.
@@ -113,7 +230,7 @@ def add_files_from_folder(folder, base_dir_path, root=True):
113
230
  result_str += """<div class="collapsible_content">
114
231
  """
115
232
 
116
- for item in os.listdir(folder):
233
+ for item in sorted(os.listdir(folder)):
117
234
  item_path = os.path.abspath(os.path.join(folder, item))
118
235
  if item.endswith(".html"):
119
236
  name_without_ext = os.path.splitext(item)[0]
@@ -262,7 +379,13 @@ def create_toc(
262
379
  current_date_time = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
263
380
  doc_files_links = add_files_from_folder(src_subdir, os.path.abspath(html_docs_dir))
264
381
  with open(homepage_path, "w", encoding="utf8") as f:
265
- f.write(homepage(current_date_time, homepage_template))
382
+ f.write(homepage(homepage_template))
383
+
384
+ # build search index from generated docs
385
+ search_index = build_search_index(src_subdir, os.path.abspath(html_docs_dir))
386
+
387
+ # inject libtoc script into all libdoc HTML files
388
+ inject_libtoc_script(src_subdir)
266
389
 
267
390
  # create TOC
268
391
  toc_file_path = os.path.join(html_docs_dir, toc_file)
@@ -273,6 +396,7 @@ def create_toc(
273
396
  current_date_time,
274
397
  os.path.relpath(homepage_path, os.path.abspath(html_docs_dir)),
275
398
  toc_template,
399
+ search_index,
276
400
  )
277
401
  )
278
402
 
@@ -0,0 +1,918 @@
1
+ <html>
2
+
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <link rel="icon" type="image/x-icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKcAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAqAAAAAAAAAAAAAAAAAAAALIAAAD/AAAA4AAAANwAAADcAAAA3AAAANwAAADcAAAA3AAAANwAAADcAAAA4AAAAP8AAACxAAAAAAAAAKYAAAD/AAAAuwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC/AAAA/wAAAKkAAAD6AAAAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN8AAAD/AAAA+gAAAMMAAAAAAAAAAgAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAADAAAAAAAAADaAAAA/wAAAPoAAADDAAAAAAAAAIsAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAANEAAAAAAAAA2gAAAP8AAAD6AAAAwwAAAAAAAAAAAAAAMgAAADIAAAAyAAAAMgAAADIAAAAyAAAAMgAAADIAAAAFAAAAAAAAANoAAAD/AAAA+gAAAMMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADaAAAA/wAAAPoAAADDAAAAAAAAADwAAAB8AAAAAAAAAGAAAABcAAAAAAAAAH8AAABKAAAAAAAAAAAAAAAAAAAA2gAAAP8AAAD6AAAAwwAAAAAAAADCAAAA/wAAACkAAADqAAAA4QAAAAAAAAD7AAAA/wAAALAAAAAGAAAAAAAAANoAAAD/AAAA+gAAAMMAAAAAAAAAIwAAAP4AAAD/AAAA/wAAAGAAAAAAAAAAAAAAAMkAAAD/AAAAigAAAAAAAADaAAAA/wAAAPoAAADDAAAAAAAAAAAAAAAIAAAAcAAAABkAAAAAAAAAAAAAAAAAAAAAAAAAEgAAAAAAAAAAAAAA2gAAAP8AAAD7AAAAywAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN4AAAD/AAAAqwAAAP8AAACvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALIAAAD/AAAAsgAAAAAAAAC5AAAA/wAAAMoAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMkAAAD/AAAAvAAAAAAAAAAAAAAAAAAAAKwAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAArQAAAAAAAAAAwAMAAIABAAAf+AAAP/wAAD/8AAAgBAAAP/wAAD/8AAA//AAAJIwAADHEAAA//AAAP/wAAB/4AACAAQAAwAMAAA==">
7
+ <style>
8
+ :root {
9
+ --background-color: white;
10
+ --text-color: black;
11
+ --border-color: #e0e0e2;
12
+ --light-background-color: #f3f3f3;
13
+ --robot-highlight: #00c0b5;
14
+ --highlighted-background-color: yellow;
15
+ --highlighted-text-color: black;
16
+ --less-important-text-color: gray;
17
+ --sidebar-bg: #f8f9fa;
18
+ --sidebar-text: #495057;
19
+ --sidebar-text-hover: #212529;
20
+ --sidebar-text-active: #000000;
21
+ --sidebar-border: #dee2e6;
22
+ --sidebar-accent: #00c0b5;
23
+ }
24
+
25
+ [data-theme=dark] {
26
+ --background-color: #1c2227;
27
+ --text-color: #e2e1d7;
28
+ --border-color: #4e4e4e;
29
+ --light-background-color: #002b36;
30
+ --robot-highlight: yellow;
31
+ --highlighted-background-color: #b8860b;
32
+ --highlighted-text-color: #fff;
33
+ --less-important-text-color: #8fa8ae;
34
+ --sidebar-bg: #131920;
35
+ --sidebar-text: #e2e1d7;
36
+ --sidebar-text-hover: #ffffff;
37
+ --sidebar-text-active: #ffffff;
38
+ --sidebar-border: #4e4e4e;
39
+ --sidebar-accent: #00c0b5;
40
+ color-scheme: dark;
41
+ }
42
+
43
+ body {
44
+ background: var(--background-color);
45
+ color: var(--text-color);
46
+ margin: 0;
47
+ font-family: system-ui, -apple-system, sans-serif;
48
+ }
49
+
50
+ .collapsible {
51
+ background: none;
52
+ color: var(--sidebar-text);
53
+ cursor: pointer;
54
+ padding: 6px 8px 6px 16px;
55
+ width: 100%;
56
+ text-align: left;
57
+ font-size: 16px;
58
+ border: none;
59
+ font-family: inherit;
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 4px;
63
+ }
64
+
65
+ .collapsible::before {
66
+ content: '\25B6';
67
+ font-size: 10px;
68
+ transition: transform 0.15s ease;
69
+ display: inline-block;
70
+ flex-shrink: 0;
71
+ }
72
+
73
+ .collapsible_expanded::before {
74
+ transform: rotate(90deg);
75
+ }
76
+
77
+ .collapsible:hover {
78
+ color: var(--sidebar-text-hover);
79
+ }
80
+
81
+ .collapsible_expanded {
82
+ color: var(--sidebar-text);
83
+ }
84
+
85
+ .collapsible_content {
86
+ padding-left: 12px;
87
+ display: none;
88
+ overflow: hidden;
89
+ }
90
+
91
+ .sidenav {
92
+ height: 100%;
93
+ width: 300px;
94
+ position: fixed;
95
+ z-index: 1;
96
+ top: 0;
97
+ left: 0;
98
+ text-align: left;
99
+ background-color: var(--sidebar-bg);
100
+ overflow: hidden;
101
+ resize: none;
102
+ font-size: 16px;
103
+ display: flex;
104
+ flex-direction: column;
105
+ border-right: 1px solid var(--sidebar-border);
106
+ transition: transform 0.25s ease;
107
+ }
108
+
109
+ .sidenav a:hover {
110
+ color: var(--sidebar-text-hover);
111
+ }
112
+
113
+ .link_not_selected,
114
+ .link_selected {
115
+ padding: 5px 8px 5px 16px;
116
+ text-decoration: none;
117
+ display: block;
118
+ white-space: nowrap;
119
+ overflow: hidden;
120
+ text-overflow: ellipsis;
121
+ }
122
+
123
+ .link_not_selected {
124
+ color: var(--sidebar-text);
125
+ border-left: 3px solid transparent;
126
+ }
127
+
128
+ .link_not_selected:hover {
129
+ background: rgba(0, 0, 0, 0.04);
130
+ }
131
+
132
+ [data-theme=dark] .link_not_selected:hover {
133
+ background: rgba(255, 255, 255, 0.05);
134
+ }
135
+
136
+ .link_selected {
137
+ color: var(--sidebar-text-active);
138
+ background: rgba(0, 192, 181, 0.12);
139
+ border-left: 3px solid var(--sidebar-accent);
140
+ }
141
+
142
+ .sidenav-footer {
143
+ padding: 8px 12px;
144
+ color: var(--less-important-text-color);
145
+ font-size: 16px;
146
+ border-top: 1px solid var(--sidebar-border);
147
+ margin-top: auto;
148
+ flex-shrink: 0;
149
+ }
150
+
151
+ .sidenav-header {
152
+ padding: 12px;
153
+ border-bottom: 1px solid var(--sidebar-border);
154
+ flex-shrink: 0;
155
+ }
156
+
157
+ .sidenav-title {
158
+ display: flex;
159
+ align-items: center;
160
+ gap: 8px;
161
+ padding: 0 4px 8px;
162
+ }
163
+
164
+ .sidenav-title h2 {
165
+ margin: 0;
166
+ font-size: 16px;
167
+ font-weight: 600;
168
+ color: var(--sidebar-text-active);
169
+ white-space: nowrap;
170
+ overflow: hidden;
171
+ text-overflow: ellipsis;
172
+ }
173
+
174
+ .sidenav-title svg {
175
+ flex-shrink: 0;
176
+ }
177
+
178
+ .sidenav-home-link {
179
+ display: flex;
180
+ align-items: center;
181
+ gap: 8px;
182
+ text-decoration: none;
183
+ color: inherit;
184
+ cursor: pointer;
185
+ flex: 1;
186
+ min-width: 0;
187
+ }
188
+
189
+ .sidenav-nav {
190
+ flex: 1;
191
+ overflow-y: auto;
192
+ padding: 8px 0;
193
+ }
194
+
195
+ .sidenav-section-label {
196
+ padding: 12px 16px 4px;
197
+ font-size: 14px;
198
+ font-weight: 600;
199
+ text-transform: uppercase;
200
+ color: var(--less-important-text-color);
201
+ letter-spacing: 0.05em;
202
+ }
203
+
204
+ .sidenav-ext-link {
205
+ padding: 5px 8px 5px 16px;
206
+ text-decoration: none;
207
+ color: var(--sidebar-text);
208
+ display: block;
209
+ font-size: 16px;
210
+ }
211
+
212
+ .sidenav-ext-link:hover {
213
+ color: var(--sidebar-text-hover);
214
+ }
215
+
216
+ .main {
217
+ margin-left: 300px;
218
+ height: 100vh;
219
+ }
220
+
221
+ /* Search Button */
222
+ .search-button {
223
+ background: rgba(0, 0, 0, 0.03);
224
+ color: var(--sidebar-text);
225
+ border: 1px solid var(--sidebar-border);
226
+ border-radius: 6px;
227
+ padding: 8px 10px;
228
+ width: 100%;
229
+ cursor: pointer;
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: space-between;
233
+ font-size: 16px;
234
+ font-family: inherit;
235
+ transition: border-color 0.15s, color 0.15s;
236
+ }
237
+
238
+ [data-theme=dark] .search-button {
239
+ background: rgba(255, 255, 255, 0.06);
240
+ }
241
+
242
+ .search-button:hover {
243
+ color: var(--sidebar-text-hover);
244
+ border-color: var(--sidebar-accent);
245
+ }
246
+
247
+ .search-button-text {
248
+ display: flex;
249
+ align-items: center;
250
+ gap: 8px;
251
+ }
252
+
253
+ .search-kbd {
254
+ background: rgba(0, 0, 0, 0.05);
255
+ border: 1px solid var(--sidebar-border);
256
+ border-radius: 4px;
257
+ padding: 2px 6px;
258
+ font-size: 14px;
259
+ font-family: monospace;
260
+ color: var(--less-important-text-color);
261
+ }
262
+
263
+ [data-theme=dark] .search-kbd {
264
+ background: rgba(255, 255, 255, 0.08);
265
+ }
266
+
267
+ /* Theme Toggle */
268
+ .theme-toggle {
269
+ background: none;
270
+ border: 1px solid var(--sidebar-border);
271
+ border-radius: 6px;
272
+ color: var(--sidebar-text);
273
+ cursor: pointer;
274
+ padding: 6px 8px;
275
+ font-size: 20px;
276
+ line-height: 1;
277
+ transition: color 0.15s, border-color 0.15s;
278
+ flex-shrink: 0;
279
+ }
280
+
281
+ .theme-toggle:hover {
282
+ color: var(--sidebar-text-hover);
283
+ border-color: var(--sidebar-accent);
284
+ }
285
+
286
+ /* Search Modal */
287
+ .search-overlay {
288
+ display: none;
289
+ position: fixed;
290
+ top: 0;
291
+ left: 0;
292
+ right: 0;
293
+ bottom: 0;
294
+ background: rgba(0, 0, 0, 0.5);
295
+ z-index: 1000;
296
+ justify-content: center;
297
+ padding-top: 8vh;
298
+ }
299
+
300
+ .search-overlay.active {
301
+ display: flex;
302
+ }
303
+
304
+ .search-modal {
305
+ background: var(--background-color);
306
+ border: 1px solid var(--border-color);
307
+ border-radius: 12px;
308
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
309
+ width: 620px;
310
+ max-width: 90vw;
311
+ max-height: 72vh;
312
+ display: flex;
313
+ flex-direction: column;
314
+ align-self: flex-start;
315
+ overflow: hidden;
316
+ }
317
+
318
+ .search-input-wrapper {
319
+ display: flex;
320
+ align-items: center;
321
+ padding: 14px 16px;
322
+ border-bottom: 1px solid var(--border-color);
323
+ gap: 10px;
324
+ }
325
+
326
+ .search-input-wrapper svg {
327
+ width: 20px;
328
+ height: 20px;
329
+ color: var(--sidebar-accent);
330
+ flex-shrink: 0;
331
+ }
332
+
333
+ .search-input-wrapper input {
334
+ flex: 1;
335
+ border: none;
336
+ outline: none;
337
+ font-size: 20px;
338
+ font-family: inherit;
339
+ background: transparent;
340
+ color: var(--text-color);
341
+ }
342
+
343
+ .search-input-wrapper input::placeholder {
344
+ color: var(--less-important-text-color);
345
+ }
346
+
347
+ .search-esc {
348
+ background: var(--light-background-color);
349
+ border: 1px solid var(--border-color);
350
+ border-radius: 4px;
351
+ padding: 2px 8px;
352
+ font-size: 14px;
353
+ font-family: monospace;
354
+ color: var(--less-important-text-color);
355
+ cursor: pointer;
356
+ }
357
+
358
+ .search-results {
359
+ overflow-y: auto;
360
+ padding: 4px 0;
361
+ flex: 1;
362
+ }
363
+
364
+ .search-category {
365
+ padding: 10px 16px 4px;
366
+ font-size: 14px;
367
+ font-weight: 600;
368
+ text-transform: uppercase;
369
+ color: var(--less-important-text-color);
370
+ letter-spacing: 0.05em;
371
+ }
372
+
373
+ .search-result {
374
+ padding: 8px 16px;
375
+ cursor: pointer;
376
+ display: flex;
377
+ flex-direction: column;
378
+ border-left: 3px solid transparent;
379
+ transition: background 0.1s;
380
+ }
381
+
382
+ .search-result.selected {
383
+ background: var(--light-background-color);
384
+ border-left-color: var(--robot-highlight);
385
+ }
386
+
387
+ .search-result-name {
388
+ font-weight: 500;
389
+ color: var(--text-color);
390
+ font-size: 16px;
391
+ }
392
+
393
+ .search-result-name mark,
394
+ .search-result-meta mark {
395
+ background: var(--highlighted-background-color);
396
+ color: var(--highlighted-text-color);
397
+ border-radius: 2px;
398
+ padding: 0 1px;
399
+ }
400
+
401
+ .search-result-meta {
402
+ font-size: 16px;
403
+ color: var(--less-important-text-color);
404
+ margin-top: 2px;
405
+ overflow: hidden;
406
+ text-overflow: ellipsis;
407
+ white-space: nowrap;
408
+ }
409
+
410
+ .search-empty {
411
+ padding: 32px 16px;
412
+ text-align: center;
413
+ color: var(--less-important-text-color);
414
+ font-size: 16px;
415
+ }
416
+
417
+ .search-result-type {
418
+ display: inline-block;
419
+ font-size: 14px;
420
+ padding: 1px 6px;
421
+ border-radius: 4px;
422
+ background: rgba(0, 192, 181, 0.12);
423
+ color: var(--robot-highlight);
424
+ margin-left: 8px;
425
+ vertical-align: middle;
426
+ font-weight: 600;
427
+ letter-spacing: 0.02em;
428
+ }
429
+
430
+ .search-footer {
431
+ padding: 8px 16px;
432
+ border-top: 1px solid var(--border-color);
433
+ font-size: 14px;
434
+ color: var(--less-important-text-color);
435
+ display: flex;
436
+ gap: 16px;
437
+ flex-shrink: 0;
438
+ }
439
+
440
+ .search-footer kbd {
441
+ background: var(--light-background-color);
442
+ border: 1px solid var(--border-color);
443
+ border-radius: 3px;
444
+ padding: 1px 5px;
445
+ font-family: monospace;
446
+ font-size: 12px;
447
+ }
448
+
449
+ /* Mobile header bar */
450
+ .mobile-header {
451
+ display: none;
452
+ position: fixed;
453
+ top: 0;
454
+ left: 0;
455
+ right: 0;
456
+ height: 48px;
457
+ z-index: 200;
458
+ background: var(--sidebar-bg);
459
+ border-bottom: 1px solid var(--sidebar-border);
460
+ align-items: center;
461
+ padding: 0 8px;
462
+ gap: 8px;
463
+ }
464
+
465
+ .mobile-header-title {
466
+ font-size: 16px;
467
+ font-weight: 600;
468
+ color: var(--sidebar-text-active);
469
+ white-space: nowrap;
470
+ overflow: hidden;
471
+ text-overflow: ellipsis;
472
+ padding-top: 3px;
473
+ }
474
+
475
+ /* Mobile hamburger button */
476
+ .mobile-menu-btn {
477
+ background: none;
478
+ border: none;
479
+ border-radius: 6px;
480
+ color: var(--sidebar-text);
481
+ cursor: pointer;
482
+ padding: 6px 8px;
483
+ font-size: 22px;
484
+ line-height: 1;
485
+ flex-shrink: 0;
486
+ }
487
+
488
+ .mobile-menu-btn:hover {
489
+ color: var(--sidebar-text-hover);
490
+ }
491
+
492
+ /* Sidenav backdrop */
493
+ .sidenav-backdrop {
494
+ display: none;
495
+ position: fixed;
496
+ top: 0;
497
+ left: 0;
498
+ right: 0;
499
+ bottom: 0;
500
+ background: rgba(0, 0, 0, 0.5);
501
+ z-index: 99;
502
+ }
503
+
504
+ .sidenav-backdrop.active {
505
+ display: block;
506
+ }
507
+
508
+ /* Responsive */
509
+ @media only screen and (max-width: 768px) {
510
+ .mobile-header {
511
+ display: flex;
512
+ }
513
+
514
+ .sidenav {
515
+ transform: translateX(-100%);
516
+ z-index: 100;
517
+ }
518
+
519
+ .sidenav.sidenav-open {
520
+ transform: translateX(0);
521
+ }
522
+
523
+ .main {
524
+ margin-left: 0;
525
+ margin-top: 48px;
526
+ height: calc(100vh - 48px);
527
+ }
528
+ }
529
+ </style>
530
+ </head>
531
+
532
+ <body>
533
+ <script>
534
+ (function () {
535
+ var saved = localStorage.getItem('libtoc-theme');
536
+ var theme = saved || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
537
+ document.documentElement.setAttribute('data-theme', theme);
538
+ })();
539
+ </script>
540
+
541
+ <div class="mobile-header">
542
+ <button class="mobile-menu-btn" id="mobileMenuBtn" onclick="toggleSidenav()" aria-label="Toggle navigation">&#9776;</button>
543
+ <a class="sidenav-home-link" href="#" onclick="navigateToHomepage(); return false;" title="Go to homepage">
544
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
545
+ viewBox="0 0 202.4325 202.34125" height="24" width="24" style="flex-shrink:0;margin-top:3px">
546
+ <g transform="matrix(1.25,0,0,-1.25,0,202.34125)">
547
+ <g>
548
+ <g clip-path="none">
549
+ <g transform="translate(52.4477,88.1268)">
550
+ <path fill="#00c0b5"
551
+ d="m 0,0 c 0,7.6 6.179,13.779 13.77,13.779 7.6,0 13.779,-6.179 13.779,-13.779 0,-2.769 -2.238,-5.007 -4.998,-5.007 -2.761,0 -4.999,2.238 -4.999,5.007 0,2.078 -1.695,3.765 -3.782,3.765 C 11.693,3.765 9.997,2.078 9.997,0 9.997,-2.769 7.76,-5.007 4.999,-5.007 2.238,-5.007 0,-2.769 0,0 m 57.05,-23.153 c 0,-2.771 -2.237,-5.007 -4.998,-5.007 l -46.378,0 c -2.761,0 -4.999,2.236 -4.999,5.007 0,2.769 2.238,5.007 4.999,5.007 l 46.378,0 c 2.761,0 4.998,-2.238 4.998,-5.007 M 35.379,-2.805 c -1.545,2.291 -0.941,5.398 1.35,6.943 l 11.594,7.83 c 2.273,1.58 5.398,0.941 6.943,-1.332 1.545,-2.29 0.941,-5.398 -1.35,-6.943 l -11.594,-7.83 c -0.852,-0.586 -1.829,-0.87 -2.788,-0.87 -1.607,0 -3.187,0.781 -4.155,2.202 m 31.748,-30.786 c 0,-0.945 -0.376,-1.852 -1.045,-2.522 l -8.617,-8.617 c -0.669,-0.668 -1.576,-1.045 -2.523,-1.045 l -52.833,0 c -0.947,0 -1.854,0.377 -2.523,1.045 l -8.617,8.617 c -0.669,0.67 -1.045,1.577 -1.045,2.522 l 0,52.799 c 0,0.947 0.376,1.854 1.045,2.522 l 8.617,8.619 c 0.669,0.668 1.576,1.044 2.523,1.044 l 52.833,0 c 0.947,0 1.854,-0.376 2.523,-1.044 l 8.617,-8.619 c 0.669,-0.668 1.045,-1.575 1.045,-2.522 l 0,-52.799 z m 7.334,61.086 -11.25,11.25 c -1.705,1.705 -4.018,2.663 -6.428,2.663 l -56.523,0 c -2.412,0 -4.725,-0.959 -6.43,-2.665 L -17.412,27.494 c -1.704,-1.705 -2.661,-4.016 -2.661,-6.427 l 0,-56.515 c 0,-2.411 0.958,-4.725 2.663,-6.428 l 11.25,-11.25 c 1.705,-1.705 4.017,-2.662 6.428,-2.662 l 56.515,0 c 2.41,0 4.723,0.957 6.428,2.662 l 11.25,11.25 c 1.705,1.703 2.663,4.017 2.663,6.428 l 0,56.514 c 0,2.412 -0.958,4.724 -2.663,6.429" />
552
+ </g>
553
+ </g>
554
+ </g>
555
+ </g>
556
+ </svg>
557
+ <span class="mobile-header-title">Keyword Documentation</span>
558
+ </a>
559
+ </div>
560
+
561
+ <div id="main" class="main">
562
+ <iframe name="targetFrame" src="{}" allowTransparency="true" frameborder="0" width="100%" height="100%">
563
+ </iframe>
564
+ </div>
565
+
566
+ <div id="sidenavBackdrop" class="sidenav-backdrop" onclick="closeSidenav()"></div>
567
+
568
+ <div id="sidenav" class="sidenav">
569
+ <div class="sidenav-header">
570
+ <div class="sidenav-title">
571
+ <a class="sidenav-home-link" href="#" onclick="navigateToHomepage(); return false;" title="Go to homepage">
572
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
573
+ viewBox="0 0 202.4325 202.34125" height="28" width="28">
574
+ <g transform="matrix(1.25,0,0,-1.25,0,202.34125)">
575
+ <g>
576
+ <g clip-path="none">
577
+ <g transform="translate(52.4477,88.1268)">
578
+ <path fill="#00c0b5"
579
+ d="m 0,0 c 0,7.6 6.179,13.779 13.77,13.779 7.6,0 13.779,-6.179 13.779,-13.779 0,-2.769 -2.238,-5.007 -4.998,-5.007 -2.761,0 -4.999,2.238 -4.999,5.007 0,2.078 -1.695,3.765 -3.782,3.765 C 11.693,3.765 9.997,2.078 9.997,0 9.997,-2.769 7.76,-5.007 4.999,-5.007 2.238,-5.007 0,-2.769 0,0 m 57.05,-23.153 c 0,-2.771 -2.237,-5.007 -4.998,-5.007 l -46.378,0 c -2.761,0 -4.999,2.236 -4.999,5.007 0,2.769 2.238,5.007 4.999,5.007 l 46.378,0 c 2.761,0 4.998,-2.238 4.998,-5.007 M 35.379,-2.805 c -1.545,2.291 -0.941,5.398 1.35,6.943 l 11.594,7.83 c 2.273,1.58 5.398,0.941 6.943,-1.332 1.545,-2.29 0.941,-5.398 -1.35,-6.943 l -11.594,-7.83 c -0.852,-0.586 -1.829,-0.87 -2.788,-0.87 -1.607,0 -3.187,0.781 -4.155,2.202 m 31.748,-30.786 c 0,-0.945 -0.376,-1.852 -1.045,-2.522 l -8.617,-8.617 c -0.669,-0.668 -1.576,-1.045 -2.523,-1.045 l -52.833,0 c -0.947,0 -1.854,0.377 -2.523,1.045 l -8.617,8.617 c -0.669,0.67 -1.045,1.577 -1.045,2.522 l 0,52.799 c 0,0.947 0.376,1.854 1.045,2.522 l 8.617,8.619 c 0.669,0.668 1.576,1.044 2.523,1.044 l 52.833,0 c 0.947,0 1.854,-0.376 2.523,-1.044 l 8.617,-8.619 c 0.669,-0.668 1.045,-1.575 1.045,-2.522 l 0,-52.799 z m 7.334,61.086 -11.25,11.25 c -1.705,1.705 -4.018,2.663 -6.428,2.663 l -56.523,0 c -2.412,0 -4.725,-0.959 -6.43,-2.665 L -17.412,27.494 c -1.704,-1.705 -2.661,-4.016 -2.661,-6.427 l 0,-56.515 c 0,-2.411 0.958,-4.725 2.663,-6.428 l 11.25,-11.25 c 1.705,-1.705 4.017,-2.662 6.428,-2.662 l 56.515,0 c 2.41,0 4.723,0.957 6.428,2.662 l 11.25,11.25 c 1.705,1.703 2.663,4.017 2.663,6.428 l 0,56.514 c 0,2.412 -0.958,4.724 -2.663,6.429" />
580
+ </g>
581
+ </g>
582
+ </g>
583
+ </g>
584
+ </svg>
585
+ <h2>Keyword Documentation</h2>
586
+ </a>
587
+ <button class="theme-toggle" id="themeToggle" onclick="toggleTheme()"
588
+ title="Toggle dark/light mode">&#9790;</button>
589
+ </div>
590
+ <button class="search-button" onclick="openSearch()">
591
+ <span class="search-button-text"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"
592
+ viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
593
+ stroke-linejoin="round">
594
+ <circle cx="11" cy="11" r="8"></circle>
595
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
596
+ </svg>Search</span>
597
+ <kbd class="search-kbd">Ctrl+K</kbd>
598
+ </button>
599
+ </div>
600
+ <div class="sidenav-nav">
601
+ <a class="sidenav-ext-link" href="http://robotframework.org/robotframework/" target="_blank" rel="noopener noreferrer">&#8599;
602
+ Robot Framework Docs</a>
603
+ <div class="sidenav-section-label">Libraries &amp; Resources</div>
604
+ {}
605
+ </div>
606
+ <div class="sidenav-footer">
607
+ Created: {}
608
+ </div>
609
+ </div>
610
+ <script>
611
+ var coll = document.getElementsByClassName("collapsible");
612
+ var i;
613
+ for (i = 0; i < coll.length; i++) {
614
+ coll[i].addEventListener("click", function () {
615
+ this.classList.toggle("collapsible_expanded");
616
+ var content = this.nextElementSibling;
617
+ if (content.style.display === "block") {
618
+ content.style.display = "none";
619
+ }
620
+ else {
621
+ content.style.display = "block";
622
+ }
623
+ });
624
+ }
625
+
626
+ var homepageUrl = document.querySelector('iframe[name="targetFrame"]').getAttribute('src');
627
+ var currentIframeSrc = homepageUrl;
628
+
629
+ function navigateToHomepage() {
630
+ var iframe = document.querySelector('iframe[name="targetFrame"]');
631
+ iframe.src = homepageUrl;
632
+ currentIframeSrc = homepageUrl;
633
+ var activeLinks = document.getElementsByClassName("link_selected");
634
+ for (var i = activeLinks.length - 1; i >= 0; i--) {
635
+ activeLinks[i].className = "link_not_selected";
636
+ }
637
+ }
638
+
639
+ var leaf_links = document.getElementsByClassName("link_not_selected");
640
+ var i;
641
+ for (i = 0; i < leaf_links.length; i++) {
642
+ leaf_links[i].addEventListener("click", function () {
643
+ var active_leaf_links = document.getElementsByClassName("link_selected");
644
+ var j;
645
+ for (j = 0; j < active_leaf_links.length; j++) {
646
+ active_leaf_links[j].className = "link_not_selected";
647
+ }
648
+ this.className = "link_selected";
649
+ currentIframeSrc = this.getAttribute('href');
650
+ closeSidenav();
651
+ });
652
+ }
653
+
654
+ function getTheme() {
655
+ return document.documentElement.getAttribute('data-theme') || 'light';
656
+ }
657
+ function toggleTheme() {
658
+ var current = getTheme();
659
+ var next = current === 'dark' ? 'light' : 'dark';
660
+ localStorage.setItem('libtoc-theme', next);
661
+ document.documentElement.setAttribute('data-theme', next);
662
+ updateThemeIcon(next);
663
+ var iframe = document.querySelector('iframe[name="targetFrame"]');
664
+ if (iframe) iframe.src = currentIframeSrc;
665
+ }
666
+ function updateThemeIcon(theme) {
667
+ var btn = document.getElementById('themeToggle');
668
+ btn.innerHTML = theme === 'dark' ? '&#9788;' : '&#9790;';
669
+ btn.title = theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode';
670
+ }
671
+ updateThemeIcon(getTheme());
672
+
673
+ function openSidenav() {
674
+ document.getElementById('sidenav').classList.add('sidenav-open');
675
+ document.getElementById('sidenavBackdrop').classList.add('active');
676
+ }
677
+ function closeSidenav() {
678
+ document.getElementById('sidenav').classList.remove('sidenav-open');
679
+ document.getElementById('sidenavBackdrop').classList.remove('active');
680
+ }
681
+ function toggleSidenav() {
682
+ if (document.getElementById('sidenav').classList.contains('sidenav-open')) {
683
+ closeSidenav();
684
+ } else {
685
+ openSidenav();
686
+ }
687
+ }
688
+ </script>
689
+ <script>var searchIndex = SEARCH_INDEX_DATA;</script>
690
+ <div id="searchOverlay" class="search-overlay" onclick="handleOverlayClick(event)">
691
+ <div class="search-modal">
692
+ <div class="search-input-wrapper">
693
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
694
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
695
+ <circle cx="11" cy="11" r="8"></circle>
696
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
697
+ </svg>
698
+ <input id="searchInput" type="text" placeholder="Search libraries, keywords, documentation..."
699
+ autocomplete="off">
700
+ <kbd class="search-esc" onclick="closeSearch()">ESC</kbd>
701
+ </div>
702
+ <div id="searchResults" class="search-results">
703
+ <div class="search-empty">Type to search across all libraries and keywords</div>
704
+ </div>
705
+ <div class="search-footer">
706
+ <span><kbd>&uarr;</kbd><kbd>&darr;</kbd> Navigate</span>
707
+ <span><kbd>Enter</kbd> Open</span>
708
+ <span><kbd>Esc</kbd> Close</span>
709
+ </div>
710
+ </div>
711
+ </div>
712
+ <script>
713
+ var selectedResultIndex = -1;
714
+ function escapeHtml(text) {
715
+ var d = document.createElement("div");
716
+ d.textContent = text;
717
+ return d.innerHTML;
718
+ }
719
+ function openSearch() {
720
+ var overlay = document.getElementById("searchOverlay");
721
+ overlay.classList.add("active");
722
+ var input = document.getElementById("searchInput");
723
+ input.value = "";
724
+ input.focus();
725
+ selectedResultIndex = -1;
726
+ showDefaultMessage();
727
+ }
728
+ function closeSearch() {
729
+ document.getElementById("searchOverlay").classList.remove("active");
730
+ selectedResultIndex = -1;
731
+ }
732
+ function handleOverlayClick(e) {
733
+ if (e.target.id === "searchOverlay") {
734
+ closeSearch();
735
+ }
736
+ }
737
+ function showDefaultMessage() {
738
+ document.getElementById("searchResults").innerHTML = '<div class="search-empty">Type to search across all libraries and keywords</div>';
739
+ }
740
+ function highlightMatch(text, query) {
741
+ if (!query) return escapeHtml(text);
742
+ var escaped = escapeHtml(text);
743
+ var lower = escaped.toLowerCase();
744
+ var qLower = query.toLowerCase();
745
+ var idx = lower.indexOf(qLower);
746
+ if (idx === -1) return escaped;
747
+ return escaped.substring(0, idx) + "<mark>" + escaped.substring(idx, idx + query.length) + "</mark>" + escaped.substring(idx + query.length);
748
+ }
749
+ function performSearch(query) {
750
+ if (!query || query.length < 1) {
751
+ showDefaultMessage();
752
+ selectedResultIndex = -1;
753
+ return;
754
+ }
755
+ var q = query.toLowerCase();
756
+ var libraryResults = [];
757
+ var fileResults = [];
758
+ var keywordResults = [];
759
+ var docResults = [];
760
+ for (var i = 0; i < searchIndex.length; i++) {
761
+ var lib = searchIndex[i];
762
+ var nameMatch = lib.name.toLowerCase().indexOf(q) !== -1;
763
+ var fileNameMatch = lib.fileName && lib.fileName.toLowerCase().indexOf(q) !== -1;
764
+ var pathMatch = lib.path.toLowerCase().indexOf(q) !== -1;
765
+ if (nameMatch) {
766
+ libraryResults.push(lib);
767
+ } else if (fileNameMatch || pathMatch) {
768
+ fileResults.push(lib);
769
+ }
770
+ for (var j = 0; j < lib.keywords.length; j++) {
771
+ var kw = lib.keywords[j];
772
+ if (kw.name.toLowerCase().indexOf(q) !== -1) {
773
+ keywordResults.push({ kw: kw, lib: lib });
774
+ } else if (kw.shortdoc.toLowerCase().indexOf(q) !== -1) {
775
+ docResults.push({ kw: kw, lib: lib });
776
+ }
777
+ }
778
+ }
779
+ var html = "";
780
+ var resultCount = 0;
781
+ if (libraryResults.length > 0) {
782
+ html += '<div class="search-category">Libraries / Resources</div>';
783
+ for (var li = 0; li < libraryResults.length && li < 10; li++) {
784
+ var libItem = libraryResults[li];
785
+ html += '<div class="search-result" data-path="' + escapeHtml(libItem.path) + '" data-idx="' + resultCount + '" onclick="navigateToResult(this)">';
786
+ html += '<div class="search-result-name">' + highlightMatch(libItem.name, query) + '<span class="search-result-type">' + escapeHtml(libItem.type) + '</span></div>';
787
+ html += '<div class="search-result-meta">' + escapeHtml(libItem.path) + '</div>';
788
+ html += '</div>';
789
+ resultCount++;
790
+ }
791
+ }
792
+ if (fileResults.length > 0) {
793
+ html += '<div class="search-category">Files</div>';
794
+ for (var fi = 0; fi < fileResults.length && fi < 10; fi++) {
795
+ var fileItem = fileResults[fi];
796
+ html += '<div class="search-result" data-path="' + escapeHtml(fileItem.path) + '" data-idx="' + resultCount + '" onclick="navigateToResult(this)">';
797
+ html += '<div class="search-result-name">' + highlightMatch(fileItem.fileName || fileItem.name, query) + '<span class="search-result-type">' + escapeHtml(fileItem.type) + '</span></div>';
798
+ html += '<div class="search-result-meta">' + highlightMatch(fileItem.path, query) + '</div>';
799
+ html += '</div>';
800
+ resultCount++;
801
+ }
802
+ }
803
+ if (keywordResults.length > 0) {
804
+ html += '<div class="search-category">Keywords</div>';
805
+ for (var ki = 0; ki < keywordResults.length && ki < 20; ki++) {
806
+ var kwItem = keywordResults[ki];
807
+ var argsDisplay = kwItem.kw.args ? " (" + escapeHtml(kwItem.kw.args) + ")" : "";
808
+ html += '<div class="search-result" data-path="' + escapeHtml(kwItem.lib.path) + '" data-hash="' + encodeURIComponent(kwItem.kw.name) + '" data-idx="' + resultCount + '" onclick="navigateToResult(this)">';
809
+ html += '<div class="search-result-name">' + highlightMatch(kwItem.kw.name, query) + '</div>';
810
+ html += '<div class="search-result-meta">' + escapeHtml(kwItem.lib.name) + argsDisplay + '</div>';
811
+ html += '</div>';
812
+ resultCount++;
813
+ }
814
+ }
815
+ if (docResults.length > 0) {
816
+ html += '<div class="search-category">Documentation</div>';
817
+ for (var di = 0; di < docResults.length && di < 10; di++) {
818
+ var docItem = docResults[di];
819
+ html += '<div class="search-result" data-path="' + escapeHtml(docItem.lib.path) + '" data-hash="' + encodeURIComponent(docItem.kw.name) + '" data-idx="' + resultCount + '" onclick="navigateToResult(this)">';
820
+ html += '<div class="search-result-name">' + escapeHtml(docItem.kw.name) + ' <span class="search-result-type">' + escapeHtml(docItem.lib.name) + '</span></div>';
821
+ html += '<div class="search-result-meta">' + highlightMatch(docItem.kw.shortdoc, query) + '</div>';
822
+ html += '</div>';
823
+ resultCount++;
824
+ }
825
+ }
826
+ if (resultCount === 0) {
827
+ html = '<div class="search-empty">No results found for "' + escapeHtml(query) + '"</div>';
828
+ }
829
+ document.getElementById("searchResults").innerHTML = html;
830
+ selectedResultIndex = -1;
831
+ }
832
+ function navigateToResult(el) {
833
+ var path = el.getAttribute("data-path");
834
+ var hash = el.getAttribute("data-hash");
835
+ var iframe = document.querySelector('iframe[name="targetFrame"]');
836
+ var url = path;
837
+ if (hash) {
838
+ url += "#" + hash;
839
+ }
840
+ iframe.src = url;
841
+ currentIframeSrc = url;
842
+ var activeLinks = document.getElementsByClassName("link_selected");
843
+ for (var ai = activeLinks.length - 1; ai >= 0; ai--) {
844
+ activeLinks[ai].className = "link_not_selected";
845
+ }
846
+ var sideLinks = document.querySelectorAll(".sidenav a.link_not_selected, .sidenav a.link_selected");
847
+ for (var si = 0; si < sideLinks.length; si++) {
848
+ var href = sideLinks[si].getAttribute("href");
849
+ if (href) {
850
+ var normalizedHref = href.replace(/\\/g, "/");
851
+ if (normalizedHref === path) {
852
+ sideLinks[si].className = "link_selected";
853
+ break;
854
+ }
855
+ }
856
+ }
857
+ closeSearch();
858
+ }
859
+ function updateSearchSelection(newIdx) {
860
+ var results = document.querySelectorAll("#searchResults .search-result");
861
+ if (newIdx < 0 || newIdx >= results.length) return;
862
+ for (var ui = 0; ui < results.length; ui++) {
863
+ results[ui].classList.remove("selected");
864
+ }
865
+ results[newIdx].classList.add("selected");
866
+ results[newIdx].scrollIntoView({ block: "nearest" });
867
+ selectedResultIndex = newIdx;
868
+ }
869
+ window.addEventListener("message", function (e) {
870
+ if (e.data === "libtoc-open-search") openSearch();
871
+ });
872
+ document.addEventListener("keydown", function (e) {
873
+ if ((e.ctrlKey || e.metaKey) && e.key === "k") {
874
+ e.preventDefault();
875
+ openSearch();
876
+ return;
877
+ }
878
+ var overlay = document.getElementById("searchOverlay");
879
+ if (!overlay.classList.contains("active")) return;
880
+ if (e.key === "Escape") {
881
+ closeSearch();
882
+ e.preventDefault();
883
+ return;
884
+ }
885
+ var results = document.querySelectorAll("#searchResults .search-result");
886
+ if (results.length === 0) return;
887
+ if (e.key === "ArrowDown") {
888
+ e.preventDefault();
889
+ var ndi = selectedResultIndex + 1;
890
+ if (ndi >= results.length) ndi = 0;
891
+ updateSearchSelection(ndi);
892
+ } else if (e.key === "ArrowUp") {
893
+ e.preventDefault();
894
+ var nui = selectedResultIndex - 1;
895
+ if (nui < 0) nui = results.length - 1;
896
+ updateSearchSelection(nui);
897
+ } else if (e.key === "Enter") {
898
+ e.preventDefault();
899
+ if (selectedResultIndex >= 0 && selectedResultIndex < results.length) {
900
+ navigateToResult(results[selectedResultIndex]);
901
+ }
902
+ }
903
+ });
904
+ document.getElementById("searchInput").addEventListener("input", function () {
905
+ performSearch(this.value);
906
+ });
907
+ document.getElementById("searchResults").addEventListener("mouseover", function (e) {
908
+ var el = e.target.closest(".search-result");
909
+ if (!el) return;
910
+ var idx = parseInt(el.getAttribute("data-idx"), 10);
911
+ if (!isNaN(idx) && idx !== selectedResultIndex) {
912
+ updateSearchSelection(idx);
913
+ }
914
+ });
915
+ </script>
916
+ </body>
917
+
918
+ </html>
@@ -1,17 +0,0 @@
1
- <html>
2
- <meta charset="utf-8">
3
- <body style="font-family: 'Lato', sans-serif;">
4
- <h1>
5
- Docs for Robot Framework keywords
6
- </h1>
7
- <p>
8
- Please select a library in the navigation sidebar
9
- </p>
10
- <p>
11
- Created:
12
- <b>
13
- {0}
14
- </b>
15
- </p>
16
- </body>
17
- </html>
@@ -1,95 +0,0 @@
1
- <html>
2
- <head>
3
- <meta charset="utf-8">
4
- <style>
5
- body { font-family: "Lato", sans-serif;}
6
- .collapsible { background: none; color: #818181; cursor: pointer; padding: 6px 6px 6px 32px; width: 100%; text-align:
7
- left; font-size: 16px; border: none;}
8
- .collapsible:hover { color: white;}
9
- .collapsible_expanded { color: #818181; }
10
- .collapsible_content { padding-left: 15px; display: none; overflow: hidden;}
11
- .sidenav { height: 100%; width: 20%; position: fixed; z-index: 1; top: 0; left: 0; text-align: left;
12
- background-color: #111; overflow: auto; resize: none; font-size: 16px;}
13
- .sidenav a:hover { color: #f1f1f1;}
14
- .link_not_selected { padding: 6px 6px 6px 32px; text-decoration: none; color: #818181; display: block;}
15
- .link_selected {padding: 6px 6px 6px 32px; text-decoration: none; color: white; display: block;}
16
- .sidenav p { padding: 0px 0px 0px 12px; color: #818181; position: relative; bottom: 0}
17
- .sidenav_open_button {background: none; cursor: pointer; border:none; font-size: 24px;}
18
- .sidenav_close_button {background: none; color: white; cursor: pointer; width: 100%; text-align:
19
- left; border:none; font-size: 30px; padding-top: 5px; padding-left: 15px}
20
- .main { margin-left: 20%;, height: 100%}
21
- </style>
22
- </head>
23
- <body>
24
-
25
- <div id="main" class="main">
26
- <div>
27
- <button id="openNav" class="sidenav_open_button" onclick="sidenav_open()" style="display:none">&#9776;</button>
28
- </div>
29
- <iframe name="targetFrame" src="{}" allowTransparency="true" frameborder="0" width="100%" height="98%">
30
- </iframe>
31
- </div>
32
-
33
- <div id="sidenav" class="sidenav">
34
- <button class="sidenav_close_button" onclick="sidenav_close()">&times;</button>
35
- <a class="link_not_selected" href="http://robotframework.org/robotframework/" target="targetFrame">Robot Framework Documentation
36
- </a>
37
- <hr>
38
- {}
39
- <hr>
40
- <p>
41
- Created: {}
42
- </p>
43
- </div>
44
- <script>
45
- var coll = document.getElementsByClassName("collapsible");
46
- var i;
47
- for (i = 0; i < coll.length; i++)
48
- {
49
- coll[i].addEventListener("click", function()
50
- {
51
- this.classList.toggle("collapsible_expanded");
52
- var content = this.nextElementSibling;
53
- if (content.style.display === "block")
54
- {
55
- content.style.display = "none";
56
- }
57
- else
58
- {
59
- content.style.display = "block";
60
- }
61
- });
62
- }
63
-
64
- var leaf_links = document.getElementsByClassName("link_not_selected");
65
- var i;
66
- for (i = 0; i < leaf_links.length; i++)
67
- {
68
- leaf_links[i].addEventListener("click", function()
69
- {
70
- var active_leaf_links = document.getElementsByClassName("link_selected");
71
- var j;
72
- for (j=0; j < active_leaf_links.length; j++)
73
- {
74
- active_leaf_links[j].className = "link_not_selected";
75
- }
76
- this.className = "link_selected";
77
- });
78
- }
79
-
80
- function sidenav_open()
81
- {
82
- document.getElementById("main").style.marginLeft = "20%";
83
- document.getElementById("sidenav").style.width = "20%";
84
- document.getElementById("sidenav").style.display = "block";
85
- document.getElementById("openNav").style.display = 'none';
86
- }
87
- function sidenav_close()
88
- {
89
- document.getElementById("main").style.marginLeft = "0%";
90
- document.getElementById("sidenav").style.display = "none";
91
- document.getElementById("openNav").style.display = "inline";
92
- }
93
- </script>
94
- </body>
95
- </html>