kward 0.70.0 → 0.71.0

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/pages.yml +1 -1
  3. data/CHANGELOG.md +48 -2
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +90 -2
  6. data/README.md +30 -6
  7. data/Rakefile +96 -0
  8. data/doc/agent-tools.md +43 -0
  9. data/doc/api.md +92 -0
  10. data/doc/authentication.md +39 -25
  11. data/doc/configuration.md +1 -15
  12. data/doc/context-tools.md +70 -0
  13. data/doc/plugins.md +2 -2
  14. data/doc/releasing.md +14 -5
  15. data/doc/rpc.md +3 -11
  16. data/doc/session-management.md +220 -0
  17. data/doc/usage.md +7 -8
  18. data/doc/workspace-tools.md +105 -0
  19. data/lib/kward/cli/commands.rb +8 -0
  20. data/lib/kward/cli/openrouter_commands.rb +55 -0
  21. data/lib/kward/cli/prompt_interface.rb +80 -6
  22. data/lib/kward/cli/rendering.rb +11 -6
  23. data/lib/kward/cli/sessions.rb +260 -11
  24. data/lib/kward/cli/settings.rb +0 -30
  25. data/lib/kward/cli/slash_commands.rb +24 -6
  26. data/lib/kward/cli.rb +13 -0
  27. data/lib/kward/compactor.rb +4 -1
  28. data/lib/kward/config_files.rb +4 -6
  29. data/lib/kward/conversation.rb +49 -20
  30. data/lib/kward/model/client.rb +37 -50
  31. data/lib/kward/model/context_usage.rb +13 -6
  32. data/lib/kward/model/model_info.rb +92 -16
  33. data/lib/kward/model/payloads.rb +2 -0
  34. data/lib/kward/openrouter_model_cache.rb +120 -0
  35. data/lib/kward/plugin_registry.rb +47 -1
  36. data/lib/kward/prompt_interface/banner.rb +16 -51
  37. data/lib/kward/prompt_interface/composer_controller.rb +60 -87
  38. data/lib/kward/prompt_interface/composer_renderer.rb +7 -1
  39. data/lib/kward/prompt_interface/key_handler.rb +31 -10
  40. data/lib/kward/prompt_interface/layout.rb +2 -2
  41. data/lib/kward/prompt_interface/prompt_renderer.rb +32 -13
  42. data/lib/kward/prompt_interface/question_prompt.rb +34 -42
  43. data/lib/kward/prompt_interface/runtime_state.rb +6 -1
  44. data/lib/kward/prompt_interface/screen.rb +1 -0
  45. data/lib/kward/prompt_interface/selection_prompt.rb +513 -54
  46. data/lib/kward/prompt_interface/transcript_buffer.rb +7 -16
  47. data/lib/kward/prompt_interface/transcript_renderer.rb +3 -3
  48. data/lib/kward/prompt_interface.rb +22 -28
  49. data/lib/kward/prompts/commands.rb +2 -1
  50. data/lib/kward/prompts.rb +2 -2
  51. data/lib/kward/rpc/server.rb +3 -8
  52. data/lib/kward/rpc/session_manager.rb +17 -6
  53. data/lib/kward/session_store.rb +23 -4
  54. data/lib/kward/telemetry/logger.rb +5 -3
  55. data/lib/kward/tool_output_compactor.rb +127 -0
  56. data/lib/kward/tools/base.rb +8 -2
  57. data/lib/kward/tools/registry.rb +37 -6
  58. data/lib/kward/tools/retrieve_tool_output.rb +71 -0
  59. data/lib/kward/tools/search/web.rb +2 -2
  60. data/lib/kward/tools/summarize_file_structure.rb +29 -0
  61. data/lib/kward/tools/tool_call.rb +2 -0
  62. data/lib/kward/version.rb +1 -1
  63. data/lib/kward/workspace.rb +58 -2
  64. data/templates/default/fulldoc/html/css/kward.css +256 -7
  65. data/templates/default/fulldoc/html/full_list.erb +107 -0
  66. data/templates/default/fulldoc/html/js/kward.js +161 -2
  67. data/templates/default/fulldoc/html/setup.rb +8 -0
  68. data/templates/default/kward_navigation.rb +91 -0
  69. data/templates/default/layout/html/layout.erb +39 -8
  70. data/templates/default/layout/html/setup.rb +33 -38
  71. metadata +13 -3
  72. data/lib/kward/resources/avatar_kward_logo.rb +0 -50
  73. data/lib/kward/resources/pixel_logo.rb +0 -232
