trifle-docs 0.6.0 → 0.7.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/Gemfile.lock +1 -1
- data/lib/trifle/docs/app.rb +60 -26
- data/lib/trifle/docs/engine.rb +101 -58
- data/lib/trifle/docs/helper/llms.rb +13 -8
- data/lib/trifle/docs/helper/sitemap.rb +79 -0
- data/lib/trifle/docs/version.rb +1 -1
- data/lib/trifle/docs.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f51e6c6b892eaca5b790d652ccb970146c9e98b624cf3353c84af3ffb95ed4c
|
|
4
|
+
data.tar.gz: f5058c9fa39c81a635db554fabffaab5535f57a76a078138319df0e72732cd49
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48ce9ec16b882be46f90825f0e12bb13c783fa16e5b22f5eae27551418a5e58d112a321496fdf46810474c68de5f8933661931a696b6378bc836ffd7a8e74192
|
|
7
|
+
data.tar.gz: 9721a213e1f2cef167e0adf4ed18025b0740ba6594e26f6f0f326e4e6412631f5957447f503cd2ca45c4ce4e2df5efb497a15a8aaf2943a29b246a2c373869a8
|
data/Gemfile.lock
CHANGED
data/lib/trifle/docs/app.rb
CHANGED
|
@@ -27,9 +27,7 @@ module Trifle
|
|
|
27
27
|
|
|
28
28
|
get '/llms.txt' do
|
|
29
29
|
meta = Trifle::Docs.meta(url: 'llms.txt')
|
|
30
|
-
if meta && meta['type'] == 'file'
|
|
31
|
-
return send_file(meta['path'])
|
|
32
|
-
end
|
|
30
|
+
return send_file(meta['path']) if meta && meta['type'] == 'file'
|
|
33
31
|
|
|
34
32
|
content = Trifle::Docs::Helper::Llms.homepage_markdown
|
|
35
33
|
halt(404, 'Not Found') if content.nil?
|
|
@@ -40,9 +38,7 @@ module Trifle
|
|
|
40
38
|
|
|
41
39
|
get '/llms-full.txt' do
|
|
42
40
|
meta = Trifle::Docs.meta(url: 'llms-full.txt')
|
|
43
|
-
if meta && meta['type'] == 'file'
|
|
44
|
-
return send_file(meta['path'])
|
|
45
|
-
end
|
|
41
|
+
return send_file(meta['path']) if meta && meta['type'] == 'file'
|
|
46
42
|
|
|
47
43
|
content = Trifle::Docs::Helper::Llms.full_markdown
|
|
48
44
|
halt(404, 'Not Found') if content.nil? || content.strip.empty?
|
|
@@ -51,32 +47,70 @@ module Trifle
|
|
|
51
47
|
content
|
|
52
48
|
end
|
|
53
49
|
|
|
50
|
+
get '/sitemap.xml' do
|
|
51
|
+
meta = Trifle::Docs.meta(url: 'sitemap.xml')
|
|
52
|
+
return send_file(meta['path']) if meta && meta['type'] == 'file'
|
|
53
|
+
|
|
54
|
+
render_generated_sitemap
|
|
55
|
+
end
|
|
56
|
+
|
|
54
57
|
get '/*' do
|
|
58
|
+
handle_request(params, request)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def markdown_requested?(request, params)
|
|
62
|
+
return true if params['format'].to_s.downcase == 'md'
|
|
63
|
+
|
|
64
|
+
accept = request.env['HTTP_ACCEPT'].to_s
|
|
65
|
+
accept.include?('text/markdown')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def render_markdown?(meta, request, params)
|
|
69
|
+
return false if meta['type'] == 'file'
|
|
70
|
+
|
|
71
|
+
markdown_requested?(request, params) ||
|
|
72
|
+
Trifle::Docs::Helper::AiDetection.ai_scraper?(request.user_agent)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def handle_request(params, request)
|
|
55
76
|
url = params['splat'].first.chomp('/')
|
|
56
77
|
meta = Trifle::Docs.meta(url: url)
|
|
57
78
|
halt(404, 'Not Found') if meta.nil?
|
|
58
79
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
return render_markdown(meta, url) if render_markdown?(meta, request, params)
|
|
81
|
+
return send_file(meta['path']) if meta['type'] == 'file'
|
|
82
|
+
|
|
83
|
+
render_html(meta, url)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def render_markdown(meta, url)
|
|
87
|
+
content_type 'text/markdown'
|
|
88
|
+
Trifle::Docs::Helper::MarkdownLayout.render(
|
|
89
|
+
meta: meta,
|
|
90
|
+
raw_content: Trifle::Docs.raw_content(url: url),
|
|
91
|
+
sitemap: Trifle::Docs.sitemap
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def render_html(meta, url)
|
|
96
|
+
erb (meta['template'] || 'page').to_sym, {}, {
|
|
97
|
+
sitemap: Trifle::Docs.sitemap,
|
|
98
|
+
collection: Trifle::Docs.collection(url: url),
|
|
99
|
+
content: Trifle::Docs.content(url: url),
|
|
100
|
+
meta: meta,
|
|
101
|
+
url: url
|
|
102
|
+
}
|
|
79
103
|
end
|
|
104
|
+
|
|
105
|
+
def render_generated_sitemap
|
|
106
|
+
content = Trifle::Docs::Helper::Sitemap.xml
|
|
107
|
+
halt(404, 'Not Found') if content.nil? || content.strip.empty?
|
|
108
|
+
|
|
109
|
+
content_type 'application/xml'
|
|
110
|
+
content
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private :handle_request
|
|
80
114
|
end
|
|
81
115
|
end
|
|
82
116
|
end
|
data/lib/trifle/docs/engine.rb
CHANGED
|
@@ -19,79 +19,35 @@ if Object.const_defined?('Rails')
|
|
|
19
19
|
root to: 'page#show'
|
|
20
20
|
get 'llms.txt', to: 'page#llms'
|
|
21
21
|
get 'llms-full.txt', to: 'page#llms_full'
|
|
22
|
+
get 'sitemap.xml', to: 'page#sitemap'
|
|
22
23
|
get 'search', to: 'page#search'
|
|
23
24
|
get '*url', to: 'page#show'
|
|
24
25
|
end
|
|
25
26
|
end
|
|
26
27
|
end
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def configuration
|
|
32
|
-
params[:configuration] || Trifle::Docs.default
|
|
33
|
-
end
|
|
29
|
+
module PageControllerHelpers
|
|
30
|
+
private
|
|
34
31
|
|
|
35
|
-
def
|
|
36
|
-
"layouts/trifle/docs/#{configuration.layout}"
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def show # rubocop:disable Metrics/AbcSize
|
|
40
|
-
url = [params[:url], params[:format]].compact.join('.')
|
|
32
|
+
def render_llms(url, allow_empty: false)
|
|
41
33
|
meta = Trifle::Docs.meta(url: url, config: configuration)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if Trifle::Docs::Helper::AiDetection.ai_scraper?(request.user_agent) && meta['type'] != 'file'
|
|
45
|
-
render_markdown(url: url, meta: meta)
|
|
46
|
-
return
|
|
47
|
-
end
|
|
48
|
-
render_file(meta: meta) and return if meta['type'] == 'file'
|
|
49
|
-
|
|
50
|
-
render_content(url: url, meta: meta)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def search
|
|
54
|
-
results = Trifle::Docs.search(query: params[:query], scope: params[:scope])
|
|
55
|
-
|
|
56
|
-
render 'search', locals: {
|
|
57
|
-
results: results,
|
|
58
|
-
query: params[:query],
|
|
59
|
-
scope: params[:scope],
|
|
60
|
-
sitemap: Trifle::Docs.sitemap,
|
|
61
|
-
meta: { description: 'Search' }
|
|
62
|
-
}
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def llms
|
|
66
|
-
meta = Trifle::Docs.meta(url: 'llms.txt', config: configuration)
|
|
67
|
-
if meta && meta['type'] == 'file'
|
|
68
|
-
send_file(meta['path'])
|
|
69
|
-
return
|
|
70
|
-
end
|
|
34
|
+
return send_file(meta['path']) if meta && meta['type'] == 'file'
|
|
71
35
|
|
|
72
|
-
content =
|
|
73
|
-
if content.nil?
|
|
74
|
-
|
|
75
|
-
return
|
|
76
|
-
end
|
|
36
|
+
content = yield
|
|
37
|
+
return render_not_found if content.nil?
|
|
38
|
+
return render_not_found if !allow_empty && content.strip.empty?
|
|
77
39
|
|
|
78
40
|
render plain: content, content_type: 'text/markdown'
|
|
79
41
|
end
|
|
80
42
|
|
|
81
|
-
def
|
|
82
|
-
meta = Trifle::Docs.meta(url:
|
|
83
|
-
if meta && meta['type'] == 'file'
|
|
84
|
-
send_file(meta['path'])
|
|
85
|
-
return
|
|
86
|
-
end
|
|
43
|
+
def render_sitemap(url)
|
|
44
|
+
meta = Trifle::Docs.meta(url: url, config: configuration)
|
|
45
|
+
return send_file(meta['path']) if meta && meta['type'] == 'file'
|
|
87
46
|
|
|
88
|
-
content =
|
|
89
|
-
if content.nil? || content.strip.empty?
|
|
90
|
-
render_not_found
|
|
91
|
-
return
|
|
92
|
-
end
|
|
47
|
+
content = yield
|
|
48
|
+
return render_not_found if content.nil? || content.strip.empty?
|
|
93
49
|
|
|
94
|
-
render plain: content, content_type: '
|
|
50
|
+
render plain: content, content_type: 'application/xml'
|
|
95
51
|
end
|
|
96
52
|
|
|
97
53
|
def render_not_found
|
|
@@ -119,6 +75,93 @@ if Object.const_defined?('Rails')
|
|
|
119
75
|
sitemap: Trifle::Docs.sitemap(config: configuration)
|
|
120
76
|
), content_type: 'text/markdown'
|
|
121
77
|
end
|
|
78
|
+
|
|
79
|
+
def fetch_meta(url)
|
|
80
|
+
Trifle::Docs.meta(url: url, config: configuration)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def render_for_meta(meta, url, wants_md, request)
|
|
84
|
+
return render_markdown(url: url, meta: meta) if render_markdown?(meta, wants_md, request)
|
|
85
|
+
return render_file(meta: meta) if file_meta?(meta)
|
|
86
|
+
|
|
87
|
+
render_content(url: url, meta: meta)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def file_meta?(meta)
|
|
91
|
+
meta['type'] == 'file'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def resolve_url(params)
|
|
95
|
+
raw_url = params[:url]
|
|
96
|
+
format = params[:format]
|
|
97
|
+
wants_md = markdown_requested?(format, request)
|
|
98
|
+
url = wants_md ? raw_url : [raw_url, format].compact.join('.')
|
|
99
|
+
[url, wants_md]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def render_markdown?(meta, wants_md, request)
|
|
103
|
+
return false if meta['type'] == 'file'
|
|
104
|
+
|
|
105
|
+
wants_md || Trifle::Docs::Helper::AiDetection.ai_scraper?(request.user_agent)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def markdown_requested?(format, request)
|
|
109
|
+
return true if format.to_s.downcase == 'md'
|
|
110
|
+
|
|
111
|
+
request.headers['Accept'].to_s.include?('text/markdown')
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
class PageController < ActionController::Base
|
|
116
|
+
include PageControllerHelpers
|
|
117
|
+
|
|
118
|
+
layout :docs_layout
|
|
119
|
+
|
|
120
|
+
def configuration
|
|
121
|
+
params[:configuration] || Trifle::Docs.default
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def docs_layout
|
|
125
|
+
"layouts/trifle/docs/#{configuration.layout}"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def show
|
|
129
|
+
url, wants_md = resolve_url(params)
|
|
130
|
+
meta = fetch_meta(url)
|
|
131
|
+
return render_not_found if meta.nil?
|
|
132
|
+
|
|
133
|
+
render_for_meta(meta, url, wants_md, request)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def search
|
|
137
|
+
results = Trifle::Docs.search(query: params[:query], scope: params[:scope])
|
|
138
|
+
|
|
139
|
+
render 'search', locals: {
|
|
140
|
+
results: results,
|
|
141
|
+
query: params[:query],
|
|
142
|
+
scope: params[:scope],
|
|
143
|
+
sitemap: Trifle::Docs.sitemap,
|
|
144
|
+
meta: { description: 'Search' }
|
|
145
|
+
}
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def llms
|
|
149
|
+
render_llms('llms.txt', allow_empty: true) do
|
|
150
|
+
Trifle::Docs::Helper::Llms.homepage_markdown(config: configuration)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def llms_full
|
|
155
|
+
render_llms('llms-full.txt') do
|
|
156
|
+
Trifle::Docs::Helper::Llms.full_markdown(config: configuration)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def sitemap
|
|
161
|
+
render_sitemap('sitemap.xml') do
|
|
162
|
+
Trifle::Docs::Helper::Sitemap.xml(config: configuration)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
122
165
|
end
|
|
123
166
|
end
|
|
124
167
|
end
|
|
@@ -49,20 +49,25 @@ module Trifle
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def format_page(meta:, url:, raw_content:)
|
|
52
|
-
|
|
53
|
-
page_url = meta['url'] || "/#{url}"
|
|
54
|
-
|
|
55
|
-
lines = []
|
|
56
|
-
lines << "# #{title}"
|
|
57
|
-
lines << ''
|
|
58
|
-
lines << "Source: #{page_url}"
|
|
59
|
-
lines << ''
|
|
52
|
+
lines = page_header(meta: meta, url: url)
|
|
60
53
|
lines << raw_content.to_s.strip
|
|
61
54
|
lines << ''
|
|
62
55
|
lines << '---'
|
|
63
56
|
|
|
64
57
|
lines.join("\n")
|
|
65
58
|
end
|
|
59
|
+
|
|
60
|
+
def page_header(meta:, url:)
|
|
61
|
+
title = meta['title'] || Trifle::Docs::Helper::MarkdownLayout.derive_title_from_url(url)
|
|
62
|
+
page_url = meta['url'] || "/#{url}"
|
|
63
|
+
|
|
64
|
+
[
|
|
65
|
+
"# #{title}",
|
|
66
|
+
'',
|
|
67
|
+
"Source: #{page_url}",
|
|
68
|
+
''
|
|
69
|
+
]
|
|
70
|
+
end
|
|
66
71
|
end
|
|
67
72
|
end
|
|
68
73
|
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cgi'
|
|
4
|
+
require 'time'
|
|
5
|
+
|
|
6
|
+
module Trifle
|
|
7
|
+
module Docs
|
|
8
|
+
module Helper
|
|
9
|
+
module Sitemap
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def xml(config: nil)
|
|
13
|
+
sitemap = Trifle::Docs.sitemap(config: config)
|
|
14
|
+
urls = sitemap_urls(sitemap)
|
|
15
|
+
return nil if urls.empty?
|
|
16
|
+
|
|
17
|
+
build_document(urls)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def flatten_sitemap(node, path = [])
|
|
21
|
+
return [] unless node.is_a?(Hash)
|
|
22
|
+
|
|
23
|
+
entries = []
|
|
24
|
+
meta = node['_meta']
|
|
25
|
+
entries << { url: path.join('/'), meta: meta } if meta
|
|
26
|
+
|
|
27
|
+
node.keys.reject { |key| key == '_meta' }.sort.each do |key|
|
|
28
|
+
entries.concat(flatten_sitemap(node[key], path + [key]))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
entries
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def sitemap_urls(sitemap)
|
|
35
|
+
entries = flatten_sitemap(sitemap)
|
|
36
|
+
entries.filter_map do |entry|
|
|
37
|
+
meta = entry[:meta]
|
|
38
|
+
next if meta.nil? || meta['type'] == 'file'
|
|
39
|
+
|
|
40
|
+
build_url_entry(entry[:url], meta)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build_document(urls)
|
|
45
|
+
[
|
|
46
|
+
%(<?xml version="1.0" encoding="UTF-8"?>),
|
|
47
|
+
%(<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">),
|
|
48
|
+
urls.join("\n"),
|
|
49
|
+
%(</urlset>)
|
|
50
|
+
].join("\n")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def build_url_entry(url, meta)
|
|
54
|
+
loc = normalize_loc(url, meta)
|
|
55
|
+
lastmod = format_lastmod(meta['updated_at'])
|
|
56
|
+
lastmod_tag = lastmod ? "<lastmod>#{lastmod}</lastmod>" : ''
|
|
57
|
+
|
|
58
|
+
"<url><loc>#{loc}</loc>#{lastmod_tag}</url>"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def normalize_loc(url, meta)
|
|
62
|
+
loc = meta['url']
|
|
63
|
+
loc = "/#{url}" if loc.nil? || loc.empty?
|
|
64
|
+
loc = '/' if loc == '//'
|
|
65
|
+
CGI.escapeHTML(loc)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def format_lastmod(value)
|
|
69
|
+
return nil if value.nil?
|
|
70
|
+
|
|
71
|
+
return value.utc.iso8601 if value.respond_to?(:utc) && value.respond_to?(:iso8601)
|
|
72
|
+
return value.iso8601 if value.respond_to?(:iso8601)
|
|
73
|
+
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/lib/trifle/docs/version.rb
CHANGED
data/lib/trifle/docs.rb
CHANGED
|
@@ -5,6 +5,7 @@ require_relative 'docs/helper/tree'
|
|
|
5
5
|
require_relative 'docs/helper/markdown_layout'
|
|
6
6
|
require_relative 'docs/helper/ai_detection'
|
|
7
7
|
require_relative 'docs/helper/llms'
|
|
8
|
+
require_relative 'docs/helper/sitemap'
|
|
8
9
|
require_relative 'docs/harvester'
|
|
9
10
|
require_relative 'docs/harvester/file'
|
|
10
11
|
require_relative 'docs/harvester/markdown'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trifle-docs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jozef Vaclavik
|
|
@@ -203,6 +203,7 @@ files:
|
|
|
203
203
|
- lib/trifle/docs/helper/ai_detection.rb
|
|
204
204
|
- lib/trifle/docs/helper/llms.rb
|
|
205
205
|
- lib/trifle/docs/helper/markdown_layout.rb
|
|
206
|
+
- lib/trifle/docs/helper/sitemap.rb
|
|
206
207
|
- lib/trifle/docs/helper/tree.rb
|
|
207
208
|
- lib/trifle/docs/operations/collection.rb
|
|
208
209
|
- lib/trifle/docs/operations/content.rb
|