kash-shell 0.3.9__py3-none-any.whl → 0.3.11__py3-none-any.whl

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.
Files changed (151) hide show
  1. kash/actions/__init__.py +4 -4
  2. kash/actions/core/format_markdown_template.py +2 -5
  3. kash/actions/core/markdownify.py +7 -6
  4. kash/actions/core/readability.py +7 -6
  5. kash/actions/core/render_as_html.py +37 -0
  6. kash/actions/core/show_webpage.py +6 -11
  7. kash/actions/core/strip_html.py +2 -6
  8. kash/actions/core/tabbed_webpage_config.py +31 -0
  9. kash/actions/core/{webpage_generate.py → tabbed_webpage_generate.py} +5 -4
  10. kash/commands/__init__.py +8 -20
  11. kash/commands/base/basic_file_commands.py +15 -0
  12. kash/commands/base/debug_commands.py +13 -0
  13. kash/commands/base/files_command.py +28 -10
  14. kash/commands/base/general_commands.py +21 -16
  15. kash/commands/base/logs_commands.py +4 -2
  16. kash/commands/base/model_commands.py +8 -8
  17. kash/commands/base/search_command.py +3 -2
  18. kash/commands/base/show_command.py +5 -3
  19. kash/commands/extras/parse_uv_lock.py +186 -0
  20. kash/commands/help/doc_commands.py +2 -31
  21. kash/commands/help/welcome.py +33 -0
  22. kash/commands/workspace/selection_commands.py +11 -6
  23. kash/commands/workspace/workspace_commands.py +19 -17
  24. kash/config/colors.py +3 -1
  25. kash/config/env_settings.py +14 -1
  26. kash/config/init.py +2 -2
  27. kash/config/logger.py +59 -56
  28. kash/config/logger_basic.py +3 -3
  29. kash/config/settings.py +116 -57
  30. kash/config/setup.py +28 -12
  31. kash/config/text_styles.py +3 -13
  32. kash/docs/load_api_docs.py +2 -1
  33. kash/docs/markdown/topics/a3_getting_started.md +3 -2
  34. kash/{concepts → embeddings}/text_similarity.py +2 -2
  35. kash/exec/__init__.py +20 -3
  36. kash/exec/action_decorators.py +24 -10
  37. kash/exec/action_exec.py +41 -23
  38. kash/exec/action_registry.py +13 -48
  39. kash/exec/command_registry.py +2 -1
  40. kash/exec/fetch_url_metadata.py +4 -6
  41. kash/exec/importing.py +56 -0
  42. kash/exec/llm_transforms.py +12 -10
  43. kash/exec/precondition_registry.py +2 -1
  44. kash/exec/preconditions.py +22 -1
  45. kash/exec/resolve_args.py +4 -0
  46. kash/exec/shell_callable_action.py +33 -19
  47. kash/file_storage/file_store.py +42 -27
  48. kash/file_storage/item_file_format.py +5 -2
  49. kash/file_storage/metadata_dirs.py +11 -2
  50. kash/help/assistant.py +1 -1
  51. kash/help/assistant_instructions.py +2 -1
  52. kash/help/function_param_info.py +1 -1
  53. kash/help/help_embeddings.py +2 -2
  54. kash/help/help_printing.py +7 -11
  55. kash/llm_utils/clean_headings.py +1 -1
  56. kash/llm_utils/llm_api_keys.py +4 -4
  57. kash/llm_utils/llm_features.py +68 -0
  58. kash/llm_utils/llm_messages.py +1 -2
  59. kash/llm_utils/llm_names.py +1 -1
  60. kash/llm_utils/llms.py +8 -3
  61. kash/local_server/__init__.py +5 -2
  62. kash/local_server/local_server.py +8 -5
  63. kash/local_server/local_server_commands.py +2 -2
  64. kash/local_server/local_server_routes.py +1 -7
  65. kash/local_server/local_url_formatters.py +1 -1
  66. kash/mcp/__init__.py +5 -2
  67. kash/mcp/mcp_cli.py +5 -5
  68. kash/mcp/mcp_server_commands.py +5 -5
  69. kash/mcp/mcp_server_routes.py +5 -5
  70. kash/mcp/mcp_server_sse.py +4 -2
  71. kash/media_base/media_cache.py +8 -8
  72. kash/media_base/media_services.py +1 -1
  73. kash/media_base/media_tools.py +6 -6
  74. kash/media_base/services/local_file_media.py +2 -2
  75. kash/media_base/{speech_transcription.py → transcription_deepgram.py} +25 -110
  76. kash/media_base/transcription_format.py +73 -0
  77. kash/media_base/transcription_whisper.py +38 -0
  78. kash/model/__init__.py +73 -5
  79. kash/model/actions_model.py +38 -4
  80. kash/model/concept_model.py +30 -0
  81. kash/model/items_model.py +115 -32
  82. kash/model/params_model.py +24 -0
  83. kash/shell/completions/completion_scoring.py +37 -5
  84. kash/shell/output/kerm_codes.py +1 -2
  85. kash/shell/output/shell_formatting.py +14 -4
  86. kash/shell/shell_main.py +2 -2
  87. kash/shell/utils/exception_printing.py +6 -0
  88. kash/shell/utils/native_utils.py +26 -20
  89. kash/shell/utils/shell_function_wrapper.py +15 -15
  90. kash/text_handling/custom_sliding_transforms.py +12 -4
  91. kash/text_handling/doc_normalization.py +6 -2
  92. kash/text_handling/markdown_render.py +118 -0
  93. kash/text_handling/markdown_utils.py +226 -0
  94. kash/utils/common/function_inspect.py +360 -110
  95. kash/utils/common/import_utils.py +12 -3
  96. kash/utils/common/type_utils.py +0 -29
  97. kash/utils/common/url.py +27 -3
  98. kash/utils/errors.py +6 -0
  99. kash/utils/file_utils/file_ext.py +4 -0
  100. kash/utils/file_utils/file_formats.py +2 -2
  101. kash/utils/file_utils/file_formats_model.py +20 -1
  102. kash/web_content/dir_store.py +1 -2
  103. kash/web_content/file_cache_utils.py +37 -10
  104. kash/web_content/file_processing.py +68 -0
  105. kash/web_content/local_file_cache.py +12 -9
  106. kash/web_content/web_extract.py +8 -3
  107. kash/web_content/web_fetch.py +12 -4
  108. kash/web_gen/__init__.py +0 -4
  109. kash/web_gen/simple_webpage.py +52 -0
  110. kash/web_gen/tabbed_webpage.py +24 -14
  111. kash/web_gen/template_render.py +37 -2
  112. kash/web_gen/templates/base_styles.css.jinja +169 -43
  113. kash/web_gen/templates/base_webpage.html.jinja +110 -45
  114. kash/web_gen/templates/content_styles.css.jinja +4 -2
  115. kash/web_gen/templates/item_view.html.jinja +49 -39
  116. kash/web_gen/templates/simple_webpage.html.jinja +24 -0
  117. kash/web_gen/templates/tabbed_webpage.html.jinja +42 -33
  118. kash/workspaces/__init__.py +15 -2
  119. kash/workspaces/selections.py +18 -3
  120. kash/workspaces/source_items.py +0 -1
  121. kash/workspaces/workspaces.py +5 -11
  122. kash/xonsh_custom/command_nl_utils.py +40 -19
  123. kash/xonsh_custom/custom_shell.py +43 -11
  124. kash/xonsh_custom/customize_prompt.py +39 -21
  125. kash/xonsh_custom/load_into_xonsh.py +22 -25
  126. kash/xonsh_custom/shell_load_commands.py +2 -2
  127. kash/xonsh_custom/xonsh_completers.py +2 -249
  128. kash/xonsh_custom/xonsh_keybindings.py +282 -0
  129. kash/xonsh_custom/xonsh_modern_tools.py +3 -3
  130. kash/xontrib/kash_extension.py +5 -6
  131. {kash_shell-0.3.9.dist-info → kash_shell-0.3.11.dist-info}/METADATA +10 -8
  132. {kash_shell-0.3.9.dist-info → kash_shell-0.3.11.dist-info}/RECORD +137 -136
  133. kash/actions/core/webpage_config.py +0 -21
  134. kash/concepts/concept_formats.py +0 -23
  135. kash/shell/clideps/api_keys.py +0 -100
  136. kash/shell/clideps/dotenv_setup.py +0 -115
  137. kash/shell/clideps/dotenv_utils.py +0 -98
  138. kash/shell/clideps/pkg_deps.py +0 -257
  139. kash/shell/clideps/platforms.py +0 -11
  140. kash/shell/clideps/terminal_features.py +0 -56
  141. kash/shell/utils/osc_utils.py +0 -95
  142. kash/shell/utils/terminal_images.py +0 -133
  143. kash/text_handling/markdown_util.py +0 -167
  144. kash/utils/common/atomic_var.py +0 -171
  145. kash/utils/common/string_replace.py +0 -93
  146. kash/utils/common/string_template.py +0 -101
  147. /kash/{concepts → embeddings}/cosine.py +0 -0
  148. /kash/{concepts → embeddings}/embeddings.py +0 -0
  149. {kash_shell-0.3.9.dist-info → kash_shell-0.3.11.dist-info}/WHEEL +0 -0
  150. {kash_shell-0.3.9.dist-info → kash_shell-0.3.11.dist-info}/entry_points.txt +0 -0
  151. {kash_shell-0.3.9.dist-info → kash_shell-0.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -1,12 +1,45 @@
1
+ from collections.abc import Iterator
2
+ from contextlib import contextmanager
3
+ from contextvars import ContextVar
1
4
  from pathlib import Path
2
5
 
3
6
  from jinja2 import Environment, FileSystemLoader
4
7
 
5
8
  from kash.config import colors
6
9
 
10
+ _base_templates_dir = Path(__file__).parent / "templates"
11
+ """Common base web page templates."""
12
+
13
+
14
+ _additional_template_dirs: ContextVar[list[Path] | None] = ContextVar(
15
+ "_additional_template_dirs", default=None
16
+ )
17
+
18
+
19
+ def get_template_dirs(*dirs: Path) -> list[Path]:
20
+ """
21
+ Returns template directories currently in context along with any
22
+ additional template directories.
23
+ """
24
+ additional = _additional_template_dirs.get() or []
25
+ return list(dirs) + [*additional, _base_templates_dir]
26
+
27
+
28
+ @contextmanager
29
+ def additional_template_dirs(*dirs: Path) -> Iterator[None]:
30
+ """
31
+ Context manager for temporarily adding template directories to the search path.
32
+ """
33
+ original = _additional_template_dirs.get()
34
+ current = [] if not original else original.copy()
35
+ token = _additional_template_dirs.set(current + list(dirs))
36
+ try:
37
+ yield
38
+ finally:
39
+ _additional_template_dirs.reset(token)
40
+
7
41
 
8
42
  def render_web_template(
9
- templates_dir: Path,
10
43
  template_filename: str,
11
44
  data: dict,
12
45
  autoescape: bool = True,
@@ -14,11 +47,13 @@ def render_web_template(
14
47
  ) -> str:
15
48
  """
16
49
  Render a Jinja2 template file with the given data, returning an HTML string.
50
+ Uses template directories from the base directory and any added via context manager.
17
51
  """
18
52
  if css_overrides is None:
19
53
  css_overrides = {}
20
54
 
21
- env = Environment(loader=FileSystemLoader(templates_dir), autoescape=autoescape)
55
+ search_paths = get_template_dirs()
56
+ env = Environment(loader=FileSystemLoader(search_paths), autoescape=autoescape)
22
57
 
23
58
  # Load and render the template.
24
59
  template = env.get_template(template_filename)
@@ -1,8 +1,10 @@
1
1
  :root {
2
+ {% block root_variables %}
2
3
  font-size: 16px;
3
- {# Adding Hack Nerd Font to all fonts for icon support, if it is installed. #}
4
- --font-sans: "Varta", sans-serif, "Hack Nerd Font";
4
+ /* Adding Hack Nerd Font to all fonts for icon support, if it is installed. */
5
+ --font-sans: "Source Sans 3 Variable", sans-serif, "Hack Nerd Font";
5
6
  --font-serif: "PT Serif", serif, "Hack Nerd Font";
7
+ --font-weight-sans-bold: 625; /* Source Sans 3 Variable better at this weight. */
6
8
  --font-mono: "Hack Nerd Font", "Menlo", "DejaVu Sans Mono", Consolas, "Lucida Console", monospace;
7
9
 
8
10
  --font-size-large: 1.2rem;
@@ -15,14 +17,17 @@
15
17
 
16
18
  --console-char-width: 88;
17
19
  --console-width: calc(var(--console-char-width) + 2rem);
20
+ {% endblock root_variables %}
18
21
  }
19
22
 
20
23
  {{ color_defs|safe }}
21
24
 
25
+ {% block selection_styles %}
22
26
  ::selection {
23
27
  background-color: var(--color-selection);
24
28
  color: inherit;
25
29
  }
30
+ {% endblock selection_styles %}
26
31
 
27
32
  {# TODO: Fix PDF issues and re-enable for prettier emoji. #}
28
33
  {# @font-face {
@@ -38,6 +43,7 @@
38
43
  U+1F900-1F9FF; /* Supplemental Symbols and Pictographs */
39
44
  } #}
40
45
 
46
+ {% block scrollbar_styles %}
41
47
  /* Scrollbar coloring. */
42
48
  /* For Webkit browsers (Chrome, Safari) */
43
49
  ::-webkit-scrollbar {
@@ -59,20 +65,40 @@
59
65
  scrollbar-width: thin;
60
66
  scrollbar-color: var(--color-scrollbar) var(--color-bg);
61
67
  }
68
+ {% endblock scrollbar_styles %}
62
69
 
70
+ {% block body_styles %}
63
71
  body {
64
72
  font-family: var(--font-serif);
65
73
  color: var(--color-text);
66
- line-height: 1.3;
74
+ line-height: 1.4;
67
75
  padding: 0; /* No padding so we can have full width elements. */
68
76
  margin: auto;
69
77
  background-color: var(--color-bg);
78
+ overflow-wrap: break-word; /* Don't let long words/URLs break layout. */
70
79
  }
80
+ {% endblock body_styles %}
71
81
 
82
+ {% block typography %}
72
83
  p {
73
84
  margin-bottom: 1rem;
74
85
  }
75
86
 
87
+ b, strong {
88
+ font-weight: var(--font-weight-sans-bold);
89
+ }
90
+
91
+ a {
92
+ color: var(--color-primary);
93
+ text-decoration: none;
94
+ }
95
+
96
+ a:hover {
97
+ color: var(--color-primary-light);
98
+ text-decoration: underline;
99
+ transition: all 0.15s ease-in-out;
100
+ }
101
+
76
102
  h1,
77
103
  h2,
78
104
  h3,
@@ -88,49 +114,19 @@ h1 {
88
114
 
89
115
  h2 {
90
116
  font-size: 1.4rem;
91
- margin-top: 2rem;
117
+ margin-top: 2.5rem;
92
118
  margin-bottom: 1rem;
93
119
  }
94
120
 
95
121
  h3 {
96
- font-size: 1.15rem;
97
- margin-top: 1.5rem;
98
- margin-bottom: 1rem;
122
+ font-size: 1.09rem;
123
+ margin-top: 1.7rem;
124
+ margin-bottom: 0.7rem;
99
125
  }
100
126
 
101
127
  h4 {
102
128
  margin-top: 1rem;
103
- margin-bottom: 1rem;
104
- }
105
-
106
- /* Long text stylings, for nicely formatting blog post length or longer texts. */
107
-
108
- .long-text h1 {
109
- font-family: var(--font-serif);
110
- font-weight: 400;
111
- }
112
-
113
- .long-text h2 {
114
- font-family: var(--font-serif);
115
- font-weight: 400;
116
- font-style: italic;
117
- }
118
-
119
- .long-text h3 {
120
- font-family: var(--font-sans);
121
- font-weight: 700;
122
- text-transform: uppercase;
123
- }
124
-
125
- .long-text h4 {
126
- font-family: var(--font-serif);
127
- font-weight: 700;
128
- }
129
-
130
- .subtitle {
131
- font-family: var(--font-serif);
132
- font-style: italic;
133
- font-size: 1rem;
129
+ margin-bottom: 0.7rem;
134
130
  }
135
131
 
136
132
  ul {
@@ -141,22 +137,31 @@ ul {
141
137
  }
142
138
 
143
139
  li {
144
- margin-top: 0.5rem;
140
+ margin-top: 0.7rem;
145
141
  margin-bottom: 0;
146
142
  position: relative;
147
143
  }
148
144
 
149
- ul>li::before {
145
+ li > p {
146
+ margin-bottom: 0;
147
+ }
148
+
149
+ ul > li::before {
150
150
  content: "▪︎";
151
151
  position: absolute;
152
- left: -1rem;
153
- font-size: 0.9rem;
152
+ left: -.85rem;
153
+ top: .25rem;
154
+ font-size: 0.62rem;
154
155
  }
155
156
 
156
157
  ol {
158
+ margin-bottom: 0.7rem;
157
159
  list-style-type: decimal;
158
160
  margin-left: 2rem;
159
- margin-bottom: 1rem;
161
+ }
162
+
163
+ ol > li {
164
+ padding-left: 0.25rem;
160
165
  }
161
166
 
162
167
  blockquote {
@@ -180,7 +185,86 @@ pre {
180
185
  letter-spacing: -0.025em;
181
186
  {# overflow-x: auto; #}
182
187
  }
188
+ {% endblock typography %}
189
+
190
+ {% block long_text_styles %}
191
+ /* Long text stylings, for nicely formatting blog post length or longer texts. */
192
+
193
+ .long-text h1 {
194
+ font-family: var(--font-serif);
195
+ font-weight: 400;
196
+ }
183
197
 
198
+ .long-text h2 {
199
+ font-family: var(--font-serif);
200
+ font-weight: 400;
201
+ font-style: italic;
202
+ }
203
+
204
+ .long-text h3 {
205
+ font-family: var(--font-sans);
206
+ font-weight: var(--font-weight-sans-bold);
207
+ text-transform: uppercase;
208
+ letter-spacing: 0.02em;
209
+ }
210
+
211
+ .long-text h4 {
212
+ font-family: var(--font-serif);
213
+ font-weight: 700;
214
+ }
215
+
216
+ .subtitle {
217
+ font-family: var(--font-serif);
218
+ font-style: italic;
219
+ font-size: 1rem;
220
+ }
221
+ {% endblock long_text_styles %}
222
+
223
+ {% block table_styles %}
224
+ table {
225
+ font-family: var(--font-sans);
226
+ font-size: var(--font-size-small);
227
+ width: auto;
228
+ margin-left: auto;
229
+ margin-right: auto;
230
+ border-collapse: collapse;
231
+ word-break: break-word; /* long words/URLs wrap instead of inflating the column */
232
+ border: 1px solid var(--color-border-hint);
233
+ }
234
+
235
+ th {
236
+ text-transform: uppercase;
237
+ letter-spacing: 0.02em;
238
+ border-bottom: 1px solid var(--color-border-hint);
239
+ }
240
+
241
+ th, td {
242
+ padding: 0.3rem 0.6rem;
243
+ max-width: 40rem;
244
+ min-width: 6rem;
245
+ }
246
+
247
+ th {
248
+ background-color: var(--color-bg-alt-solid);
249
+ }
250
+
251
+ tbody tr:nth-child(even) {
252
+ background-color: var(--color-bg-alt-solid);
253
+ }
254
+
255
+ /* Container for wide tables to allow tables to break out of parent width. */
256
+ .table-container {
257
+ {# max-width: calc(100vw - 6rem); #}
258
+ position: relative;
259
+ left: 50%;
260
+ transform: translateX(-50%);
261
+ box-sizing: border-box;
262
+ margin-bottom: 1rem;
263
+ background-color: var(--color-bg-solid);
264
+ }
265
+ {% endblock table_styles %}
266
+
267
+ {% block nav_styles %}
184
268
  nav {
185
269
  display: flex;
186
270
  flex-wrap: wrap;
@@ -190,3 +274,45 @@ nav {
190
274
  gap: 1rem;
191
275
  /* Add some space between the buttons */
192
276
  }
277
+ {% endblock nav_styles %}
278
+
279
+
280
+
281
+ {% block footnote_styles %}
282
+ /* Footnotes. */
283
+ sup {
284
+ font-size: 80%;
285
+ }
286
+
287
+ .footnote-ref a, .footnote {
288
+ text-decoration: none;
289
+ padding: 0 0.15rem;
290
+ border-radius: 4px;
291
+ transition: all 0.15s ease-in-out;
292
+ }
293
+
294
+ .footnote-ref a:hover, .footnote:hover {
295
+ background-color: var(--color-hover-bg);
296
+ color: var(--color-primary-light);
297
+ text-decoration: none;
298
+ }
299
+ {% endblock footnote_styles %}
300
+
301
+ {% block responsive_styles %}
302
+ /* Bleed wide on larger screens. */
303
+ /* TODO: Don't make so wide if table itself isn't large? */
304
+ @media (min-width: 768px) {
305
+ table {
306
+ width: calc(100vw - 6rem);
307
+ }
308
+ .table-container {
309
+ width: calc(100vw - 6rem);
310
+ }
311
+ }
312
+
313
+ @media (max-width: 768px) {
314
+ table {
315
+ font-size: var(--font-size-smaller);
316
+ }
317
+ }
318
+ {% endblock responsive_styles %}
@@ -2,10 +2,14 @@
2
2
  <html lang="en">
3
3
 
4
4
  <head>
5
+ {% block meta %}
5
6
  <meta charset="UTF-8" />
6
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>{{ title }}</title>
8
+ {% endblock meta %}
8
9
 
10
+ {% block title %}<title>{{ title }}</title>{% endblock title %}
11
+
12
+ {% block head_basic %}
9
13
  <link rel="preconnect" href="https://fonts.googleapis.com" />
10
14
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
11
15
  <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
@@ -20,12 +24,19 @@
20
24
  <link rel="preload" as="font" type="font/woff2" crossorigin
21
25
  href="https://cdn.jsdelivr.net/fontsource/fonts/pt-serif@latest/latin-700-italic.woff2" />
22
26
  <link rel="preload" as="font" type="font/woff2" crossorigin
23
- href="https://cdn.jsdelivr.net/fontsource/fonts/varta:vf@latest/latin-wght-normal.woff2" />
27
+ href="https://cdn.jsdelivr.net/fontsource/fonts/source-sans-3:vf@latest/latin-wght-normal.woff2" />
28
+ <link rel="preload" as="font" type="font/woff2" crossorigin
29
+ href="https://cdn.jsdelivr.net/fontsource/fonts/source-sans-3:vf@latest/latin-wght-italic.woff2" />
24
30
 
25
31
  <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet" />
26
32
  <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js" defer></script>
33
+ {% endblock head_basic %}
34
+
35
+ {% block head_extra %}{% endblock head_extra %}
27
36
 
28
37
  <style>
38
+ {% block font_faces %}
39
+ /* https://fontsource.org/fonts/pt-serif/cdn */
29
40
  /* pt-serif-latin-400-normal */
30
41
  @font-face {
31
42
  font-family: 'PT Serif';
@@ -62,63 +73,117 @@
62
73
  src: url(https://cdn.jsdelivr.net/fontsource/fonts/pt-serif@latest/latin-700-italic.woff2) format('woff2');
63
74
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
64
75
  }
76
+ /* https://fontsource.org/fonts/source-sans-3/cdn */
77
+ /* source-sans-3-latin-wght-normal */
65
78
  @font-face {
66
- font-family: 'Varta';
79
+ font-family: 'Source Sans 3 Variable';
67
80
  font-style: normal;
68
81
  font-display: block;
69
- font-weight: 300 700;
70
- src: url(https://cdn.jsdelivr.net/fontsource/fonts/varta:vf@latest/latin-wght-normal.woff2) format('woff2-variations');
71
- unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
82
+ font-weight: 200 900;
83
+ src: url(https://cdn.jsdelivr.net/fontsource/fonts/source-sans-3:vf@latest/latin-wght-normal.woff2) format('woff2-variations');
84
+ unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
72
85
  }
86
+ /* source-sans-3-latin-wght-normal */
87
+ @font-face {
88
+ font-family: 'Source Sans 3 Variable';
89
+ font-style: italic;
90
+ font-display: block;
91
+ font-weight: 200 900;
92
+ src: url(https://cdn.jsdelivr.net/fontsource/fonts/source-sans-3:vf@latest/latin-wght-italic.woff2) format('woff2-variations');
93
+ unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
94
+ }
95
+ {# Other decent sans serif options: Work Sans Variable, Nunito Sans Variable #}
96
+ {% endblock font_faces %}
73
97
 
98
+ {% block base_styles %}
74
99
  {% include "base_styles.css.jinja" %}
100
+ {% endblock base_styles %}
101
+
102
+ {% block content_styles %}
75
103
  {% include "content_styles.css.jinja" %}
76
-
77
- {% if extra_css %}
78
- {{ extra_css|safe }}
79
- {% endif %}
104
+ {% endblock content_styles %}
105
+
106
+ {% block custom_styles %}{% endblock custom_styles %}
80
107
  </style>
81
-
82
108
  </head>
83
109
 
84
110
  <body>
111
+ {% block body_header %}{% endblock body_header %}
112
+
113
+ {% block main_content %}
85
114
  {{ content|safe }}
86
- </body>
115
+ {% endblock main_content %}
116
+
117
+ {% block body_footer %}{% endblock body_footer %}
118
+
119
+ {% block scripts %}
120
+ <script>
121
+ document.addEventListener('DOMContentLoaded', () => {
122
+ // Send messages to the parent window, in case we are in a viewport where that matters
123
+ // (e.g. an iframe tooltip).
124
+ // Request a resize of the parent viewport. This iframe size message format isn't
125
+ // standardized by ResizeObserver, but is common. It is supported by Kerm.
126
+ const content = document.body;
127
+ console.log("Suggesting resize to parent:", content.offsetWidth, content.offsetHeight);
87
128
 
88
- <script>
89
- // Some messages are sent to the parent window, in case we are in a viewport like a
90
- // tooltip that supports it.
91
-
92
- // Request a resize of the parent viewport (e.g. tooltip).
93
- document.addEventListener('DOMContentLoaded', () => {
94
- const content = document.body;
95
- console.log("Suggesting resize to parent:", content.offsetWidth, content.offsetHeight);
96
- // This iframe size message format isn't standardized by ResizeObserver, but is common.
97
- // It is supported by Kerm.
98
- window.parent.postMessage({
99
- type: 'resize',
100
- width: Math.max(content.offsetWidth, 600),
101
- height: Math.max(content.offsetHeight, 100)
102
- }, '*');
103
- });
104
-
105
- // Double-click to expand (e.g. expand tooltip to popover).
106
- document.addEventListener('dblclick', () => {
107
- console.log("Sending expand message to parent");
108
- window.parent.postMessage({
109
- type: 'expand'
110
- }, '*');
111
- });
112
-
113
- // Escape to close tooltip or popover.
114
- document.addEventListener('keydown', (event) => {
115
- if (event.key === 'Escape') {
116
- console.log("Sending close message to parent");
117
129
  window.parent.postMessage({
118
- type: 'close'
130
+ type: 'resize',
131
+ width: Math.max(content.offsetWidth, 600),
132
+ height: Math.max(content.offsetHeight, 100)
119
133
  }, '*');
120
- }
121
- });
122
- </script>
134
+
135
+ // Wrap tables within the main content area for horizontal scrolling.
136
+ const containers = [];
137
+ document.querySelectorAll('.long-text').forEach(el => {
138
+ const pane = el.querySelector('.tab-pane');
139
+ containers.push(pane || el);
140
+ });
141
+ containers.forEach(container => {
142
+ // Find all tables within the container.
143
+ const tables = Array.from(container.querySelectorAll('table'));
144
+ tables.forEach(table => {
145
+ // Skip tables already in table-container divs
146
+ if (table.parentNode.classList.contains('table-container')) {
147
+ return;
148
+ }
149
+ try {
150
+ // Create the wrapper.
151
+ const wrapper = document.createElement('div');
152
+ wrapper.className = 'table-container';
153
+ // Get the parent and insert the wrapper where the table is, then move the table into the wrapper.
154
+ const parent = table.parentNode;
155
+ parent.insertBefore(wrapper, table);
156
+ wrapper.appendChild(table);
157
+ } catch (e) {
158
+ console.error("Error wrapping table:", e);
159
+ }
160
+ });
161
+ });
162
+ });
163
+
164
+ // Double-click to expand (e.g. expand tooltip to popover).
165
+ document.addEventListener('dblclick', () => {
166
+ console.log("Sending expand message to parent");
167
+ window.parent.postMessage({
168
+ type: 'expand'
169
+ }, '*');
170
+ });
171
+
172
+ // Escape to close tooltip or popover.
173
+ document.addEventListener('keydown', (event) => {
174
+ if (event.key === 'Escape') {
175
+ console.log("Sending close message to parent");
176
+ window.parent.postMessage({
177
+ type: 'close'
178
+ }, '*');
179
+ }
180
+ });
181
+
182
+ {% block scripts_extra %}{% endblock scripts_extra %}
183
+ </script>
184
+ {% endblock scripts %}
185
+
186
+ {% block analytics %}{% endblock analytics %}
187
+ </body>
123
188
 
124
189
  </html>
@@ -15,7 +15,7 @@
15
15
  padding: 0 0.4rem;
16
16
  }
17
17
 
18
- /* More novel bracket ideas: [❲⟦⟪⟬〔〘〚〖 ]❳⟧⟫⟭ 〕〙〛〗 */
18
+ {# More novel bracket ideas: [❲⟦⟪⟬〔〘〚〖 ]❳⟧⟫⟭ 〕〙〛〗 #}
19
19
  .citation::before {
20
20
  content: "[";
21
21
  }
@@ -57,6 +57,7 @@
57
57
  font-weight: 500;
58
58
  font-size: 1.2rem;
59
59
  text-transform: uppercase;
60
+ letter-spacing: 0.02em;
60
61
  margin-bottom: 0.5rem;
61
62
  }
62
63
 
@@ -81,6 +82,7 @@
81
82
  font-weight: 500;
82
83
  font-size: 1.2rem;
83
84
  text-transform: uppercase;
85
+ letter-spacing: 0.02em;
84
86
  margin-bottom: 0.5rem;
85
87
 
86
88
  /* Hack to center the header above the columns */
@@ -107,7 +109,7 @@
107
109
  font-size: var(--font-size-small);
108
110
  font-weight: 600;
109
111
  text-transform: uppercase;
110
- letter-spacing: 0.05em;
112
+ letter-spacing: 0.02em;
111
113
  line-height: 1.2;
112
114
  padding: 0 0.5rem;
113
115
  border-bottom-width: 2px;