@@ -0,0 +1,71 @@
1
+ require_relative "base"
2
+
3
+ # Namespace for the Kward CLI agent runtime.
4
+ module Kward
5
+ # Model-callable tool wrappers and their argument schemas.
6
+ module Tools
7
+ # Retrieves original tool outputs that were compacted out of model context.
8
+ class RetrieveToolOutput < Base
9
+ DEFAULT_LIMIT = 120
10
+
11
+ # Builds the retrieval tool schema.
12
+ def initialize
13
+ super(
14
+ "retrieve_tool_output",
15
+ "Retrieve original output from a compacted previous tool result.",
16
+ properties: {
17
+ id: { type: "string", description: "Tool output id, for example toolout_abc123." },
18
+ query: { type: "string", description: "Optional case-insensitive text search within the original output." },
19
+ offset: { type: "integer", description: "1-indexed line offset for returned output." },
20
+ limit: { type: "integer", description: "Maximum lines to return; default 120." }
21
+ },
22
+ required: ["id"]
23
+ )
24
+ end
25
+
26
+ # Executes retrieval from the active conversation artifact store.
27
+ def call(args, conversation, cancellation: nil)
28
+ cancellation&.raise_if_cancelled!
29
+ id = argument(args, :id, "").to_s
30
+ return "Error: id is required" if id.empty?
31
+
32
+ artifact = conversation.tool_output_artifacts[id]
33
+ return "Error: unknown tool output id: #{id}" unless artifact
34
+
35
+ content = artifact[:content].to_s
36
+ query = argument(args, :query, "").to_s
37
+ lines = query.empty? ? content.split("\n", -1) : matching_lines(content, query)
38
+ return "No matching lines for #{query.inspect} in #{id}." if lines.empty?
39
+
40
+ slice_lines(id, lines, offset: argument(args, :offset), limit: argument(args, :limit), query: query)
41
+ end
42
+
43
+ private
44
+
45
+ def matching_lines(content, query)
46
+ needle = query.downcase
47
+ content.split("\n", -1).each_with_index.filter_map do |line, index|
48
+ next unless line.downcase.include?(needle)
49
+
50
+ "#{index + 1}: #{line}"
51
+ end
52
+ end
53
+
54
+ def slice_lines(id, lines, offset:, limit:, query:)
55
+ start_index = [offset.to_i - 1, 0].max
56
+ return "Error: offset #{offset} is beyond output (#{lines.length} lines total)" if start_index >= lines.length
57
+
58
+ line_limit = limit.to_i.positive? ? limit.to_i : DEFAULT_LIMIT
59
+ selected = lines[start_index, line_limit] || []
60
+ header = "[Retrieved tool output #{id}"
61
+ header << " matching #{query.inspect}" unless query.empty?
62
+ header << ": lines #{start_index + 1}-#{start_index + selected.length} of #{lines.length}]"
63
+ output = "#{header}\n#{selected.join("\n")}".rstrip
64
+ if start_index + selected.length < lines.length
65
+ output << "\n\n[#{lines.length - start_index - selected.length} more lines. Use offset=#{start_index + selected.length + 1} to continue.]"
66
+ end
67
+ output
68
+ end
69
+ end
70
+ end
71
+ end
@@ -619,8 +619,8 @@ module Kward
619
619
  return web_config[snake] if web_config.key?(snake)
620
620
  return web_config[camel] if web_config.key?(camel)
621
621
  return config[prefixed] if config.key?(prefixed)
