infodocs 0.3.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 +7 -0
- data/CHANGELOG.md +26 -0
- data/CONTRIBUTING.md +31 -0
- data/LICENSE.txt +21 -0
- data/README.md +341 -0
- data/Rakefile +10 -0
- data/app/controllers/infodocs/application_controller.rb +68 -0
- data/app/controllers/infodocs/assets_controller.rb +15 -0
- data/app/controllers/infodocs/editor_controller.rb +36 -0
- data/app/controllers/infodocs/indexes_controller.rb +22 -0
- data/app/controllers/infodocs/pages_controller.rb +48 -0
- data/app/controllers/infodocs/raws_controller.rb +13 -0
- data/app/controllers/infodocs/searches_controller.rb +18 -0
- data/app/controllers/infodocs/tags_controller.rb +11 -0
- data/app/controllers/infodocs/trees_controller.rb +40 -0
- data/app/helpers/infodocs/application_helper.rb +70 -0
- data/app/javascript/infodocs/application.js +74 -0
- data/app/javascript/infodocs/controllers/editor_controller.js +135 -0
- data/app/javascript/infodocs/controllers/highlight_controller.js +44 -0
- data/app/javascript/infodocs/controllers/mermaid_controller.js +25 -0
- data/app/javascript/infodocs/controllers/search_controller.js +125 -0
- data/app/javascript/infodocs/controllers/sidebar_controller.js +34 -0
- data/app/javascript/infodocs/controllers/toc_controller.js +18 -0
- data/app/javascript/infodocs/controllers/tree_editor_controller.js +65 -0
- data/app/javascript/infodocs/controllers/tree_state_controller.js +37 -0
- data/app/javascript/infodocs/vendor/sortable.esm.js +3378 -0
- data/app/views/infodocs/_delete_button.html.erb +6 -0
- data/app/views/infodocs/_handle.html.erb +5 -0
- data/app/views/infodocs/_tree.html.erb +35 -0
- data/app/views/infodocs/editor/edit.html.erb +61 -0
- data/app/views/infodocs/pages/empty_folder.html.erb +6 -0
- data/app/views/infodocs/pages/not_found.html.erb +11 -0
- data/app/views/infodocs/pages/show.html.erb +92 -0
- data/app/views/infodocs/searches/index.html.erb +20 -0
- data/app/views/infodocs/tags/index.html.erb +26 -0
- data/app/views/layouts/infodocs/application.html.erb +104 -0
- data/config/importmap.rb +6 -0
- data/config/routes.rb +26 -0
- data/lib/generators/infodocs/install/install_generator.rb +59 -0
- data/lib/generators/infodocs/install/templates/getting-started.md.tt +8 -0
- data/lib/generators/infodocs/install/templates/initializer.rb.tt +51 -0
- data/lib/generators/infodocs/views/views_generator.rb +13 -0
- data/lib/infodocs/code_theme.rb +130 -0
- data/lib/infodocs/configuration.rb +50 -0
- data/lib/infodocs/engine.rb +39 -0
- data/lib/infodocs/link_checker.rb +43 -0
- data/lib/infodocs/linter.rb +48 -0
- data/lib/infodocs/page.rb +124 -0
- data/lib/infodocs/rails_lexer.rb +39 -0
- data/lib/infodocs/renderer.rb +231 -0
- data/lib/infodocs/search.rb +131 -0
- data/lib/infodocs/source.rb +35 -0
- data/lib/infodocs/tree.rb +215 -0
- data/lib/infodocs/tree_editor.rb +179 -0
- data/lib/infodocs/url_helper.rb +23 -0
- data/lib/infodocs/version.rb +5 -0
- data/lib/infodocs.rb +74 -0
- data/lib/tasks/infodocs.rake +63 -0
- data/sig/infodocs.rbs +4 -0
- metadata +192 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "open3"
|
|
5
|
+
|
|
6
|
+
module Infodocs
|
|
7
|
+
# Moves a page or folder to another folder / position by renaming files and
|
|
8
|
+
# rewriting the `order` lists of the affected `_tree.yml` manifests.
|
|
9
|
+
class TreeEditor
|
|
10
|
+
class Invalid < Error; end
|
|
11
|
+
|
|
12
|
+
def initialize(tree)
|
|
13
|
+
@tree = tree
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# slug: node to move; parent: slug of the destination folder ("" = top);
|
|
17
|
+
# before: slug of the sibling it goes in front of (nil = last).
|
|
18
|
+
def move(slug:, parent:, before: nil)
|
|
19
|
+
node = @tree.node(slug)
|
|
20
|
+
raise Invalid, "unknown node #{slug.inspect}" if node.nil? || node.path.nil?
|
|
21
|
+
|
|
22
|
+
target = @tree.node(parent)
|
|
23
|
+
raise Invalid, "unknown destination #{parent.inspect}" if target.nil? || !target.folder?
|
|
24
|
+
raise Invalid, "cannot place items next to multiple source roots" if target.path.nil?
|
|
25
|
+
raise Invalid, "source folders cannot be moved" if @tree.root.children.include?(node) && @tree.multiple_roots?
|
|
26
|
+
raise Invalid, "cannot move a folder into itself" if node.folder? && (target.slug == node.slug || target.slug.start_with?("#{node.slug}/"))
|
|
27
|
+
|
|
28
|
+
source_dir = node.path.parent
|
|
29
|
+
name = node.name
|
|
30
|
+
destination = target.path.join(node.folder? ? name : "#{name}.md")
|
|
31
|
+
|
|
32
|
+
if source_dir != target.path
|
|
33
|
+
raise Invalid, "#{destination.basename} already exists in the destination" if destination.exist?
|
|
34
|
+
|
|
35
|
+
carried = node.leaf? ? (@tree.node(node.slug)&.page&.manifest_tags || []) : []
|
|
36
|
+
moved_pages = pages_under(node)
|
|
37
|
+
rename(node.path, destination)
|
|
38
|
+
leave_redirects(moved_pages, node, destination, target)
|
|
39
|
+
update_manifest(source_dir) { |manifest| remove_name(manifest, name) }
|
|
40
|
+
update_manifest(target.path) { |manifest| set_tags(manifest, name, carried) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
order = ordered_names(target, exclude: name)
|
|
44
|
+
sibling = before && @tree.node(before)&.name
|
|
45
|
+
index = sibling && order.index(sibling) || order.size
|
|
46
|
+
order.insert(index, name)
|
|
47
|
+
update_manifest(target.path) { |manifest| manifest["order"] = order }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Saves the tags of a page in its folder's `_tree.yml` (`tags: { name: [..] }`).
|
|
51
|
+
# Tags that come from the page's own frontmatter are left alone.
|
|
52
|
+
def tag(slug:, tags:)
|
|
53
|
+
node = @tree.node(slug)
|
|
54
|
+
raise Invalid, "unknown page #{slug.inspect}" if node.nil? || node.path.nil? || !node.leaf?
|
|
55
|
+
|
|
56
|
+
list = Page.normalize_tags(tags) - Page.normalize_tags(node.page.frontmatter["tags"])
|
|
57
|
+
update_manifest(node.path.parent) { |manifest| set_tags(manifest, node.name, list) }
|
|
58
|
+
list
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Creates a page (`name.md`) or an (empty) folder with a `_tree.yml` titled
|
|
62
|
+
# after it, at the end of `parent`. Returns the slug of the new node.
|
|
63
|
+
def create(parent:, title:, kind: :page)
|
|
64
|
+
title = title.to_s.strip
|
|
65
|
+
raise Invalid, "title can't be blank" if title.empty?
|
|
66
|
+
|
|
67
|
+
target = @tree.node(parent)
|
|
68
|
+
raise Invalid, "unknown destination #{parent.inspect}" if target.nil? || !target.folder? || target.path.nil?
|
|
69
|
+
|
|
70
|
+
name = unique_name(target, title.parameterize.presence || "untitled")
|
|
71
|
+
if kind.to_s == "folder"
|
|
72
|
+
dir = target.path.join(name)
|
|
73
|
+
FileUtils.mkdir_p(dir)
|
|
74
|
+
dir.join(Tree::MANIFEST).write({ "title" => title }.to_yaml.sub(/\A---\s*\n/, ""))
|
|
75
|
+
else
|
|
76
|
+
target.path.join("#{name}.md").write("# #{title}\n\n")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
order = ordered_names(target, exclude: name) << name
|
|
80
|
+
update_manifest(target.path) { |manifest| manifest["order"] = order }
|
|
81
|
+
[target.slug, name].reject(&:empty?).join("/")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Deletes a page or a whole folder (with everything inside). Returns the
|
|
85
|
+
# slug of the folder that contained it. Tracked files go through `git rm`.
|
|
86
|
+
def delete(slug:)
|
|
87
|
+
node = @tree.node(slug)
|
|
88
|
+
raise Invalid, "unknown node #{slug.inspect}" if node.nil? || node.path.nil? || node.slug.empty?
|
|
89
|
+
raise Invalid, "source folders cannot be deleted" if @tree.root.children.include?(node) && @tree.multiple_roots?
|
|
90
|
+
raise Invalid, "path is outside the docs sources" unless inside_source?(node.path)
|
|
91
|
+
|
|
92
|
+
parent_dir = node.path.parent
|
|
93
|
+
remove(node.path)
|
|
94
|
+
update_manifest(parent_dir) { |manifest| remove_name(manifest, node.name) }
|
|
95
|
+
File.dirname(node.slug).sub(/\A\.\z/, "")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def inside_source?(path)
|
|
101
|
+
real = path.realpath
|
|
102
|
+
@tree.sources.any? { |source| real.to_s.start_with?("#{source.path.realpath}/") }
|
|
103
|
+
rescue Errno::ENOENT
|
|
104
|
+
false
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def remove(path)
|
|
108
|
+
_out, _err, status = Open3.capture3("git", "rm", "-rq", "--", path.to_s, chdir: path.parent.to_s)
|
|
109
|
+
FileUtils.rm_rf(path) unless status.success?
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def unique_name(folder, base)
|
|
113
|
+
taken = folder.children.map(&:name) + folder.path.children.map { |child| child.basename(".md").to_s }
|
|
114
|
+
name = base
|
|
115
|
+
counter = 1
|
|
116
|
+
name = "#{base}-#{counter += 1}" while taken.include?(name) || name.start_with?("_")
|
|
117
|
+
name
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Current display order of the folder's children (all, hidden included).
|
|
121
|
+
def ordered_names(folder, exclude:)
|
|
122
|
+
folder.children.map(&:name) - [exclude]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# [page, path relative to the moved node] for the page, or every page in the folder.
|
|
126
|
+
def pages_under(node)
|
|
127
|
+
return [[node.page, nil]] if node.leaf?
|
|
128
|
+
|
|
129
|
+
@tree.all_pages.select { |page| page.slug == node.slug || page.slug.start_with?("#{node.slug}/") }
|
|
130
|
+
.map { |page| [page, page.file.relative_path_from(node.path)] }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Old URLs keep working: each moved page lists its previous slug in `redirect_from`.
|
|
134
|
+
def leave_redirects(moved_pages, node, destination, target)
|
|
135
|
+
new_prefix = [target.slug, node.name].reject(&:empty?).join("/")
|
|
136
|
+
moved_pages.each do |page, relative|
|
|
137
|
+
file = relative ? destination.join(relative) : destination
|
|
138
|
+
next unless file.file?
|
|
139
|
+
|
|
140
|
+
old_slug = page.slug
|
|
141
|
+
new_slug = node.leaf? ? new_prefix : old_slug.sub(/\A#{Regexp.escape(node.slug)}/, new_prefix)
|
|
142
|
+
moved = Page.new(file: file, slug: new_slug, root: target.path)
|
|
143
|
+
moved.add_redirect_from(old_slug)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def set_tags(manifest, name, tags)
|
|
148
|
+
map = manifest["tags"].is_a?(Hash) ? manifest["tags"].transform_keys(&:to_s) : {}
|
|
149
|
+
tags.empty? ? map.delete(name) : map[name] = tags
|
|
150
|
+
map.empty? ? manifest.delete("tags") : manifest["tags"] = map
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def remove_name(manifest, name)
|
|
154
|
+
set_tags(manifest, name, []) if manifest["tags"].is_a?(Hash)
|
|
155
|
+
manifest["order"] = Array(manifest["order"]).map(&:to_s) - [name] if manifest.key?("order")
|
|
156
|
+
manifest["hidden"] = Array(manifest["hidden"]).map(&:to_s) - [name] if manifest.key?("hidden")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def update_manifest(dir)
|
|
160
|
+
file = dir.join(Tree::MANIFEST)
|
|
161
|
+
manifest = file.file? ? (YAML.safe_load(file.read) || {}) : {}
|
|
162
|
+
yield manifest
|
|
163
|
+
manifest.delete("order") if manifest["order"] == []
|
|
164
|
+
manifest.delete("hidden") if manifest["hidden"] == []
|
|
165
|
+
if manifest.empty?
|
|
166
|
+
file.delete if file.exist?
|
|
167
|
+
else
|
|
168
|
+
file.write(manifest.to_yaml.sub(/\A---\s*\n/, ""))
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# `git mv` keeps history readable when the file is tracked; plain rename otherwise.
|
|
173
|
+
def rename(from, to)
|
|
174
|
+
Open3.capture3("git", "mv", from.to_s, to.to_s, chdir: from.parent.to_s).then do |_out, _err, status|
|
|
175
|
+
FileUtils.mv(from, to) unless status.success?
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Infodocs
|
|
4
|
+
# Link to the docs from the host app: `link_to "Setup", infodocs_page_path("guides/setup")`.
|
|
5
|
+
# Included into the host's views and controllers by the engine.
|
|
6
|
+
module UrlHelper
|
|
7
|
+
def infodocs_page_path(slug = nil, **options)
|
|
8
|
+
slug = clean_infodocs_slug(slug)
|
|
9
|
+
slug.empty? ? infodocs.root_path(**options) : infodocs.page_path(slug, **options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def infodocs_page_url(slug = nil, **options)
|
|
13
|
+
slug = clean_infodocs_slug(slug)
|
|
14
|
+
slug.empty? ? infodocs.root_url(**options) : infodocs.page_url(slug, **options)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def clean_infodocs_slug(slug)
|
|
20
|
+
slug.to_s.delete_prefix("/").delete_suffix("/").delete_suffix(".md")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/infodocs.rb
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "infodocs/version"
|
|
4
|
+
|
|
5
|
+
module Infodocs
|
|
6
|
+
class Error < StandardError; end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require_relative "infodocs/source"
|
|
10
|
+
require_relative "infodocs/code_theme"
|
|
11
|
+
require_relative "infodocs/configuration"
|
|
12
|
+
require_relative "infodocs/page"
|
|
13
|
+
require_relative "infodocs/tree"
|
|
14
|
+
require_relative "infodocs/tree_editor"
|
|
15
|
+
require_relative "infodocs/renderer"
|
|
16
|
+
require_relative "infodocs/url_helper"
|
|
17
|
+
require_relative "infodocs/link_checker"
|
|
18
|
+
require_relative "infodocs/linter"
|
|
19
|
+
require_relative "infodocs/search"
|
|
20
|
+
require_relative "infodocs/engine" if defined?(Rails)
|
|
21
|
+
|
|
22
|
+
module Infodocs
|
|
23
|
+
class << self
|
|
24
|
+
def config
|
|
25
|
+
@config ||= Configuration.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def configure
|
|
29
|
+
yield config
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def reset!
|
|
33
|
+
@config = nil
|
|
34
|
+
@tree = nil
|
|
35
|
+
@search = nil
|
|
36
|
+
@rendered = nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def tree
|
|
40
|
+
@tree ||= Tree.build(config.paths, drafts: show_drafts?)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# `draft: true` pages are visible only while developing the docs.
|
|
44
|
+
def show_drafts?
|
|
45
|
+
defined?(Rails) && Rails.env.development?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Editing is a development-only feature, on top of `config.editable`.
|
|
49
|
+
def editing_enabled?
|
|
50
|
+
config.editable && Rails.env.development?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
RENDER_CACHE_LIMIT = 500
|
|
54
|
+
|
|
55
|
+
# Rendered page, cached until the file changes on disk.
|
|
56
|
+
def render(page, mount_path: "/docs")
|
|
57
|
+
stat = page.file.stat
|
|
58
|
+
key = [page.file.to_s, stat.mtime.to_r, stat.size, mount_path]
|
|
59
|
+
@rendered ||= {}
|
|
60
|
+
@rendered.delete(@rendered.keys.first) if @rendered.size >= RENDER_CACHE_LIMIT && !@rendered.key?(key)
|
|
61
|
+
@rendered[key] ||= Renderer.new(page, mount_path: mount_path).call
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def reload!
|
|
65
|
+
@tree = nil
|
|
66
|
+
@search = nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Reindexed once per tree load (every reload in development).
|
|
70
|
+
def search
|
|
71
|
+
@search ||= Search.new(tree, db_path: config.index_path.to_s).tap(&:reindex!)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :infodocs do
|
|
4
|
+
desc "Report broken relative links, anchors and images in the docs"
|
|
5
|
+
task check_links: :environment do
|
|
6
|
+
problems = Infodocs::LinkChecker.new(Infodocs.tree).call
|
|
7
|
+
problems.each { |problem| puts "#{problem.page.file}: #{problem.target} (#{problem.reason})" }
|
|
8
|
+
puts "#{problems.size} broken link#{'s' unless problems.size == 1}"
|
|
9
|
+
exit(1) if problems.any?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc "Check content: titles, frontmatter keys, hidden pages nobody links to, useless redirects"
|
|
13
|
+
task lint: :environment do
|
|
14
|
+
problems = Infodocs::Linter.new(Infodocs.tree).call
|
|
15
|
+
problems.each { |problem| puts "#{problem.page.file}: #{problem.message} [#{problem.rule}]" }
|
|
16
|
+
puts "#{problems.size} problem#{'s' unless problems.size == 1}"
|
|
17
|
+
exit(1) if problems.any?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
desc "Export the docs as static HTML (default dir: tmp/infodocs-export). Run assets:precompile first"
|
|
21
|
+
task :export, [:dir] => :environment do |_task, args|
|
|
22
|
+
require "fileutils"
|
|
23
|
+
out = Pathname(args[:dir] || Rails.root.join("tmp/infodocs-export"))
|
|
24
|
+
mount = Infodocs::Engine.routes.find_script_name({}).presence || "/docs"
|
|
25
|
+
session = ActionDispatch::Integration::Session.new(Rails.application)
|
|
26
|
+
session.host! "localhost"
|
|
27
|
+
|
|
28
|
+
slugs = Infodocs.tree.all_pages.map(&:slug)
|
|
29
|
+
slugs.each do |slug|
|
|
30
|
+
path = [mount, slug].reject(&:empty?).join("/")
|
|
31
|
+
session.get(path)
|
|
32
|
+
abort "#{path} returned #{session.response.status}; static export needs auth = :none" unless session.response.successful?
|
|
33
|
+
|
|
34
|
+
target = out.join(*mount.split("/").reject(&:empty?), *slug.split("/"), "index.html")
|
|
35
|
+
FileUtils.mkdir_p(target.dirname)
|
|
36
|
+
target.write(session.response.body)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
%w[llms.txt sitemap.xml].each do |name|
|
|
40
|
+
session.get("#{mount}/#{name}")
|
|
41
|
+
File.write(out.join(*mount.split("/").reject(&:empty?), name), session.response.body) if session.response.successful?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Images and files next to the pages.
|
|
45
|
+
Infodocs.tree.sources.each do |source|
|
|
46
|
+
Dir.glob(source.path.join("**/*")).each do |file|
|
|
47
|
+
next unless Infodocs::Tree::ASSET_TYPES.key?(File.extname(file).downcase)
|
|
48
|
+
|
|
49
|
+
relative = Pathname(file).relative_path_from(source.path).to_s
|
|
50
|
+
next if relative.split("/").any? { |part| part.start_with?("_", ".") }
|
|
51
|
+
|
|
52
|
+
slug = Infodocs.tree.multiple_roots? ? File.join(source.key, relative) : relative
|
|
53
|
+
copy = out.join(*mount.split("/").reject(&:empty?), "_asset", slug)
|
|
54
|
+
FileUtils.mkdir_p(copy.dirname)
|
|
55
|
+
FileUtils.cp(file, copy)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
public_assets = Rails.root.join("public/assets")
|
|
60
|
+
FileUtils.cp_r(public_assets, out.join("assets")) if public_assets.directory?
|
|
61
|
+
puts "Exported #{slugs.size} pages to #{out}"
|
|
62
|
+
end
|
|
63
|
+
end
|
data/sig/infodocs.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: infodocs
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jakub Cieślar
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-10-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '7.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '7.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: commonmarker
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '2.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '2.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rouge
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '4.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '4.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: sqlite3
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.7'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.7'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: importmap-rails
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '2.0'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '2.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: stimulus-rails
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.3'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.3'
|
|
97
|
+
description: Mountable Rails engine that serves .md files from your repo as a docs
|
|
98
|
+
site with a sidebar tree (_tree.yml ordering), SQLite FTS5 search, server-side syntax
|
|
99
|
+
highlighting and Mermaid diagrams. Tailwind + Stimulus.
|
|
100
|
+
email:
|
|
101
|
+
- cieslar.jakub@gmail.com
|
|
102
|
+
executables: []
|
|
103
|
+
extensions: []
|
|
104
|
+
extra_rdoc_files: []
|
|
105
|
+
files:
|
|
106
|
+
- CHANGELOG.md
|
|
107
|
+
- CONTRIBUTING.md
|
|
108
|
+
- LICENSE.txt
|
|
109
|
+
- README.md
|
|
110
|
+
- Rakefile
|
|
111
|
+
- app/controllers/infodocs/application_controller.rb
|
|
112
|
+
- app/controllers/infodocs/assets_controller.rb
|
|
113
|
+
- app/controllers/infodocs/editor_controller.rb
|
|
114
|
+
- app/controllers/infodocs/indexes_controller.rb
|
|
115
|
+
- app/controllers/infodocs/pages_controller.rb
|
|
116
|
+
- app/controllers/infodocs/raws_controller.rb
|
|
117
|
+
- app/controllers/infodocs/searches_controller.rb
|
|
118
|
+
- app/controllers/infodocs/tags_controller.rb
|
|
119
|
+
- app/controllers/infodocs/trees_controller.rb
|
|
120
|
+
- app/helpers/infodocs/application_helper.rb
|
|
121
|
+
- app/javascript/infodocs/application.js
|
|
122
|
+
- app/javascript/infodocs/controllers/editor_controller.js
|
|
123
|
+
- app/javascript/infodocs/controllers/highlight_controller.js
|
|
124
|
+
- app/javascript/infodocs/controllers/mermaid_controller.js
|
|
125
|
+
- app/javascript/infodocs/controllers/search_controller.js
|
|
126
|
+
- app/javascript/infodocs/controllers/sidebar_controller.js
|
|
127
|
+
- app/javascript/infodocs/controllers/toc_controller.js
|
|
128
|
+
- app/javascript/infodocs/controllers/tree_editor_controller.js
|
|
129
|
+
- app/javascript/infodocs/controllers/tree_state_controller.js
|
|
130
|
+
- app/javascript/infodocs/vendor/sortable.esm.js
|
|
131
|
+
- app/views/infodocs/_delete_button.html.erb
|
|
132
|
+
- app/views/infodocs/_handle.html.erb
|
|
133
|
+
- app/views/infodocs/_tree.html.erb
|
|
134
|
+
- app/views/infodocs/editor/edit.html.erb
|
|
135
|
+
- app/views/infodocs/pages/empty_folder.html.erb
|
|
136
|
+
- app/views/infodocs/pages/not_found.html.erb
|
|
137
|
+
- app/views/infodocs/pages/show.html.erb
|
|
138
|
+
- app/views/infodocs/searches/index.html.erb
|
|
139
|
+
- app/views/infodocs/tags/index.html.erb
|
|
140
|
+
- app/views/layouts/infodocs/application.html.erb
|
|
141
|
+
- config/importmap.rb
|
|
142
|
+
- config/routes.rb
|
|
143
|
+
- lib/generators/infodocs/install/install_generator.rb
|
|
144
|
+
- lib/generators/infodocs/install/templates/getting-started.md.tt
|
|
145
|
+
- lib/generators/infodocs/install/templates/initializer.rb.tt
|
|
146
|
+
- lib/generators/infodocs/views/views_generator.rb
|
|
147
|
+
- lib/infodocs.rb
|
|
148
|
+
- lib/infodocs/code_theme.rb
|
|
149
|
+
- lib/infodocs/configuration.rb
|
|
150
|
+
- lib/infodocs/engine.rb
|
|
151
|
+
- lib/infodocs/link_checker.rb
|
|
152
|
+
- lib/infodocs/linter.rb
|
|
153
|
+
- lib/infodocs/page.rb
|
|
154
|
+
- lib/infodocs/rails_lexer.rb
|
|
155
|
+
- lib/infodocs/renderer.rb
|
|
156
|
+
- lib/infodocs/search.rb
|
|
157
|
+
- lib/infodocs/source.rb
|
|
158
|
+
- lib/infodocs/tree.rb
|
|
159
|
+
- lib/infodocs/tree_editor.rb
|
|
160
|
+
- lib/infodocs/url_helper.rb
|
|
161
|
+
- lib/infodocs/version.rb
|
|
162
|
+
- lib/tasks/infodocs.rake
|
|
163
|
+
- sig/infodocs.rbs
|
|
164
|
+
homepage: https://github.com/jcieslar/infodocs
|
|
165
|
+
licenses:
|
|
166
|
+
- MIT
|
|
167
|
+
metadata:
|
|
168
|
+
homepage_uri: https://github.com/jcieslar/infodocs
|
|
169
|
+
source_code_uri: https://github.com/jcieslar/infodocs
|
|
170
|
+
rubygems_mfa_required: 'true'
|
|
171
|
+
changelog_uri: https://github.com/jcieslar/infodocs/blob/main/CHANGELOG.md
|
|
172
|
+
post_install_message:
|
|
173
|
+
rdoc_options: []
|
|
174
|
+
require_paths:
|
|
175
|
+
- lib
|
|
176
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - ">="
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: 3.2.0
|
|
181
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
182
|
+
requirements:
|
|
183
|
+
- - ">="
|
|
184
|
+
- !ruby/object:Gem::Version
|
|
185
|
+
version: '0'
|
|
186
|
+
requirements: []
|
|
187
|
+
rubygems_version: 3.5.16
|
|
188
|
+
signing_key:
|
|
189
|
+
specification_version: 4
|
|
190
|
+
summary: 'Markdown documentation browser for Rails: tree sidebar, full-text search,
|
|
191
|
+
Mermaid.'
|
|
192
|
+
test_files: []
|