kward 0.69.1 → 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.
- checksums.yaml +4 -4
- data/.github/workflows/pages.yml +1 -1
- data/CHANGELOG.md +68 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +90 -2
- data/README.md +30 -6
- data/Rakefile +96 -0
- data/doc/agent-tools.md +43 -0
- data/doc/api.md +92 -0
- data/doc/authentication.md +39 -25
- data/doc/configuration.md +2 -16
- data/doc/context-tools.md +70 -0
- data/doc/getting-started.md +3 -1
- data/doc/plugins.md +2 -2
- data/doc/releasing.md +14 -5
- data/doc/rpc.md +3 -11
- data/doc/session-management.md +220 -0
- data/doc/usage.md +13 -7
- data/doc/workspace-tools.md +105 -0
- data/lib/kward/cli/commands.rb +8 -0
- data/lib/kward/cli/openrouter_commands.rb +55 -0
- data/lib/kward/cli/prompt_interface.rb +85 -7
- data/lib/kward/cli/rendering.rb +11 -6
- data/lib/kward/cli/sessions.rb +454 -15
- data/lib/kward/cli/settings.rb +0 -30
- data/lib/kward/cli/slash_commands.rb +38 -11
- data/lib/kward/cli.rb +14 -0
- data/lib/kward/compactor.rb +4 -1
- data/lib/kward/config_files.rb +4 -6
- data/lib/kward/conversation.rb +49 -5
- data/lib/kward/model/client.rb +37 -50
- data/lib/kward/model/context_usage.rb +13 -6
- data/lib/kward/model/model_info.rb +92 -9
- data/lib/kward/model/payloads.rb +2 -0
- data/lib/kward/openrouter_model_cache.rb +120 -0
- data/lib/kward/plugin_registry.rb +47 -1
- data/lib/kward/prompt_interface/banner.rb +16 -51
- data/lib/kward/prompt_interface/composer_controller.rb +60 -87
- data/lib/kward/prompt_interface/composer_renderer.rb +7 -1
- data/lib/kward/prompt_interface/key_handler.rb +31 -10
- data/lib/kward/prompt_interface/layout.rb +2 -2
- data/lib/kward/prompt_interface/overlay_renderer.rb +24 -0
- data/lib/kward/prompt_interface/prompt_renderer.rb +23 -2
- data/lib/kward/prompt_interface/question_prompt.rb +34 -42
- data/lib/kward/prompt_interface/runtime_state.rb +6 -1
- data/lib/kward/prompt_interface/screen.rb +10 -4
- data/lib/kward/prompt_interface/selection_prompt.rb +518 -61
- data/lib/kward/prompt_interface/slash_overlay.rb +4 -4
- data/lib/kward/prompt_interface/transcript_buffer.rb +7 -16
- data/lib/kward/prompt_interface/transcript_renderer.rb +3 -3
- data/lib/kward/prompt_interface.rb +31 -32
- data/lib/kward/prompts/commands.rb +6 -3
- data/lib/kward/prompts.rb +2 -2
- data/lib/kward/rpc/server.rb +3 -8
- data/lib/kward/rpc/session_manager.rb +19 -8
- data/lib/kward/session_diff.rb +106 -9
- data/lib/kward/session_store.rb +23 -4
- data/lib/kward/session_tree_renderer.rb +2 -1
- data/lib/kward/telemetry/logger.rb +5 -3
- data/lib/kward/tool_output_compactor.rb +127 -0
- data/lib/kward/tools/base.rb +8 -2
- data/lib/kward/tools/registry.rb +37 -6
- data/lib/kward/tools/retrieve_tool_output.rb +71 -0
- data/lib/kward/tools/search/web.rb +2 -2
- data/lib/kward/tools/summarize_file_structure.rb +29 -0
- data/lib/kward/tools/tool_call.rb +2 -0
- data/lib/kward/version.rb +1 -1
- data/lib/kward/workspace.rb +58 -2
- data/templates/default/fulldoc/html/css/kward.css +570 -78
- data/templates/default/fulldoc/html/full_list.erb +107 -0
- data/templates/default/fulldoc/html/js/kward.js +259 -97
- data/templates/default/fulldoc/html/setup.rb +8 -0
- data/templates/default/kward_navigation.rb +91 -0
- data/templates/default/layout/html/layout.erb +59 -13
- data/templates/default/layout/html/setup.rb +34 -39
- metadata +13 -3
- data/lib/kward/resources/avatar_kward_logo.rb +0 -50
- data/lib/kward/resources/pixel_logo.rb +0 -232
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
2
|
+
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<%= erb(:headers) %>
|
|
5
5
|
</head>
|
|
6
6
|
<body class="kward-docs <%= home_page? ? 'kward-home-body' : 'kward-content-body' %>">
|
|
7
|
-
<
|
|
7
|
+
<a href="#main" class="kward-skip-link">Skip to content</a>
|
|
8
8
|
|
|
9
9
|
<header class="kward-topbar">
|
|
10
10
|
<a class="kward-brand" href="<%= url_for('index.html') %>">
|
|
@@ -13,14 +13,17 @@
|
|
|
13
13
|
<strong>Kward</strong>
|
|
14
14
|
</span>
|
|
15
15
|
</a>
|
|
16
|
-
<button class="kward-nav-toggle" type="button" aria-expanded="false" aria-controls="kward-primary-nav" onclick="document.body.classList.toggle('kward-nav-open'); this.setAttribute('aria-expanded', document.body.classList.contains('kward-nav-open'))">
|
|
17
|
-
|
|
16
|
+
<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'))">
|
|
17
|
+
<span class="kward-nav-toggle-bar"></span>
|
|
18
|
+
<span class="kward-nav-toggle-bar"></span>
|
|
19
|
+
<span class="kward-nav-toggle-bar"></span>
|
|
20
|
+
<span class="kward-sr-only">Menu</span>
|
|
18
21
|
</button>
|
|
19
22
|
<nav id="kward-primary-nav" class="kward-topnav" aria-label="Primary navigation">
|
|
20
23
|
<a href="<%= url_for('index.html') %>" class="<%= 'active' if home_page? %>">Home</a>
|
|
21
24
|
<div class="kward-nav-menu <%= 'active' if guide_page? %>">
|
|
22
|
-
<a class="kward-nav-menu-link" href="<%= url_for(
|
|
23
|
-
<button class="kward-nav-menu-button" type="button" aria-label="
|
|
25
|
+
<a class="kward-nav-menu-link" href="<%= url_for(guide_overview) %>">Guides</a>
|
|
26
|
+
<button class="kward-nav-menu-button" type="button" aria-label="Expand guides" aria-expanded="false">⌄</button>
|
|
24
27
|
<div class="kward-nav-dropdown">
|
|
25
28
|
<% guide_groups.each do |title, items| %>
|
|
26
29
|
<section>
|
|
@@ -32,13 +35,44 @@
|
|
|
32
35
|
<% end %>
|
|
33
36
|
</div>
|
|
34
37
|
</div>
|
|
35
|
-
<
|
|
38
|
+
<div class="kward-nav-menu <%= 'active' if extension_page? %>">
|
|
39
|
+
<a class="kward-nav-menu-link" href="<%= url_for(extension_overview) %>">Advanced</a>
|
|
40
|
+
<button class="kward-nav-menu-button" type="button" aria-label="Expand advanced guides" aria-expanded="false">⌄</button>
|
|
41
|
+
<div class="kward-nav-dropdown kward-advanced-nav-dropdown">
|
|
42
|
+
<% extension_groups.each do |title, items| %>
|
|
43
|
+
<section>
|
|
44
|
+
<h2><%= h title %></h2>
|
|
45
|
+
<% items.each do |label, link| %>
|
|
46
|
+
<a href="<%= url_for(link) %>"><%= h label %></a>
|
|
47
|
+
<% end %>
|
|
48
|
+
</section>
|
|
49
|
+
<% end %>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="kward-nav-menu <%= 'active' if api_page? %>">
|
|
53
|
+
<a class="kward-nav-menu-link" href="<%= url_for(api_overview) %>">API Docs</a>
|
|
54
|
+
<button class="kward-nav-menu-button" type="button" aria-label="Expand API docs" aria-expanded="false">⌄</button>
|
|
55
|
+
<div class="kward-nav-dropdown kward-api-nav-dropdown">
|
|
56
|
+
<% api_groups.each do |title, items| %>
|
|
57
|
+
<section>
|
|
58
|
+
<h2><%= h title %></h2>
|
|
59
|
+
<% items.each do |label, link| %>
|
|
60
|
+
<a href="<%= url_for(link) %>"><%= h label %></a>
|
|
61
|
+
<% end %>
|
|
62
|
+
</section>
|
|
63
|
+
<% end %>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
36
66
|
</nav>
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
67
|
+
<% if api_page? %>
|
|
68
|
+
<a class="kward-api-index-link" href="<%= url_for('class_list.html') %>">Search API index</a>
|
|
69
|
+
<% else %>
|
|
70
|
+
<form class="kward-guide-search" role="search">
|
|
71
|
+
<label class="kward-sr-only" for="kward-guide-search-input">Search user guides</label>
|
|
72
|
+
<input id="kward-guide-search-input" type="search" placeholder="Search guides" autocomplete="off" aria-label="Search user guides" aria-controls="kward-guide-search-results">
|
|
73
|
+
<div id="kward-guide-search-results" class="kward-guide-search-results" role="listbox" aria-label="Search results"></div>
|
|
74
|
+
</form>
|
|
75
|
+
<% end %>
|
|
42
76
|
<script type="application/json" id="kward-guide-search-index"><%= guide_search_index_json %></script>
|
|
43
77
|
</header>
|
|
44
78
|
|
|
@@ -47,7 +81,17 @@
|
|
|
47
81
|
<section class="kward-hero">
|
|
48
82
|
<div class="kward-hero-copy">
|
|
49
83
|
<p class="kward-eyebrow">⌘ Ruby CLI Coding Agent</p>
|
|
50
|
-
<h1>
|
|
84
|
+
<h1>
|
|
85
|
+
<span class="kward-swosh" aria-label="Your terminal.">
|
|
86
|
+
<span class="kward-swosh-text kward-swosh-text-a" aria-hidden="true">Your terminal.</span>
|
|
87
|
+
<span class="kward-swosh-text kward-swosh-text-b" aria-hidden="true">Your RPC frontend.</span>
|
|
88
|
+
</span>
|
|
89
|
+
<br>
|
|
90
|
+
<span class="kward-swosh" aria-label="Your agent.">
|
|
91
|
+
<span class="kward-swosh-text kward-swosh-text-c" aria-hidden="true">Your agent.</span>
|
|
92
|
+
<span class="kward-swosh-text kward-swosh-text-d" aria-hidden="true">Your LLM engine.</span>
|
|
93
|
+
</span>
|
|
94
|
+
</h1>
|
|
51
95
|
<p class="kward-lede">Kward is an extendable Ruby CLI coding agent that helps you understand your project, edit files, run commands, search the web, and automate workflows—right from your terminal.</p>
|
|
52
96
|
<div class="kward-actions">
|
|
53
97
|
<a class="kward-primary-button" href="<%= url_for('file.README.html') %>">Get Started ›</a>
|
|
@@ -137,5 +181,7 @@ kward --working-directory ~/code/project "Explain this project"</code></pre>
|
|
|
137
181
|
</main>
|
|
138
182
|
</div>
|
|
139
183
|
<% end %>
|
|
184
|
+
|
|
185
|
+
<script type="text/javascript" charset="utf-8" src="<%= url_for('js/kward.js') %>"></script>
|
|
140
186
|
</body>
|
|
141
187
|
</html>
|
|
@@ -1,44 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
+
require_relative "../../kward_navigation"
|
|
4
5
|
|
|
5
6
|
module KwardDocsNavigation
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
["Troubleshooting", "file.troubleshooting.html"]
|
|
15
|
-
]
|
|
16
|
-
],
|
|
17
|
-
[
|
|
18
|
-
"Feature guides",
|
|
19
|
-
[
|
|
20
|
-
["Memory", "file.memory.html"],
|
|
21
|
-
["Personas", "file.personas.html"],
|
|
22
|
-
["Extensibility", "file.extensibility.html"],
|
|
23
|
-
["Plugins", "file.plugins.html"],
|
|
24
|
-
["Web search", "file.web-search.html"],
|
|
25
|
-
["Code search", "file.code-search.html"]
|
|
26
|
-
]
|
|
27
|
-
],
|
|
28
|
-
[
|
|
29
|
-
"Advanced/reference",
|
|
30
|
-
[
|
|
31
|
-
["RPC protocol", "file.rpc.html"],
|
|
32
|
-
["Releasing", "file.releasing.html"]
|
|
33
|
-
]
|
|
34
|
-
]
|
|
35
|
-
].freeze
|
|
7
|
+
include KwardDocsNavigationData
|
|
8
|
+
API_FILE_LINKS = {
|
|
9
|
+
"doc/api.md" => API_OVERVIEW
|
|
10
|
+
}.freeze
|
|
11
|
+
API_LINKS = ([API_OVERVIEW] + API_GROUPS.flat_map { |_title, items| items.map(&:last) }).freeze
|
|
12
|
+
|
|
13
|
+
EXTENSION_OVERVIEW = "file.extensibility.html"
|
|
14
|
+
EXTENSION_LINKS = ([EXTENSION_OVERVIEW] + EXTENSION_GROUPS.flat_map { |_title, items| items.map(&:last) }).uniq.freeze
|
|
36
15
|
|
|
37
16
|
GUIDE_OVERVIEW = "file.README.html"
|
|
38
17
|
GUIDE_LINKS = ([GUIDE_OVERVIEW] + GUIDE_GROUPS.flat_map { |_title, items| items.map(&:last) }).freeze
|
|
39
18
|
GUIDE_SEARCH_FILES = [
|
|
40
19
|
["Overview", "README.md", GUIDE_OVERVIEW],
|
|
41
|
-
*GUIDE_GROUPS.flat_map do |_title, items|
|
|
20
|
+
*(GUIDE_GROUPS + EXTENSION_GROUPS).flat_map do |_title, items|
|
|
42
21
|
items.map do |label, link|
|
|
43
22
|
source = "doc/#{link.delete_prefix("file.").delete_suffix(".html")}.md"
|
|
44
23
|
[label, source, link]
|
|
@@ -51,24 +30,28 @@ module KwardDocsNavigation
|
|
|
51
30
|
"doc/configuration.md" => "file.configuration.html",
|
|
52
31
|
"doc/authentication.md" => "file.authentication.html",
|
|
53
32
|
"doc/troubleshooting.md" => "file.troubleshooting.html",
|
|
33
|
+
"doc/session-management.md" => "file.session-management.html",
|
|
54
34
|
"doc/memory.md" => "file.memory.html",
|
|
55
35
|
"doc/personas.md" => "file.personas.html",
|
|
56
36
|
"doc/extensibility.md" => "file.extensibility.html",
|
|
57
37
|
"doc/plugins.md" => "file.plugins.html",
|
|
38
|
+
"doc/agent-tools.md" => "file.agent-tools.html",
|
|
39
|
+
"doc/workspace-tools.md" => "file.workspace-tools.html",
|
|
58
40
|
"doc/web-search.md" => "file.web-search.html",
|
|
59
41
|
"doc/code-search.md" => "file.code-search.html",
|
|
42
|
+
"doc/context-tools.md" => "file.context-tools.html",
|
|
60
43
|
"doc/rpc.md" => "file.rpc.html",
|
|
61
44
|
"doc/releasing.md" => "file.releasing.html"
|
|
62
45
|
}.freeze
|
|
63
46
|
|
|
64
|
-
def guide_groups
|
|
65
|
-
GUIDE_GROUPS
|
|
66
|
-
end
|
|
67
|
-
|
|
68
47
|
def guide_overview
|
|
69
48
|
GUIDE_OVERVIEW
|
|
70
49
|
end
|
|
71
50
|
|
|
51
|
+
def extension_overview
|
|
52
|
+
EXTENSION_OVERVIEW
|
|
53
|
+
end
|
|
54
|
+
|
|
72
55
|
def guide_search_index_json
|
|
73
56
|
JSON.generate(guide_search_index).gsub("</", "<\\/")
|
|
74
57
|
end
|
|
@@ -99,7 +82,15 @@ module KwardDocsNavigation
|
|
|
99
82
|
end
|
|
100
83
|
|
|
101
84
|
def guide_file?
|
|
102
|
-
defined?(@file) &&
|
|
85
|
+
defined?(@file) && GUIDE_LINKS.include?(GUIDE_FILE_LINKS[@file&.filename.to_s])
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def extension_file?
|
|
89
|
+
defined?(@file) && EXTENSION_LINKS.include?(GUIDE_FILE_LINKS[@file&.filename.to_s])
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def api_file?
|
|
93
|
+
defined?(@file) && API_FILE_LINKS.key?(@file&.filename.to_s)
|
|
103
94
|
end
|
|
104
95
|
|
|
105
96
|
def home_page?
|
|
@@ -110,8 +101,12 @@ module KwardDocsNavigation
|
|
|
110
101
|
(readme_file? && !options.index) || guide_file? || GUIDE_LINKS.include?(current_docs_path)
|
|
111
102
|
end
|
|
112
103
|
|
|
104
|
+
def extension_page?
|
|
105
|
+
extension_file? || EXTENSION_LINKS.include?(current_docs_path)
|
|
106
|
+
end
|
|
107
|
+
|
|
113
108
|
def api_page?
|
|
114
|
-
!home_page? && !guide_page?
|
|
109
|
+
api_file? || API_LINKS.include?(current_docs_path) || (!home_page? && !guide_page? && !extension_page?)
|
|
115
110
|
end
|
|
116
111
|
|
|
117
112
|
def diskfile
|
|
@@ -121,7 +116,7 @@ module KwardDocsNavigation
|
|
|
121
116
|
end
|
|
122
117
|
|
|
123
118
|
def rewrite_guide_links(html)
|
|
124
|
-
GUIDE_FILE_LINKS.each do |source, target|
|
|
119
|
+
GUIDE_FILE_LINKS.merge(API_FILE_LINKS).each do |source, target|
|
|
125
120
|
html = html.gsub(%(href="#{source}"), %(href="#{target}"))
|
|
126
121
|
end
|
|
127
122
|
html
|
|
@@ -131,7 +126,7 @@ end
|
|
|
131
126
|
include KwardDocsNavigation
|
|
132
127
|
|
|
133
128
|
def javascripts
|
|
134
|
-
super
|
|
129
|
+
super
|
|
135
130
|
end
|
|
136
131
|
|
|
137
132
|
def stylesheets
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kward
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.71.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kai Wood
|
|
@@ -124,9 +124,12 @@ files:
|
|
|
124
124
|
- LICENSE
|
|
125
125
|
- README.md
|
|
126
126
|
- Rakefile
|
|
127
|
+
- doc/agent-tools.md
|
|
128
|
+
- doc/api.md
|
|
127
129
|
- doc/authentication.md
|
|
128
130
|
- doc/code-search.md
|
|
129
131
|
- doc/configuration.md
|
|
132
|
+
- doc/context-tools.md
|
|
130
133
|
- doc/extensibility.md
|
|
131
134
|
- doc/getting-started.md
|
|
132
135
|
- doc/memory.md
|
|
@@ -134,9 +137,11 @@ files:
|
|
|
134
137
|
- doc/plugins.md
|
|
135
138
|
- doc/releasing.md
|
|
136
139
|
- doc/rpc.md
|
|
140
|
+
- doc/session-management.md
|
|
137
141
|
- doc/troubleshooting.md
|
|
138
142
|
- doc/usage.md
|
|
139
143
|
- doc/web-search.md
|
|
144
|
+
- doc/workspace-tools.md
|
|
140
145
|
- exe/kward
|
|
141
146
|
- kward.gemspec
|
|
142
147
|
- lib/kward.rb
|
|
@@ -155,6 +160,7 @@ files:
|
|
|
155
160
|
- lib/kward/cli/doctor.rb
|
|
156
161
|
- lib/kward/cli/interactive_turn.rb
|
|
157
162
|
- lib/kward/cli/memory_commands.rb
|
|
163
|
+
- lib/kward/cli/openrouter_commands.rb
|
|
158
164
|
- lib/kward/cli/plugins.rb
|
|
159
165
|
- lib/kward/cli/prompt_interface.rb
|
|
160
166
|
- lib/kward/cli/rendering.rb
|
|
@@ -186,6 +192,7 @@ files:
|
|
|
186
192
|
- lib/kward/model/payloads.rb
|
|
187
193
|
- lib/kward/model/retry_message.rb
|
|
188
194
|
- lib/kward/model/stream_parser.rb
|
|
195
|
+
- lib/kward/openrouter_model_cache.rb
|
|
189
196
|
- lib/kward/pan/index.html.erb
|
|
190
197
|
- lib/kward/pan/server.rb
|
|
191
198
|
- lib/kward/plugin_registry.rb
|
|
@@ -211,8 +218,6 @@ files:
|
|
|
211
218
|
- lib/kward/prompts/commands.rb
|
|
212
219
|
- lib/kward/prompts/templates.rb
|
|
213
220
|
- lib/kward/question_contract.rb
|
|
214
|
-
- lib/kward/resources/avatar_kward_logo.rb
|
|
215
|
-
- lib/kward/resources/pixel_logo.rb
|
|
216
221
|
- lib/kward/rpc/attachment_normalizer.rb
|
|
217
222
|
- lib/kward/rpc/auth_manager.rb
|
|
218
223
|
- lib/kward/rpc/config_manager.rb
|
|
@@ -238,6 +243,7 @@ files:
|
|
|
238
243
|
- lib/kward/steering.rb
|
|
239
244
|
- lib/kward/telemetry/logger.rb
|
|
240
245
|
- lib/kward/telemetry/stats.rb
|
|
246
|
+
- lib/kward/tool_output_compactor.rb
|
|
241
247
|
- lib/kward/tools/ask_user_question.rb
|
|
242
248
|
- lib/kward/tools/base.rb
|
|
243
249
|
- lib/kward/tools/code_search.rb
|
|
@@ -248,10 +254,12 @@ files:
|
|
|
248
254
|
- lib/kward/tools/read_file.rb
|
|
249
255
|
- lib/kward/tools/read_skill.rb
|
|
250
256
|
- lib/kward/tools/registry.rb
|
|
257
|
+
- lib/kward/tools/retrieve_tool_output.rb
|
|
251
258
|
- lib/kward/tools/run_shell_command.rb
|
|
252
259
|
- lib/kward/tools/search/code.rb
|
|
253
260
|
- lib/kward/tools/search/web.rb
|
|
254
261
|
- lib/kward/tools/search/web_fetch.rb
|
|
262
|
+
- lib/kward/tools/summarize_file_structure.rb
|
|
255
263
|
- lib/kward/tools/tool_call.rb
|
|
256
264
|
- lib/kward/tools/web_search.rb
|
|
257
265
|
- lib/kward/tools/write_file.rb
|
|
@@ -260,9 +268,11 @@ files:
|
|
|
260
268
|
- lib/kward/workspace.rb
|
|
261
269
|
- lib/main.rb
|
|
262
270
|
- templates/default/fulldoc/html/css/kward.css
|
|
271
|
+
- templates/default/fulldoc/html/full_list.erb
|
|
263
272
|
- templates/default/fulldoc/html/images/kward_logo.png
|
|
264
273
|
- templates/default/fulldoc/html/js/kward.js
|
|
265
274
|
- templates/default/fulldoc/html/setup.rb
|
|
275
|
+
- templates/default/kward_navigation.rb
|
|
266
276
|
- templates/default/layout/html/breadcrumb.erb
|
|
267
277
|
- templates/default/layout/html/layout.erb
|
|
268
278
|
- templates/default/layout/html/setup.rb
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# Generated from avatar_kward_32x32.png as RGB terminal cells.
|
|
2
|
-
# The interactive banner uses this data instead of decoding a PNG at runtime.
|
|
3
|
-
module Kward
|
|
4
|
-
# Static avatar logo data used by generated resources.
|
|
5
|
-
module Resources
|
|
6
|
-
# Static avatar logo data used by generated resources.
|
|
7
|
-
module AvatarKwardLogo
|
|
8
|
-
PIXELS = [
|
|
9
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
|
|
10
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [36, 40, 24], [39, 42, 25], [39, 42, 25], [33, 36, 21], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
|
|
11
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [33, 36, 23], [78, 86, 52], [85, 93, 56], [84, 93, 55], [65, 72, 43], [31, 33, 19], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
|
|
12
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [49, 54, 33], [78, 86, 52], [85, 93, 56], [85, 93, 56], [85, 93, 56], [84, 93, 55], [75, 83, 49], [35, 38, 23], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
|
|
13
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [61, 68, 40], [80, 88, 53], [85, 93, 56], [75, 83, 49], [72, 80, 48], [70, 78, 46], [78, 86, 52], [84, 93, 55], [76, 85, 51], [50, 55, 34], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
|
|
14
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [42, 46, 27], [80, 88, 53], [84, 93, 55], [70, 78, 46], [23, 25, 15], [37, 38, 29], [34, 35, 26], [33, 37, 22], [74, 81, 48], [83, 92, 55], [78, 86, 52], [24, 26, 15], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
|
|
15
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, nil, [34, 37, 22], [83, 92, 55], [85, 93, 56], [69, 77, 46], [18, 19, 12], [53, 53, 45], [135, 132, 115], [128, 125, 111], [40, 40, 36], [21, 23, 14], [72, 80, 48], [80, 88, 53], [39, 44, 26], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
|
|
16
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, [18, 20, 13], [58, 63, 38], [85, 93, 56], [55, 61, 36], [23, 25, 15], [43, 42, 35], [146, 142, 129], [176, 173, 156], [179, 175, 159], [146, 142, 129], [67, 65, 58], [27, 30, 19], [62, 68, 41], [80, 88, 53], [54, 60, 35], nil, nil, nil, nil, nil, nil, nil, nil, nil],
|
|
17
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, [23, 26, 16], [74, 81, 48], [69, 77, 46], [21, 23, 14], [31, 31, 25], [108, 105, 91], [153, 150, 134], [134, 132, 118], [173, 169, 154], [183, 179, 163], [134, 132, 118], [27, 26, 21], [25, 28, 16], [70, 78, 46], [72, 80, 48], [21, 23, 14], nil, nil, nil, nil, nil, nil, nil, nil],
|
|
18
|
-
[nil, nil, nil, nil, nil, nil, nil, [8, 10, 6], [66, 73, 45], [58, 63, 37], [14, 15, 9], [3, 3, 2], [87, 85, 74], [160, 157, 139], [160, 157, 139], [134, 132, 118], [80, 78, 69], [173, 169, 154], [175, 171, 155], [76, 74, 65], [5, 5, 4], [14, 15, 9], [63, 70, 42], [66, 73, 45], [2, 3, 2], nil, nil, nil, nil, nil, nil, nil],
|
|
19
|
-
[nil, nil, nil, nil, nil, nil, nil, [18, 19, 12], [59, 65, 40], [23, 23, 21], [0, 0, 0], [100, 98, 86], [160, 157, 139], [163, 160, 143], [138, 136, 121], [70, 68, 58], [173, 169, 154], [175, 171, 155], [183, 179, 163], [179, 175, 159], [92, 90, 82], [1, 1, 1], [27, 30, 19], [62, 68, 41], [16, 17, 11], nil, nil, nil, nil, nil, nil, nil],
|
|
20
|
-
[nil, nil, nil, nil, nil, nil, [15, 15, 11], [62, 68, 42], [72, 70, 61], [87, 85, 74], [44, 43, 36], [77, 76, 65], [108, 105, 91], [149, 146, 130], [123, 119, 105], [158, 155, 140], [168, 164, 149], [144, 140, 126], [160, 156, 142], [118, 115, 104], [77, 76, 65], [53, 53, 45], [103, 102, 85], [76, 74, 65], [66, 72, 43], [20, 20, 13], nil, nil, nil, nil, nil, nil],
|
|
21
|
-
[nil, nil, nil, nil, nil, nil, [35, 38, 23], [78, 86, 52], [135, 132, 115], [113, 109, 94], [129, 128, 109], [23, 25, 15], [33, 55, 8], [96, 108, 72], [87, 85, 74], [168, 164, 149], [158, 155, 140], [97, 94, 83], [96, 108, 72], [24, 40, 6], [30, 33, 20], [142, 141, 122], [104, 102, 90], [153, 150, 134], [75, 83, 49], [37, 41, 25], nil, nil, nil, nil, nil, nil],
|
|
22
|
-
[nil, nil, nil, nil, nil, [2, 2, 1], [63, 70, 42], [87, 95, 60], [134, 132, 118], [77, 76, 65], [104, 102, 90], [5, 5, 4], [72, 133, 6], [100, 184, 10], [39, 44, 26], [160, 156, 142], [146, 142, 129], [47, 70, 19], [103, 188, 10], [56, 105, 5], [10, 10, 8], [128, 125, 111], [72, 70, 61], [149, 146, 130], [84, 93, 55], [54, 60, 36], [2, 3, 2], nil, nil, nil, nil, nil],
|
|
23
|
-
[nil, nil, nil, nil, nil, [46, 51, 30], [67, 74, 44], [44, 48, 30], [114, 111, 97], [80, 78, 69], [103, 102, 85], [34, 35, 26], [33, 55, 8], [59, 97, 13], [82, 84, 65], [166, 162, 147], [156, 152, 138], [76, 85, 51], [59, 97, 13], [29, 46, 8], [43, 42, 35], [134, 132, 118], [92, 90, 82], [128, 125, 111], [39, 44, 26], [65, 72, 43], [43, 48, 29], nil, nil, nil, nil, nil],
|
|
24
|
-
[nil, nil, nil, nil, [17, 19, 12], [63, 70, 42], [31, 35, 21], [5, 5, 4], [39, 39, 33], [70, 68, 58], [53, 53, 45], [103, 102, 85], [58, 56, 48], [77, 76, 65], [113, 109, 94], [171, 167, 151], [163, 160, 143], [108, 105, 91], [72, 70, 61], [64, 62, 54], [108, 105, 91], [59, 58, 50], [84, 82, 71], [39, 39, 33], [3, 3, 2], [39, 44, 26], [58, 64, 39], nil, nil, nil, nil, nil],
|
|
25
|
-
[nil, nil, nil, [2, 3, 2], [43, 47, 28], [68, 75, 45], [15, 17, 11], [0, 0, 0], [0, 0, 0], [8, 8, 7], [12, 12, 11], [59, 58, 50], [92, 89, 75], [27, 26, 21], [100, 96, 82], [171, 167, 151], [163, 160, 143], [70, 68, 58], [44, 43, 36], [114, 111, 97], [58, 56, 48], [15, 14, 13], [5, 5, 4], [0, 0, 0], [0, 0, 0], [23, 25, 15], [69, 77, 46], [30, 33, 20], nil, nil, nil, nil],
|
|
26
|
-
[nil, nil, nil, [37, 41, 26], [63, 70, 42], [27, 30, 19], [1, 1, 1], [0, 0, 0], [8, 8, 7], [32, 33, 30], [20, 20, 18], [6, 6, 5], [64, 62, 54], [31, 31, 25], [100, 96, 82], [171, 167, 151], [163, 160, 143], [70, 68, 58], [51, 49, 43], [70, 68, 58], [6, 6, 5], [23, 23, 21], [35, 35, 33], [6, 6, 5], [0, 0, 0], [1, 1, 1], [33, 37, 22], [58, 63, 37], [32, 35, 21], nil, nil, nil],
|
|
27
|
-
[nil, nil, nil, [52, 58, 35], [80, 88, 53], [52, 58, 34], [10, 10, 8], [5, 5, 4], [32, 33, 30], [23, 23, 21], [0, 0, 0], [23, 23, 21], [59, 58, 50], [70, 68, 58], [72, 70, 61], [166, 162, 147], [153, 150, 134], [67, 65, 58], [76, 74, 65], [62, 61, 57], [19, 19, 17], [0, 0, 0], [31, 31, 25], [32, 33, 30], [3, 3, 2], [15, 17, 11], [55, 61, 36], [80, 88, 53], [39, 42, 25], nil, nil, nil],
|
|
28
|
-
[nil, nil, nil, [23, 24, 16], [54, 60, 36], [83, 92, 55], [27, 30, 19], [44, 44, 40], [19, 19, 17], [1, 1, 1], [1, 1, 1], [49, 48, 45], [44, 44, 40], [103, 102, 85], [44, 44, 40], [158, 155, 140], [144, 140, 126], [64, 62, 54], [92, 89, 75], [49, 48, 45], [40, 40, 36], [0, 0, 0], [3, 3, 2], [19, 19, 17], [49, 48, 45], [36, 40, 24], [83, 92, 55], [44, 48, 30], [23, 25, 16], nil, nil, nil],
|
|
29
|
-
[nil, nil, nil, [50, 55, 34], [49, 54, 33], [62, 68, 41], [67, 74, 44], [30, 33, 20], [12, 12, 11], [30, 30, 28], [20, 20, 18], [30, 30, 28], [12, 12, 11], [92, 89, 75], [51, 49, 43], [160, 156, 142], [144, 140, 126], [64, 62, 54], [77, 76, 65], [12, 12, 11], [35, 35, 33], [17, 17, 16], [35, 35, 33], [10, 10, 8], [34, 35, 26], [68, 75, 45], [58, 63, 37], [51, 56, 34], [43, 48, 29], nil, nil, nil],
|
|
30
|
-
[nil, nil, [30, 33, 21], [68, 75, 45], [70, 78, 46], [39, 44, 26], [59, 65, 40], [67, 74, 44], [21, 23, 14], [8, 8, 7], [19, 19, 17], [32, 33, 30], [10, 10, 8], [15, 14, 13], [92, 89, 75], [168, 164, 149], [160, 157, 139], [70, 68, 58], [12, 12, 11], [10, 10, 8], [35, 35, 33], [17, 17, 16], [10, 10, 8], [21, 23, 14], [72, 80, 48], [55, 61, 36], [47, 52, 31], [72, 80, 48], [66, 72, 43], [25, 26, 18], nil, nil],
|
|
31
|
-
[nil, nil, [22, 24, 14], [27, 30, 18], [55, 61, 36], [84, 93, 55], [44, 48, 30], [54, 60, 36], [74, 81, 48], [25, 28, 16], [1, 1, 1], [49, 48, 45], [32, 33, 30], [1, 1, 1], [92, 89, 75], [123, 119, 105], [118, 115, 104], [67, 63, 54], [1, 1, 1], [44, 44, 40], [40, 40, 36], [1, 1, 1], [30, 33, 20], [76, 85, 51], [52, 58, 34], [51, 56, 34], [81, 90, 54], [47, 52, 31], [26, 28, 17], nil, nil, nil],
|
|
32
|
-
[nil, [30, 33, 21], [66, 72, 43], [80, 88, 53], [80, 88, 53], [45, 50, 29], [33, 37, 22], [51, 56, 34], [42, 47, 27], [68, 75, 45], [38, 42, 25], [19, 19, 17], [35, 35, 33], [12, 12, 11], [37, 35, 30], [70, 68, 58], [67, 63, 54], [27, 26, 21], [17, 17, 16], [35, 35, 33], [17, 17, 16], [39, 44, 26], [70, 78, 46], [42, 47, 27], [54, 60, 36], [31, 35, 21], [31, 35, 21], [63, 70, 42], [78, 86, 52], [65, 71, 42], nil, nil],
|
|
33
|
-
[nil, [14, 15, 9], [36, 39, 23], [63, 70, 42], [81, 90, 54], [62, 68, 41], [51, 56, 34], [39, 44, 26], [14, 15, 9], [49, 54, 33], [69, 77, 46], [25, 28, 16], [20, 20, 18], [8, 8, 7], [6, 6, 5], [43, 42, 35], [37, 35, 30], [3, 3, 2], [12, 12, 11], [19, 19, 17], [30, 33, 20], [70, 78, 46], [44, 48, 30], [14, 15, 9], [44, 48, 30], [49, 54, 33], [52, 58, 34], [72, 80, 48], [54, 60, 36], [36, 39, 23], [12, 14, 11], nil],
|
|
34
|
-
[nil, [5, 5, 5], nil, [53, 58, 35], [84, 93, 55], [84, 93, 55], [81, 90, 54], [69, 77, 46], [55, 61, 36], [15, 17, 11], [39, 44, 26], [68, 75, 45], [59, 65, 40], [18, 19, 12], [10, 10, 8], [62, 61, 57], [56, 55, 53], [6, 6, 5], [23, 25, 15], [63, 70, 42], [69, 77, 46], [36, 40, 24], [15, 17, 11], [58, 63, 37], [67, 74, 44], [80, 88, 53], [84, 93, 55], [83, 92, 55], [39, 43, 26], nil, [2, 2, 1], nil],
|
|
35
|
-
[nil, nil, nil, [25, 29, 18], [45, 50, 29], [27, 30, 19], [22, 25, 15], [75, 83, 50], [84, 93, 55], [54, 60, 36], [15, 17, 11], [18, 19, 12], [42, 47, 27], [74, 81, 48], [12, 12, 11], [23, 23, 21], [19, 19, 17], [30, 33, 20], [74, 81, 48], [38, 42, 25], [15, 17, 11], [15, 17, 11], [62, 68, 41], [83, 92, 55], [21, 23, 14], [33, 37, 22], [49, 54, 33], [21, 23, 14], nil, nil, nil, [4, 4, 4]],
|
|
36
|
-
[nil, nil, nil, [64, 71, 42], [43, 47, 28], [7, 7, 4], nil, [51, 56, 35], [83, 92, 55], [70, 78, 46], [47, 52, 31], [38, 42, 25], [31, 35, 21], [54, 60, 36], [17, 17, 16], [51, 51, 48], [46, 46, 43], [27, 30, 19], [52, 58, 34], [30, 33, 20], [39, 44, 26], [49, 54, 33], [76, 85, 51], [85, 93, 56], [43, 48, 29], [14, 14, 10], [8, 8, 7], [49, 54, 33], [62, 67, 41], nil, nil, nil],
|
|
37
|
-
[nil, nil, nil, [34, 38, 24], [37, 39, 24], nil, nil, [34, 37, 22], [75, 83, 50], [35, 39, 24], [1, 1, 1], [43, 48, 29], [66, 72, 43], [20, 21, 12], [3, 1, 1], [2, 2, 2], [2, 2, 2], [3, 3, 3], [27, 30, 18], [66, 72, 43], [35, 38, 23], [2, 1, 1], [42, 46, 28], [76, 85, 51], [5, 5, 4], nil, [2, 3, 2], [36, 40, 24], [22, 25, 15], nil, nil, nil],
|
|
38
|
-
[nil, nil, nil, nil, nil, nil, [14, 15, 10], [67, 74, 44], [29, 32, 19], nil, nil, nil, [25, 29, 18], nil, nil, nil, nil, nil, nil, [23, 25, 16], nil, nil, nil, [29, 32, 19], [52, 58, 35], nil, nil, nil, nil, nil, nil, nil],
|
|
39
|
-
[nil, nil, nil, nil, nil, nil, nil, [22, 24, 15], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [23, 26, 16], nil, nil, nil, nil, nil, nil, nil],
|
|
40
|
-
[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
PIXELS.each do |row|
|
|
44
|
-
row.each { |color| color.freeze if color }
|
|
45
|
-
row.freeze
|
|
46
|
-
end
|
|
47
|
-
PIXELS.freeze
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
require "zlib"
|
|
2
|
-
|
|
3
|
-
# Namespace for the Kward CLI agent runtime.
|
|
4
|
-
module Kward
|
|
5
|
-
# Pixel-art logo data and rendering helpers.
|
|
6
|
-
module PixelLogo
|
|
7
|
-
PNG_SIGNATURE = "\x89PNG\r\n\x1a\n".b.freeze
|
|
8
|
-
TRANSPARENT_ALPHA = 128
|
|
9
|
-
|
|
10
|
-
module_function
|
|
11
|
-
|
|
12
|
-
def rows_from_png(path, width:, height:)
|
|
13
|
-
rows_from_pixels(indexed_png_pixels(path), width: width, height: height)
|
|
14
|
-
rescue StandardError
|
|
15
|
-
[]
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def rows_from_pixels(pixels, width:, height:)
|
|
19
|
-
scaled = scale_pixels(pixels, width: width, height: height)
|
|
20
|
-
render_rows(scaled)
|
|
21
|
-
rescue StandardError
|
|
22
|
-
[]
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def half_block_rows_from_png(path, width:, pixel_height:)
|
|
26
|
-
half_block_rows_from_pixels(indexed_png_pixels(path), width: width, pixel_height: pixel_height)
|
|
27
|
-
rescue StandardError
|
|
28
|
-
[]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def half_block_rows_from_pixels(pixels, width:, pixel_height:)
|
|
32
|
-
scaled = scale_pixels(pixels, width: width, height: pixel_height)
|
|
33
|
-
render_half_block_rows(scaled)
|
|
34
|
-
rescue StandardError
|
|
35
|
-
[]
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def indexed_png_pixels(path)
|
|
39
|
-
data = File.binread(path)
|
|
40
|
-
raise "Invalid PNG" unless data.start_with?(PNG_SIGNATURE)
|
|
41
|
-
|
|
42
|
-
png_width = nil
|
|
43
|
-
png_height = nil
|
|
44
|
-
bit_depth = nil
|
|
45
|
-
color_type = nil
|
|
46
|
-
interlace = nil
|
|
47
|
-
palette = nil
|
|
48
|
-
transparency = []
|
|
49
|
-
idat = +"".b
|
|
50
|
-
|
|
51
|
-
each_chunk(data) do |type, chunk|
|
|
52
|
-
case type
|
|
53
|
-
when "IHDR"
|
|
54
|
-
png_width, png_height, bit_depth, color_type, _compression, _filter, interlace = chunk.unpack("NNCCCCC")
|
|
55
|
-
when "PLTE"
|
|
56
|
-
palette = chunk.bytes.each_slice(3).map { |red, green, blue| [red, green, blue, 255] }
|
|
57
|
-
when "tRNS"
|
|
58
|
-
transparency = chunk.bytes
|
|
59
|
-
when "IDAT"
|
|
60
|
-
idat << chunk
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
raise "Unsupported PNG" unless png_width && png_height && palette
|
|
65
|
-
raise "Unsupported PNG" unless bit_depth == 8 && color_type == 3 && interlace == 0
|
|
66
|
-
|
|
67
|
-
transparency.each_with_index do |alpha, index|
|
|
68
|
-
palette[index] = palette[index][0, 3] + [alpha] if palette[index]
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
unfilter_indexed_rows(Zlib::Inflate.inflate(idat), png_width, png_height).map do |row|
|
|
72
|
-
row.map { |index| palette[index] || [0, 0, 0, 0] }
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def each_chunk(data)
|
|
77
|
-
offset = PNG_SIGNATURE.bytesize
|
|
78
|
-
while offset < data.bytesize
|
|
79
|
-
length = data[offset, 4].unpack1("N")
|
|
80
|
-
type = data[offset + 4, 4]
|
|
81
|
-
chunk = data[offset + 8, length]
|
|
82
|
-
yield type, chunk
|
|
83
|
-
offset += length + 12
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def unfilter_indexed_rows(raw, width, height)
|
|
88
|
-
rows = []
|
|
89
|
-
previous = Array.new(width, 0)
|
|
90
|
-
offset = 0
|
|
91
|
-
|
|
92
|
-
height.times do
|
|
93
|
-
filter = raw.getbyte(offset)
|
|
94
|
-
offset += 1
|
|
95
|
-
scanline = raw.byteslice(offset, width).bytes
|
|
96
|
-
offset += width
|
|
97
|
-
row = unfilter_scanline(scanline, previous, filter)
|
|
98
|
-
rows << row
|
|
99
|
-
previous = row
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
rows
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def unfilter_scanline(scanline, previous, filter)
|
|
106
|
-
row = []
|
|
107
|
-
scanline.each_with_index do |byte, index|
|
|
108
|
-
left = index.zero? ? 0 : row[index - 1]
|
|
109
|
-
up = previous[index] || 0
|
|
110
|
-
up_left = index.zero? ? 0 : previous[index - 1]
|
|
111
|
-
row << case filter
|
|
112
|
-
when 0 then byte
|
|
113
|
-
when 1 then (byte + left) & 0xff
|
|
114
|
-
when 2 then (byte + up) & 0xff
|
|
115
|
-
when 3 then (byte + ((left + up) / 2)) & 0xff
|
|
116
|
-
when 4 then (byte + paeth(left, up, up_left)) & 0xff
|
|
117
|
-
else byte
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
row
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def paeth(left, up, up_left)
|
|
124
|
-
estimate = left + up - up_left
|
|
125
|
-
left_distance = (estimate - left).abs
|
|
126
|
-
up_distance = (estimate - up).abs
|
|
127
|
-
up_left_distance = (estimate - up_left).abs
|
|
128
|
-
return left if left_distance <= up_distance && left_distance <= up_left_distance
|
|
129
|
-
return up if up_distance <= up_left_distance
|
|
130
|
-
|
|
131
|
-
up_left
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def scale_pixels(pixels, width:, height:)
|
|
135
|
-
source_height = pixels.length
|
|
136
|
-
source_width = pixels.first.length
|
|
137
|
-
|
|
138
|
-
height.times.map do |target_y|
|
|
139
|
-
source_y_start = target_y * source_height / height
|
|
140
|
-
source_y_end = [(target_y + 1) * source_height / height, source_y_start + 1].max
|
|
141
|
-
width.times.map do |target_x|
|
|
142
|
-
source_x_start = target_x * source_width / width
|
|
143
|
-
source_x_end = [(target_x + 1) * source_width / width, source_x_start + 1].max
|
|
144
|
-
dominant_color(pixels, source_x_start...source_x_end, source_y_start...source_y_end)
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def dominant_color(pixels, x_range, y_range)
|
|
150
|
-
counts = Hash.new(0)
|
|
151
|
-
y_range.each do |y|
|
|
152
|
-
x_range.each do |x|
|
|
153
|
-
counts[visible_color(pixels[y][x])] += 1
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
counts.max_by { |_color, count| count }&.first
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def visible_color(color)
|
|
160
|
-
return nil unless color
|
|
161
|
-
|
|
162
|
-
red, green, blue, alpha = color
|
|
163
|
-
return nil if !alpha.nil? && alpha.to_i < TRANSPARENT_ALPHA
|
|
164
|
-
|
|
165
|
-
[red, green, blue]
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def render_rows(rows)
|
|
169
|
-
rows.map do |row|
|
|
170
|
-
current = nil
|
|
171
|
-
rendered = +""
|
|
172
|
-
row.each do |color|
|
|
173
|
-
if color != current
|
|
174
|
-
rendered << background_sgr(color)
|
|
175
|
-
current = color
|
|
176
|
-
end
|
|
177
|
-
rendered << " "
|
|
178
|
-
end
|
|
179
|
-
rendered << reset_sgr if current
|
|
180
|
-
rendered
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def render_half_block_rows(rows)
|
|
185
|
-
rows.each_slice(2).map do |top_row, bottom_row|
|
|
186
|
-
bottom_row ||= Array.new(top_row.length)
|
|
187
|
-
current_foreground = nil
|
|
188
|
-
current_background = nil
|
|
189
|
-
rendered = +""
|
|
190
|
-
top_row.each_with_index do |top, index|
|
|
191
|
-
bottom = bottom_row[index]
|
|
192
|
-
foreground, background, glyph = half_block_cell(top, bottom)
|
|
193
|
-
if background != current_background
|
|
194
|
-
rendered << background_sgr(background)
|
|
195
|
-
current_background = background
|
|
196
|
-
end
|
|
197
|
-
if foreground != current_foreground
|
|
198
|
-
rendered << foreground_sgr(foreground)
|
|
199
|
-
current_foreground = foreground
|
|
200
|
-
end
|
|
201
|
-
rendered << glyph
|
|
202
|
-
end
|
|
203
|
-
rendered << reset_sgr if current_foreground || current_background
|
|
204
|
-
rendered
|
|
205
|
-
end
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def half_block_cell(top, bottom)
|
|
209
|
-
if top && bottom
|
|
210
|
-
[top, bottom, "▀"]
|
|
211
|
-
elsif top
|
|
212
|
-
[top, nil, "▀"]
|
|
213
|
-
elsif bottom
|
|
214
|
-
[bottom, nil, "▄"]
|
|
215
|
-
else
|
|
216
|
-
[nil, nil, " "]
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def foreground_sgr(color)
|
|
221
|
-
color ? "\e[38;2;#{color.join(";")}m" : "\e[39m"
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def background_sgr(color)
|
|
225
|
-
color ? "\e[48;2;#{color.join(";")}m" : "\e[49m"
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
def reset_sgr
|
|
229
|
-
"\e[0m"
|
|
230
|
-
end
|
|
231
|
-
end
|
|
232
|
-
end
|