622
- return config[snake] if config.key?(snake)
623
- return config[camel] if config.key?(camel)
622
+ return config[snake] if key.to_s != "provider" && config.key?(snake)
623
+ return config[camel] if key.to_s != "provider" && config.key?(camel)
624
624
 
625
625
  nil
626
626
  end
@@ -0,0 +1,29 @@
1
+ require_relative "base"
2
+
3
+ # Namespace for the Kward CLI agent runtime.
4
+ module Kward
5
+ # Model-callable tool wrappers and their argument schemas.
6
+ module Tools
7
+ # Returns a compact symbol outline for a workspace source file.
8
+ class SummarizeFileStructure < Base
9
+ # Builds the tool schema and stores the execution dependency.
10
+ def initialize(workspace:)
11
+ @workspace = workspace
12
+ super(
13
+ "summarize_file_structure",
14
+ "Return a compact outline of classes, modules, methods, and functions in a workspace source file.",
15
+ properties: {
16
+ path: { type: "string", description: "Workspace-relative source file path." }
17
+ },
18
+ required: ["path"]
19
+ )
20
+ end
21
+
22
+ # Executes the structure summary tool.
23
+ def call(args, _conversation, cancellation: nil)
24
+ cancellation&.raise_if_cancelled!
25
+ @workspace.summarize_file_structure(argument(args, :path, ""))
26
+ end
27
+ end
28
+ end
29
+ end
@@ -17,6 +17,8 @@ module Kward
17
17
  "run_shell_command" => "bash",
18
18
  "list_directory" => "list_directory",
19
19
  "code_search" => "code_search",
20
+ "summarize_file_structure" => "summarize_file_structure",
21
+ "retrieve_tool_output" => "retrieve_tool_output",
20
22
  "web_search" => "web_search",
21
23
  "fetch_content" => "fetch_content",
22
24
  "fetch_raw" => "fetch_raw",
data/lib/kward/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # Namespace for the Kward CLI agent runtime.
2
2
  module Kward
3
3
  # Current gem version.
4
- VERSION = "0.70.0"
4
+ VERSION = "0.71.0"
5
5
  end
@@ -20,7 +20,7 @@ module Kward
20
20
  MAX_FILE_BYTES = 256 * 1024
21
21
  MAX_READ_OUTPUT_BYTES = 50 * 1024
22
22
  MAX_READ_OUTPUT_LINES = 2_000
23
- MAX_COMMAND_OUTPUT_BYTES = 20 * 1024
23
+ MAX_COMMAND_OUTPUT_BYTES = 128 * 1024
24
24
  MAX_EDIT_DIFF_BYTES = 8 * 1024
25
25
  DEFAULT_COMMAND_TIMEOUT_SECONDS = 30
26
26
 
@@ -64,7 +64,27 @@ module Kward
64
64
  content = File.read(resolved)
65
65
  return "Error: not a text file: #{path}" if binary_content?(content)
66
66
 
67
- read_file_slice(content, offset: offset, limit: limit)
67
+ large_file_outline_response(path, content, offset: offset, limit: limit) || read_file_slice(content, offset: offset, limit: limit)
68
+ rescue SecurityError, Errno::ENOENT => e
69
+ "Error: #{e.message}"
70
+ end
71
+
72
+ # Returns a compact outline of recognizable source-code declarations.
73
+ def summarize_file_structure(path)
74
+ resolved = workspace_path(path)
75
+ return "Error: not a file: #{path}" unless File.file?(resolved)
76
+
77
+ size = File.size(resolved)
78
+ return "Error: file too large: #{path} is #{size} bytes; limit is #{@max_file_bytes} bytes" if size > @max_file_bytes
79
+
80
+ content = File.read(resolved)
81
+ return "Error: not a text file: #{path}" if binary_content?(content)
82
+
83
+ lines = content.split("\n", -1)
84
+ outline = source_outline(lines)
85
+ return "No recognizable source structure found in #{path}." if outline.empty?
86
+
87
+ (["# File structure: #{path}", "- Lines: #{lines.length}", "- Bytes: #{content.bytesize}", "", "## Outline"] + outline).join("\n")
68
88
  rescue SecurityError, Errno::ENOENT => e
69
89
  "Error: #{e.message}"
70
90
  end
@@ -196,6 +216,42 @@ module Kward
196
216
  Pathname.new(path).relative_path_from(@root).to_s
197
217
  end
198
218
 
219
+ def large_file_outline_response(path, content, offset:, limit:)
220
+ return nil unless offset.nil? && limit.nil?
221
+ lines = content.split("\n", -1)
222
+ return nil unless lines.length > @max_read_output_lines || content.bytesize > @max_read_output_bytes
223
+
224
+ outline = source_outline(lines)
225
+ return nil if outline.empty?
226
+
227
+ preview_limit = [120, @max_read_output_lines].min
228
+ preview = lines.first(preview_limit).join("\n")
229
+ [
230
+ "File has #{lines.length} lines (#{content.bytesize} bytes). Showing an outline and the first #{preview_limit} lines to reduce model context.",
231
+ "",
232
+ "Outline:",
233
+ outline.join("\n"),
234
+ "",
235
+ "First #{preview_limit} lines:",
236
+ preview,
237
+ "",
238
+ "[Use read_file with offset=#{preview_limit + 1} and limit to continue, or request a specific section from the outline.]"
239
+ ].join("\n")
240
+ end
241
+
242
+ def source_outline(lines)
243
+ outline = []
244
+ lines.each_with_index do |line, index|
245
+ stripped = line.strip
246
+ next unless stripped.match?(/\A(class|module|def)\s+/) || stripped.match?(/\A(function|async function)\s+/) || stripped.match?(/\A(export\s+)?(class|interface|type)\s+/)
247
+
248
+ indent = line[/\A\s*/].to_s.length
249
+ outline << "line #{index + 1}: #{' ' * [indent / 2, 6].min}#{stripped}"
250
+ break if outline.length >= 80
251
+ end
252
+ outline
253
+ end
254
+
199
255
  def read_file_slice(content, offset:, limit:)
200
256
  lines = content.split("\n", -1)
201
257
  lines = [""] if lines.empty?
@@ -10,6 +10,13 @@
10
10
  --kward-accent: #9caf35;
11
11
  --kward-accent-bright: #d2df62;
12
12
  --kward-code: #030604;
13
+ --kward-syntax-comment: #7f8b67;
14
+ --kward-syntax-constant: #f0bf6a;
15
+ --kward-syntax-keyword: #d2df62;
16
+ --kward-syntax-method: #b8d852;
17
+ --kward-syntax-string: #8fd15b;
18
+ --kward-syntax-symbol: #ff7f6f;
19
+ --kward-syntax-variable: #a7c884;
13
20
  }
14
21
 
15
22
  html {
@@ -131,12 +138,31 @@ body.kward-docs::before {
131
138
  width: 1px;
132
139
  }
133
140
 
134
- .kward-guide-search {
141
+ .kward-guide-search,
142
+ .kward-api-index-link {
135
143
  justify-self: end;
144
+ }
145
+
146
+ .kward-guide-search {
136
147
  position: relative;
137
148
  width: min(280px, 24vw);
138
149
  }
139
150
 
151
+ .kward-api-index-link {
152
+ border: 1px solid rgba(156, 175, 53, 0.42);
153
+ border-radius: 999px;
154
+ color: var(--kward-ink);
155
+ padding: 10px 14px;
156
+ text-decoration: none;
157
+ white-space: nowrap;
158
+ }
159
+
160
+ .kward-api-index-link:hover,
161
+ .kward-api-index-link:focus {
162
+ background: rgba(156, 175, 53, 0.13);
163
+ color: var(--kward-accent-bright);
164
+ }
165
+
140
166
  .kward-guide-search input {
141
167
  background: rgba(3, 6, 4, 0.72);
142
168
  border: 1px solid rgba(156, 175, 53, 0.42);
@@ -328,6 +354,90 @@ body.kward-docs code {
328
354
  border-radius: 6px;
329
355
  }
330
356
 
357
+ body.kward-docs pre.code .comment {
358
+ color: var(--kward-syntax-comment);
359
+ font-style: italic;
360
+ }
361
+
362
+ body.kward-docs pre.code .const,
363
+ body.kward-docs pre.code .constant,
364
+ body.kward-docs #content .summary_desc pre.code .const > .object_link a,
365
+ body.kward-docs #content .docstring pre.code .const > .object_link a {
366
+ color: var(--kward-syntax-constant);
367
+ }
368
+
369
+ body.kward-docs pre.code .kw,
370
+ body.kward-docs pre.code .rubyid_require,
371
+ body.kward-docs pre.code .rubyid_extend,
372
+ body.kward-docs pre.code .rubyid_include {
373
+ color: var(--kward-syntax-keyword);
374
+ font-weight: 700;
375
+ }
376
+
377
+ body.kward-docs pre.code .id,
378
+ body.kward-docs #content .summary_desc pre.code .id > .object_link a,
379
+ body.kward-docs #content .docstring pre.code .id > .object_link a {
380
+ color: var(--kward-syntax-method);
381
+ }
382
+
383
+ body.kward-docs pre.code .tstring_content,
384
+ body.kward-docs pre.code .heredoc_beg,
385
+ body.kward-docs pre.code .heredoc_end,
386
+ body.kward-docs pre.code .qwords_beg,
387
+ body.kward-docs pre.code .qwords_end,
388
+ body.kward-docs pre.code .qwords_sep,
389
+ body.kward-docs pre.code .words_beg,
390
+ body.kward-docs pre.code .words_end,
391
+ body.kward-docs pre.code .words_sep,
392
+ body.kward-docs pre.code .qsymbols_beg,
393
+ body.kward-docs pre.code .qsymbols_end,
394
+ body.kward-docs pre.code .qsymbols_sep,
395
+ body.kward-docs pre.code .symbols_beg,
396
+ body.kward-docs pre.code .symbols_end,
397
+ body.kward-docs pre.code .symbols_sep,
398
+ body.kward-docs pre.code .tstring,
399
+ body.kward-docs pre.code .dstring,
400
+ body.kward-docs pre.code .regexp,
401
+ body.kward-docs .dregexp {
402
+ color: var(--kward-syntax-string);
403
+ }
404
+
405
+ body.kward-docs pre.code .label,
406
+ body.kward-docs pre.code .symbol {
407
+ color: var(--kward-syntax-symbol);
408
+ }
409
+
410
+ body.kward-docs pre.code .ivar,
411
+ body.kward-docs pre.code .gvar,
412
+ body.kward-docs pre.code .rubyid_backref,
413
+ body.kward-docs pre.code .rubyid_nth_ref {
414
+ color: var(--kward-syntax-variable);
415
+ }
416
+
417
+ body.kward-docs pre.code .op,
418
+ body.kward-docs pre.code .period,
419
+ body.kward-docs pre.code .comma,
420
+ body.kward-docs pre.code .lparen,
421
+ body.kward-docs pre.code .rparen,
422
+ body.kward-docs pre.code .lbrace,
423
+ body.kward-docs pre.code .rbrace,
424
+ body.kward-docs pre.code .lbracket,
425
+ body.kward-docs pre.code .rbracket {
426
+ color: rgba(239, 232, 208, 0.74);
427
+ }
428
+
429
+ body.kward-docs pre.code a,
430
+ body.kward-docs pre.code a:visited {
431
+ text-decoration-color: rgba(210, 223, 98, 0.45);
432
+ text-decoration-thickness: 1px;
433
+ text-underline-offset: 2px;
434
+ }
435
+
436
+ body.kward-docs pre.code a:hover {
437
+ background: rgba(156, 175, 53, 0.16);
438
+ color: var(--kward-accent-bright);
439
+ }
440
+
331
441
  body.kward-docs .copy-code-button {
332
442
  background: transparent;
333
443
  border: 1px solid rgba(156, 175, 53, 0.48);
@@ -364,7 +474,8 @@ body.kward-docs #footer {
364
474
  }
365
475
 
366
476
  .kward-topnav,
367
- .kward-guide-search {
477
+ .kward-guide-search,
478
+ .kward-api-index-link {
368
479
  display: none;
369
480
  }
370
481
 
@@ -1081,17 +1192,26 @@ body.kward-docs .kward-home #footer {
1081
1192
  border-radius: 12px;
1082
1193
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.48);
1083
1194
  display: none;
1084
- gap: 22px;
1085
- grid-template-columns: repeat(4, minmax(170px, 1fr));
1195
+ gap: 16px;
1196
+ grid-template-columns: repeat(2, minmax(160px, 220px));
1086
1197
  left: 50%;
1198
+ max-width: calc(100vw - 48px);
1087
1199
  opacity: 0;
1088
- padding: 28px 22px 22px;
1200
+ padding: 20px;
1089
1201
  pointer-events: none;
1090
1202
  position: absolute;
1091
1203
  top: 100%;
1092
1204
  transform: translate(-50%, -8px);
1093
1205
  transition: opacity 140ms ease, transform 140ms ease;
1094
- width: min(920px, calc(100vw - 48px));
1206
+ width: max-content;
1207
+ }
1208
+
1209
+ .kward-advanced-nav-dropdown {
1210
+ grid-template-columns: repeat(3, minmax(150px, 200px));
1211
+ }
1212
+
1213
+ .kward-api-nav-dropdown {
1214
+ grid-template-columns: repeat(2, minmax(170px, 210px));
1095
1215
  }
1096
1216
 
1097
1217
  .kward-nav-dropdown::before {
@@ -1257,6 +1377,134 @@ body.kward-docs .kward-home #footer {
1257
1377
  }
1258
1378
  }
1259
1379
 
1380
+ /* YARD's class/method/file list pages use a separate full_list template. */
1381
+ .kward-full-list-body .kward-topbar {
1382
+ position: relative;
1383
+ }
1384
+
1385
+ .kward-full-list-body .kward-page {
1386
+ padding-top: 40px;
1387
+ }
1388
+
1389
+ .kward-full-list-body #content {
1390
+ background: var(--kward-panel-strong);
1391
+ border: 1px solid var(--kward-border);
1392
+ border-radius: 14px;
1393
+ box-shadow: 0 26px 70px rgba(0, 0, 0, 0.28);
1394
+ padding: 38px;
1395
+ }
1396
+
1397
+ .kward-full-list-body .fixed_header {
1398
+ background: transparent;
1399
+ height: auto;
1400
+ margin: 0 0 24px;
1401
+ padding: 0;
1402
+ position: static;
1403
+ width: auto;
1404
+ }
1405
+
1406
+ .kward-full-list-body #full_list_header {
1407
+ color: var(--kward-ink);
1408
+ font-size: clamp(36px, 5vw, 62px);
1409
+ letter-spacing: -0.04em;
1410
+ line-height: 0.98;
1411
+ margin: 0 0 18px;
1412
+ padding: 0;
1413
+ }
1414
+
1415
+ .kward-full-list-body #full_list_nav {
1416
+ color: var(--kward-muted);
1417
+ font-size: 14px;
1418
+ margin: 0 0 18px;
1419
+ }
1420
+
1421
+ .kward-full-list-body #full_list_nav a,
1422
+ .kward-full-list-body #full_list_nav a:visited {
1423
+ color: var(--kward-accent-bright);
1424
+ }
1425
+
1426
+ .kward-full-list-body #full_list_nav a:hover {
1427
+ color: #f0f7a0;
1428
+ }
1429
+
1430
+ .kward-full-list-body #search {
1431
+ color: var(--kward-muted);
1432
+ font-size: 14px;
1433
+ margin: 0;
1434
+ padding: 0;
1435
+ width: auto;
1436
+ }
1437
+
1438
+ .kward-full-list-body #search input {
1439
+ background: rgba(3, 6, 4, 0.72);
1440
+ border: 1px solid rgba(156, 175, 53, 0.42);
1441
+ border-radius: 999px;
1442
+ color: var(--kward-ink);
1443
+ font: inherit;
1444
+ margin-left: 8px;
1445
+ outline: none;
1446
+ padding: 8px 12px;
1447
+ width: min(260px, 60vw);
1448
+ }
1449
+
1450
+ .kward-full-list-body #search input:focus {
1451
+ border-color: var(--kward-accent-bright);
1452
+ box-shadow: 0 0 0 3px rgba(156, 175, 53, 0.18);
1453
+ }
1454
+
1455
+ .kward-full-list-body #full_list {
1456
+ background: rgba(3, 6, 4, 0.42);
1457
+ border: 1px solid rgba(156, 175, 53, 0.22);
1458
+ border-radius: 12px;
1459
+ font-size: 15px;
1460
+ margin: 0;
1461
+ overflow: auto;
1462
+ padding: 8px;
1463
+ }
1464
+
1465
+ .kward-full-list-body #full_list li {
1466
+ color: var(--kward-muted);
1467
+ }
1468
+
1469
+ .kward-full-list-body #full_list li.odd,
1470
+ .kward-full-list-body #full_list li.even {
1471
+ background: transparent;
1472
+ }
1473
+
1474
+ .kward-full-list-body #full_list li .item {
1475
+ border-radius: 8px;
1476
+ padding-bottom: 7px;
1477
+ padding-top: 7px;
1478
+ }
1479
+
1480
+ .kward-full-list-body #full_list .item:hover {
1481
+ background: rgba(156, 175, 53, 0.13);
1482
+ }
1483
+
1484
+ .kward-full-list-body #full_list a,
1485
+ .kward-full-list-body #full_list a:visited {
1486
+ color: var(--kward-ink);
1487
+ }
1488
+
1489
+ .kward-full-list-body #full_list a:hover {
1490
+ color: var(--kward-accent-bright);
1491
+ }
1492
+
1493
+ .kward-full-list-body #full_list li.clicked > .item {
1494
+ background: rgba(156, 175, 53, 0.22);
1495
+ color: var(--kward-ink);
1496
+ }
1497
+
1498
+ .kward-full-list-body #full_list li.clicked > .item a,
1499
+ .kward-full-list-body #full_list li.clicked > .item a:visited {
1500
+ color: var(--kward-accent-bright);
1501
+ }
1502
+
1503
+ .kward-full-list-body #noresults {
1504
+ background: transparent;
1505
+ color: var(--kward-muted);
1506
+ }
1507
+
1260
1508
  /* Pages no longer use YARD's iframe/sidebar layout. */
1261
1509
  .kward-page {
1262
1510
  box-sizing: border-box;
@@ -1588,8 +1836,9 @@ body.kward-docs #filecontents table {
1588
1836
  border-collapse: collapse;
1589
1837
  color: var(--kward-ink);
1590
1838
  display: block;
1839
+ max-width: 100%;
1591
1840
  overflow-x: auto;
1592
- width: 100%;
1841
+ width: fit-content;
1593
1842
  }
1594
1843
 
1595
1844
  body.kward-docs #content th,
@@ -0,0 +1,107 @@
1
+ <!DOCTYPE html>
2
+ <html <%= "lang=\"#{html_lang}\"" unless html_lang.nil? %>>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta charset="<%= charset %>">
6
+ <% stylesheets_full_list.each do |stylesheet| %>
7
+ <link rel="stylesheet" href="<%= mtime_url(stylesheet) %>" type="text/css" media="screen">
8
+ <% end %>
9
+
10
+ <% javascripts_full_list.each do |javascript| %>
11
+ <script type="text/javascript" charset="utf-8" src="<%= mtime_url(javascript) %>"></script>
12
+ <% end %>
13
+
14
+ <title><%= @list_title %></title>
15
+ <base id="base_target" target="_parent">
16
+ </head>
17
+ <body class="kward-docs kward-content-body kward-full-list-body">
18
+ <a href="#content" class="kward-skip-link">Skip to content</a>
19
+
20
+ <header class="kward-topbar">
21
+ <a class="kward-brand" href="index.html">
22
+ <img class="kward-brand-logo" src="images/kward_logo.png" alt="" aria-hidden="true">
23
+ <span>
24
+ <strong>Kward</strong>
25
+ </span>
26
+ </a>
27
+ <button class="kward-nav-toggle" type="button" aria-expanded="false" aria-controls="kward-primary-nav" aria-label="Toggle navigation menu" onclick="document.body.classList.toggle('kward-nav-open'); this.setAttribute('aria-expanded', document.body.classList.contains('kward-nav-open'))">
28
+ <span class="kward-nav-toggle-bar"></span>
29
+ <span class="kward-nav-toggle-bar"></span>
30
+ <span class="kward-nav-toggle-bar"></span>
31
+ <span class="kward-sr-only">Menu</span>
32
+ </button>
33
+ <nav id="kward-primary-nav" class="kward-topnav" aria-label="Primary navigation">
34
+ <a href="index.html">Home</a>
35
+ <div class="kward-nav-menu">
36
+ <a class="kward-nav-menu-link" href="file.README.html">Guides</a>
37
+ <button class="kward-nav-menu-button" type="button" aria-label="Expand guides" aria-expanded="false">⌄</button>
38
+ <div class="kward-nav-dropdown">
39
+ <% guide_groups.each do |title, items| %>
40
+ <section>
41
+ <h2><%= h title %></h2>
42
+ <% items.each do |label, link| %>
43
+ <a href="<%= url_for(link) %>"><%= h label %></a>
44
+ <% end %>
45
+ </section>
46
+ <% end %>
47
+ </div>
48
+ </div>
49
+ <div class="kward-nav-menu">
50
+ <a class="kward-nav-menu-link" href="file.extensibility.html">Advanced</a>
51
+ <button class="kward-nav-menu-button" type="button" aria-label="Expand advanced guides" aria-expanded="false">⌄</button>
52
+ <div class="kward-nav-dropdown kward-advanced-nav-dropdown">
53
+ <% extension_groups.each do |title, items| %>
54
+ <section>
55
+ <h2><%= h title %></h2>
56
+ <% items.each do |label, link| %>
57
+ <a href="<%= url_for(link) %>"><%= h label %></a>
58
+ <% end %>
59
+ </section>
60
+ <% end %>
61
+ </div>
62
+ </div>
63
+ <div class="kward-nav-menu active">
64
+ <a class="kward-nav-menu-link" href="file.api.html">API Docs</a>
65
+ <button class="kward-nav-menu-button" type="button" aria-label="Expand API docs" aria-expanded="false">⌄</button>
66
+ <div class="kward-nav-dropdown kward-api-nav-dropdown">
67
+ <% api_groups.each do |title, items| %>
68
+ <section>
69
+ <h2><%= h title %></h2>
70
+ <% items.each do |label, link| %>
71
+ <a href="<%= url_for(link) %>"><%= h label %></a>
72
+ <% end %>
73
+ </section>
74
+ <% end %>
75
+ </div>
76
+ </div>
77
+ </nav>
78
+ <a class="kward-api-index-link" href="class_list.html">Search API index</a>
79
+ </header>
80
+
81
+ <main class="kward-page">
82
+ <div id="content" class="kward-content-page kward-api-page">
83
+ <div class="fixed_header">
84
+ <h1 id="full_list_header"><%= @list_title %></h1>
85
+ <div id="full_list_nav">
86
+ <% menu_lists.each do |list| %>
87
+ <span><a target="_self" href="<%= url_for_list list[:type] %>">
88
+ <%= list[:title] %>
89
+ </a></span>
90
+ <% end %>
91
+ </div>
92
+
93
+ <div id="search">
94
+ <label for="search-class">Search:</label>
95
+ <input id="search-class" type="text">
96
+ </div>
97
+ </div>
98
+
99
+ <ul id="full_list" class="<%= @list_class || @list_type %>">
100
+ <%= erb "full_list_#{@list_type}" %>
101
+ </ul>
102
+ </div>
103
+ </main>
104
+
105
+ <script type="text/javascript" charset="utf-8" src="js/kward.js"></script>
106
+ </body>
107
+ </html>