docs-kit 1.0.4 → 1.0.5
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/CHANGELOG.md +13 -0
- data/app/components/docs_ui/landing.rb +167 -0
- data/lib/docs_kit/configuration.rb +10 -0
- data/lib/docs_kit/landing_config.rb +121 -0
- data/lib/docs_kit/version.rb +1 -1
- data/lib/docs_kit.rb +1 -0
- data/lib/generators/docs_kit/install/templates/docs_kit.rb.erb +19 -0
- data/lib/generators/docs_kit/install/templates/landing.rb.erb +6 -15
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cf583f1cfdfc22d788c0dcffac91b35a374138d8670cfe24bdef3cc2e05eb171
|
|
4
|
+
data.tar.gz: 7c3168bcaf1d0d1939350d704a12cb39f0e37a561be75a255517dcf82e531eba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43c6f2f94b23ca4ad589a701f92121b3dcc3027cc06173432a653e215e9c4da2ab62e1a2044cf9ef3b1e19be9516d7a2698fc6b259f390ce3a15e5c2df50f6eb
|
|
7
|
+
data.tar.gz: 001253000ff36f16cd9ab76377cb4175dcab526a5a8dc2897fd0bde9809016d0dbd42fe5aa1e6474c24e7260eed25468843c393a4e73d447dc01f16c1a87b489
|
data/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,19 @@
|
|
|
18
18
|
|
|
19
19
|
### Added
|
|
20
20
|
|
|
21
|
+
- **`DocsUI::Landing` — a config-driven marketing landing page.** Every consuming
|
|
22
|
+
site (and this dogfood site) was hand-rolling a home page; now render
|
|
23
|
+
`DocsUI::Landing` and drive it from a new `c.landing` config block
|
|
24
|
+
(`DocsKit::LandingConfig`): a hero (`eyebrow`, `title` — wrap a run in
|
|
25
|
+
`**double asterisks**` to accent it in the primary color, `lead`, an optional
|
|
26
|
+
`install` code snippet, and `ctas`), a `features` card grid, and a
|
|
27
|
+
registry-grouped documentation index built from `nav_groups` (so it never drifts
|
|
28
|
+
from the authored pages). Every field is optional — with an empty `c.landing` it
|
|
29
|
+
still renders a minimal hero (brand + doc index), never a broken page — and its
|
|
30
|
+
`.md`/`.text` twin works like any page. The install generator's `landings#show`
|
|
31
|
+
now renders it and the initializer documents `c.landing`. This is the first
|
|
32
|
+
landing pattern proven on a **mounted** docs app (a docs section inside a larger
|
|
33
|
+
Rails app whose `/` is already taken), contributed back from that use case.
|
|
21
34
|
- **SEO + social sharing.** Every page now emits a complete SEO `<head>` —
|
|
22
35
|
meta description, Open Graph, Twitter Card, canonical, favicon, robots, and
|
|
23
36
|
theme-color — via the new `DocsUI::MetaTags` component, driven entirely by a
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocsUI
|
|
4
|
+
# The marketing landing page — a hero (eyebrow + title + lead + optional install
|
|
5
|
+
# snippet + CTA buttons), a feature-card grid, and a registry-grouped
|
|
6
|
+
# documentation index — rendered inside DocsUI::Shell. Every consuming site was
|
|
7
|
+
# hand-rolling this; drive it from config instead:
|
|
8
|
+
#
|
|
9
|
+
# # config/initializers/docs_kit.rb
|
|
10
|
+
# DocsKit.configure do |c|
|
|
11
|
+
# c.landing.eyebrow = "Developer Docs"
|
|
12
|
+
# c.landing.title = "Jobs & events on **Postgres**" # ** ** → primary color
|
|
13
|
+
# c.landing.lead = "PostgreSQL-native jobs + event bus for Rails."
|
|
14
|
+
# c.landing.install = { code: 'gem "pgbus"', filename: "Gemfile", lexer: :ruby }
|
|
15
|
+
# c.landing.ctas = [{ label: "Get started", href: "/docs/overview", style: :primary }]
|
|
16
|
+
# c.landing.features = [{ icon: "database", title: "One database", body: "No Redis." }]
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# # a controller that includes DocsKit::Controller
|
|
20
|
+
# def show = render_page(DocsUI::Landing.new)
|
|
21
|
+
#
|
|
22
|
+
# Everything is optional: with an empty c.landing it still renders a minimal hero
|
|
23
|
+
# (the brand name + the doc index), never a broken page. The doc index is built
|
|
24
|
+
# from DocsKit.configuration.nav_groups — the same registry the sidebar uses — so
|
|
25
|
+
# it never drifts from the authored pages.
|
|
26
|
+
#
|
|
27
|
+
# It IS a full document (composes Shell), so a controller renders it with
|
|
28
|
+
# `layout: false`, exactly like DocsUI::Page (DocsKit::Controller#render_page
|
|
29
|
+
# does this). The `.md`/`.text` twin of the landing works too — MarkdownExport
|
|
30
|
+
# walks the same #docs-content region Shell stamps.
|
|
31
|
+
class Landing < Phlex::HTML
|
|
32
|
+
include Phlex::Rails::Helpers::Request
|
|
33
|
+
|
|
34
|
+
def view_template
|
|
35
|
+
render DocsUI::Shell.new(title: landing.eyebrow || config.brand) do
|
|
36
|
+
div(class: "mx-auto max-w-5xl") do
|
|
37
|
+
hero
|
|
38
|
+
feature_grid
|
|
39
|
+
doc_index
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def config = DocsKit.configuration
|
|
47
|
+
def landing = config.landing
|
|
48
|
+
|
|
49
|
+
# --- hero ----------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
def hero
|
|
52
|
+
div(class: "flex flex-col gap-6") do
|
|
53
|
+
eyebrow
|
|
54
|
+
heading
|
|
55
|
+
lead
|
|
56
|
+
install_snippet
|
|
57
|
+
ctas
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def eyebrow
|
|
62
|
+
return unless (text = landing.eyebrow)
|
|
63
|
+
|
|
64
|
+
p(class: "text-sm font-medium uppercase tracking-wide text-primary") { text }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The <h1>. A **run** wrapped in double asterisks renders in the primary color
|
|
68
|
+
# (the one bit of markdown we honor, so a site can accent a word without HTML).
|
|
69
|
+
def heading
|
|
70
|
+
h1(class: "text-4xl font-bold tracking-tight md:text-5xl") do
|
|
71
|
+
(landing.title || config.brand).to_s.split(/\*\*(.+?)\*\*/).each_with_index do |part, index|
|
|
72
|
+
next if part.empty?
|
|
73
|
+
|
|
74
|
+
index.odd? ? span(class: "text-primary") { part } : plain(part)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def lead
|
|
80
|
+
return unless (text = landing.lead || config.tagline)
|
|
81
|
+
|
|
82
|
+
p(class: "max-w-2xl text-lg text-base-content/70") { text }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def install_snippet
|
|
86
|
+
return unless (snippet = landing.install_snippet)
|
|
87
|
+
|
|
88
|
+
render DocsUI::Code.new(snippet[:code], lexer: snippet[:lexer], filename: snippet[:filename])
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def ctas
|
|
92
|
+
buttons = landing.ctas
|
|
93
|
+
return if buttons.empty?
|
|
94
|
+
|
|
95
|
+
div(class: "flex flex-wrap items-center gap-4 pt-2") do
|
|
96
|
+
buttons.each { |cta| cta_button(cta) }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def cta_button(cta)
|
|
101
|
+
attrs = { href: cta.href, class: "#{cta.btn_class} gap-2" }
|
|
102
|
+
if cta.external?
|
|
103
|
+
attrs[:target] = "_blank"
|
|
104
|
+
attrs[:rel] = "noopener"
|
|
105
|
+
end
|
|
106
|
+
a(**attrs) do
|
|
107
|
+
render DocsUI::BrandMark.new(cta.icon, class: "size-4", label: cta.label) if cta.icon
|
|
108
|
+
plain cta.label
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# --- feature grid --------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
def feature_grid
|
|
115
|
+
features = landing.features
|
|
116
|
+
return if features.empty?
|
|
117
|
+
|
|
118
|
+
div(class: "mt-12 grid gap-4 sm:grid-cols-2") do
|
|
119
|
+
features.each { |feature| feature_card(feature) }
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def feature_card(feature)
|
|
124
|
+
div(class: "rounded-box border border-base-300 bg-base-200/40 p-5") do
|
|
125
|
+
div(class: "flex items-center gap-2 text-primary") do
|
|
126
|
+
render DocsUI::Icon.new(feature.icon, class: "size-5") if feature.icon
|
|
127
|
+
span(class: "font-semibold text-base-content") { feature.title }
|
|
128
|
+
end
|
|
129
|
+
p(class: "mt-2 text-sm text-base-content/70") { feature.body } if feature.body
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# --- documentation index -------------------------------------------------
|
|
134
|
+
|
|
135
|
+
# The registry-grouped page index. nav_groups is the three-level Hash the
|
|
136
|
+
# sidebar renders ({ heading => { subgroup => [NavItem] } }); the landing
|
|
137
|
+
# flattens each heading's items into a linked column.
|
|
138
|
+
def doc_index
|
|
139
|
+
return if !landing.doc_index? || (groups = flattened_nav).empty?
|
|
140
|
+
|
|
141
|
+
div(class: "mt-16") do
|
|
142
|
+
h2(class: "text-sm font-semibold uppercase tracking-wide text-base-content/50") { "Documentation" }
|
|
143
|
+
div(class: "mt-6 grid gap-8 sm:grid-cols-2") do
|
|
144
|
+
groups.each { |heading, items| doc_index_group(heading, items) }
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def doc_index_group(heading, items)
|
|
150
|
+
div do
|
|
151
|
+
h3(class: "text-xs font-semibold uppercase tracking-wide text-base-content/40") { heading }
|
|
152
|
+
ul(class: "mt-3 flex flex-col gap-2") do
|
|
153
|
+
items.each { |item| li { a(href: item.href, class: "link link-hover text-sm") { item.label } } }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Collapse nav_groups ({ heading => { subgroup => [item] } }) to
|
|
159
|
+
# { heading => [item, ...] } — the landing shows one flat column per heading.
|
|
160
|
+
def flattened_nav
|
|
161
|
+
config.nav_groups.each_with_object({}) do |(heading, grouped), acc|
|
|
162
|
+
items = Array(grouped).flat_map { |_subgroup, list| Array(list) }
|
|
163
|
+
acc[heading] = items unless items.empty?
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "seo_config"
|
|
4
|
+
require_relative "landing_config"
|
|
4
5
|
|
|
5
6
|
module DocsKit
|
|
6
7
|
# Per-site configuration for the shared docs chrome. Everything that differs
|
|
@@ -281,6 +282,15 @@ module DocsKit
|
|
|
281
282
|
@seo ||= DocsKit::SeoConfig.new
|
|
282
283
|
end
|
|
283
284
|
|
|
285
|
+
# The landing-page knobs (DocsKit::LandingConfig), read by DocsUI::Landing.
|
|
286
|
+
# Lazily built and memoized so a `c.landing.title = ...` block mutates the one
|
|
287
|
+
# instance the component later reads. A site that never touches it still gets a
|
|
288
|
+
# minimal hero + the doc index (see LandingConfig), so DocsUI::Landing is safe
|
|
289
|
+
# to render with zero landing config.
|
|
290
|
+
def landing
|
|
291
|
+
@landing ||= DocsKit::LandingConfig.new
|
|
292
|
+
end
|
|
293
|
+
|
|
284
294
|
# The loaded DocsKit::OpenApi::Document for #openapi. Memoized; when #openapi
|
|
285
295
|
# is a file path, the memo is invalidated on an mtime change so editing the
|
|
286
296
|
# spec in development is picked up without a server restart. Raises a
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DocsKit
|
|
4
|
+
# The per-site landing-page knobs, read by DocsUI::Landing to render a marketing
|
|
5
|
+
# home page (hero + feature grid + doc index) without a site hand-rolling one.
|
|
6
|
+
# Nested under DocsKit::Configuration#landing so a site configures it as a block:
|
|
7
|
+
#
|
|
8
|
+
# DocsKit.configure do |c|
|
|
9
|
+
# c.landing.eyebrow = "Developer Docs"
|
|
10
|
+
# c.landing.title = "Jobs & events on Postgres" # highlight a run with **…**
|
|
11
|
+
# c.landing.lead = "PostgreSQL-native job processing and event bus for Rails."
|
|
12
|
+
# c.landing.install = { code: 'gem "pgbus"', filename: "Gemfile", lexer: :ruby }
|
|
13
|
+
# c.landing.ctas = [
|
|
14
|
+
# { label: "Get started", href: "/docs/overview", style: :primary },
|
|
15
|
+
# { label: "GitHub", href: "https://github.com/me/repo", style: :ghost, icon: :github },
|
|
16
|
+
# ]
|
|
17
|
+
# c.landing.features = [
|
|
18
|
+
# { icon: "database", title: "One database", body: "No Redis, no broker." },
|
|
19
|
+
# { icon: "zap", title: "Fast", body: "…" },
|
|
20
|
+
# ]
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# Every field is optional and defaults to a backwards-safe value: a site that
|
|
24
|
+
# sets none still renders a minimal hero (the brand + a doc index), never a
|
|
25
|
+
# broken page. Plain accessors (not Data.define) because each field is
|
|
26
|
+
# individually assignable in the `c.landing.x = ...` block, mirroring
|
|
27
|
+
# DocsKit::SeoConfig.
|
|
28
|
+
class LandingConfig
|
|
29
|
+
# A small uppercase kicker above the title (e.g. "Developer Docs"). nil omits it.
|
|
30
|
+
attr_accessor :eyebrow
|
|
31
|
+
|
|
32
|
+
# The hero <h1>. Wrap a run in **double asterisks** to render it in the primary
|
|
33
|
+
# color (e.g. "Jobs & events on **Postgres**"). nil falls back to the brand.
|
|
34
|
+
attr_accessor :title
|
|
35
|
+
|
|
36
|
+
# The muted lead paragraph under the title. nil falls back to the tagline.
|
|
37
|
+
attr_accessor :lead
|
|
38
|
+
|
|
39
|
+
# An optional install/quickstart code block shown in the hero, as a Hash:
|
|
40
|
+
# { code: "gem \"x\"", filename: "Gemfile", lexer: :ruby }
|
|
41
|
+
# nil omits the block. See #install_snippet for the normalized form.
|
|
42
|
+
attr_accessor :install
|
|
43
|
+
|
|
44
|
+
# Whether to render the registry-grouped documentation index below the hero
|
|
45
|
+
# (the "Documentation" section linking every authored page). Default true.
|
|
46
|
+
attr_writer :doc_index
|
|
47
|
+
|
|
48
|
+
# The hero call-to-action buttons, each a Hash normalized into a Cta:
|
|
49
|
+
# { label:, href:, style: :primary|:ghost (default :ghost), icon: (brand/lucide token) }
|
|
50
|
+
attr_writer :ctas
|
|
51
|
+
|
|
52
|
+
# The feature cards shown in a grid under the hero, each a Hash normalized into
|
|
53
|
+
# a Feature: { icon: (lucide name), title:, body: }.
|
|
54
|
+
attr_writer :features
|
|
55
|
+
|
|
56
|
+
def initialize
|
|
57
|
+
@doc_index = true
|
|
58
|
+
@ctas = []
|
|
59
|
+
@features = []
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Whether the doc index is shown (default true).
|
|
63
|
+
def doc_index? = @doc_index != false
|
|
64
|
+
|
|
65
|
+
# The CTAs as normalized Cta value objects (empty when unset).
|
|
66
|
+
def ctas
|
|
67
|
+
Array(@ctas).map { |cta| Cta.from(cta) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The features as normalized Feature value objects (empty when unset).
|
|
71
|
+
def features
|
|
72
|
+
Array(@features).map { |feature| Feature.from(feature) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The install block normalized to { code:, filename:, lexer: } with a sensible
|
|
76
|
+
# default lexer, or nil when unset.
|
|
77
|
+
def install_snippet
|
|
78
|
+
return if @install.nil?
|
|
79
|
+
|
|
80
|
+
attrs = @install.to_h.transform_keys(&:to_sym)
|
|
81
|
+
{ code: attrs[:code].to_s, filename: attrs[:filename], lexer: (attrs[:lexer] || :shell).to_sym }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# One hero call-to-action button. `style` maps to a daisyUI btn variant
|
|
85
|
+
# (:primary → btn-primary, anything else → btn-ghost). `icon` is an optional
|
|
86
|
+
# brand/lucide token rendered before the label (DocsUI::BrandMark resolves it).
|
|
87
|
+
Cta = Data.define(:label, :href, :style, :icon) do
|
|
88
|
+
def initialize(label:, href:, style: :ghost, icon: nil)
|
|
89
|
+
super(label:, href:, style: style&.to_sym, icon: icon)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.from(cta)
|
|
93
|
+
return cta if cta.is_a?(self)
|
|
94
|
+
|
|
95
|
+
attrs = cta.to_h.transform_keys(&:to_sym)
|
|
96
|
+
new(label: attrs[:label], href: attrs[:href], style: attrs[:style] || :ghost, icon: attrs[:icon])
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The daisyUI button class for this CTA's style.
|
|
100
|
+
def btn_class = style == :primary ? "btn btn-primary" : "btn btn-ghost"
|
|
101
|
+
|
|
102
|
+
# Whether the href points off-site (absolute http/https) — the component adds
|
|
103
|
+
# target=_blank + rel=noopener only for external links.
|
|
104
|
+
def external? = href.to_s.match?(%r{\Ahttps?://}i)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# One feature card: a lucide icon name, a title, and a short body.
|
|
108
|
+
Feature = Data.define(:icon, :title, :body) do
|
|
109
|
+
def initialize(title:, icon: nil, body: nil)
|
|
110
|
+
super
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def self.from(feature)
|
|
114
|
+
return feature if feature.is_a?(self)
|
|
115
|
+
|
|
116
|
+
attrs = feature.to_h.transform_keys(&:to_sym)
|
|
117
|
+
new(icon: attrs[:icon], title: attrs[:title], body: attrs[:body])
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/docs_kit/version.rb
CHANGED
data/lib/docs_kit.rb
CHANGED
|
@@ -57,6 +57,7 @@ loader.ignore(File.expand_path("docs_kit/configuration.rb", __dir__))
|
|
|
57
57
|
# Required eagerly by configuration.rb (a plain-Ruby value object, no Rails), so
|
|
58
58
|
# ignore it here too or zeitwerk double-manages the constant.
|
|
59
59
|
loader.ignore(File.expand_path("docs_kit/seo_config.rb", __dir__))
|
|
60
|
+
loader.ignore(File.expand_path("docs_kit/landing_config.rb", __dir__))
|
|
60
61
|
# Loaded ONLY by the host's docs_kit:og rake task (an explicit require), never at
|
|
61
62
|
# gem runtime — so its Rack/browser tooling is never pulled into a host that
|
|
62
63
|
# doesn't run the task. Ignore it so eager_load! doesn't require it.
|
|
@@ -104,6 +104,25 @@ Rails.application.config.to_prepare do
|
|
|
104
104
|
# then. README → "Add your docs to an agent (MCP)".
|
|
105
105
|
# c.mcp = false
|
|
106
106
|
|
|
107
|
+
# The landing page (app/views/landings/show.rb renders DocsUI::Landing) — a
|
|
108
|
+
# marketing hero + feature grid + a registry-grouped doc index, all from these
|
|
109
|
+
# knobs. Every field is optional; with none set it still renders a minimal hero
|
|
110
|
+
# (the brand + the doc index). Wrap a run in **double asterisks** to accent it
|
|
111
|
+
# in the primary color.
|
|
112
|
+
# c.landing.eyebrow = "Developer Docs"
|
|
113
|
+
# c.landing.title = "The <%= app_brand %> **API**"
|
|
114
|
+
# c.landing.lead = "One sentence on what your product does."
|
|
115
|
+
# c.landing.install = { code: 'gem "<%= app_brand.downcase %>"', filename: "Gemfile", lexer: :ruby }
|
|
116
|
+
# c.landing.ctas = [
|
|
117
|
+
# { label: "Get started", href: "/docs/overview", style: :primary },
|
|
118
|
+
# { label: "GitHub", href: "https://github.com/OWNER/REPO", style: :ghost, icon: :github },
|
|
119
|
+
# ]
|
|
120
|
+
# c.landing.features = [
|
|
121
|
+
# { icon: "zap", title: "Fast", body: "Why it's fast." },
|
|
122
|
+
# { icon: "database", title: "One database", body: "No extra moving parts." },
|
|
123
|
+
# ]
|
|
124
|
+
# c.landing.doc_index = false # hide the "Documentation" index section
|
|
125
|
+
|
|
107
126
|
# The sidebar nav derives from the registry — one heading → one registry.
|
|
108
127
|
# Each registry's authored pages become NavItems automatically (an unwritten
|
|
109
128
|
# page is skipped, so no dead links). For bespoke nav (interleaved
|
|
@@ -2,23 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
module Views
|
|
4
4
|
module Landings
|
|
5
|
-
# The home page
|
|
6
|
-
#
|
|
5
|
+
# The home page — a marketing hero + feature grid + doc index, rendered by the
|
|
6
|
+
# shared DocsUI::Landing component. Customize it entirely from config:
|
|
7
|
+
# `c.landing.{eyebrow, title, lead, install, ctas, features}` in
|
|
8
|
+
# config/initializers/docs_kit.rb. With no landing config it still renders a
|
|
9
|
+
# minimal hero (the brand + the doc index), so this works out of the box.
|
|
7
10
|
class Show < Phlex::HTML
|
|
8
|
-
include Phlex::Rails::Helpers::Routes
|
|
9
|
-
|
|
10
11
|
def view_template
|
|
11
|
-
render DocsUI::
|
|
12
|
-
div(class: "prose max-w-none") do
|
|
13
|
-
h1 { "<%= app_brand %>" }
|
|
14
|
-
p { "Documentation, built with docs-kit." }
|
|
15
|
-
ul do
|
|
16
|
-
Doc.all.select(&:view_class).each do |doc|
|
|
17
|
-
li { a(href: "/docs/#{doc.slug}", class: "link") { doc.title } }
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
12
|
+
render DocsUI::Landing.new
|
|
22
13
|
end
|
|
23
14
|
end
|
|
24
15
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: docs-kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -174,6 +174,7 @@ files:
|
|
|
174
174
|
- app/components/docs_ui/header.rb
|
|
175
175
|
- app/components/docs_ui/icon.rb
|
|
176
176
|
- app/components/docs_ui/json_response.rb
|
|
177
|
+
- app/components/docs_ui/landing.rb
|
|
177
178
|
- app/components/docs_ui/markdown.rb
|
|
178
179
|
- app/components/docs_ui/markdown_action.rb
|
|
179
180
|
- app/components/docs_ui/meta_tags.rb
|
|
@@ -207,6 +208,7 @@ files:
|
|
|
207
208
|
- lib/docs_kit/configuration.rb
|
|
208
209
|
- lib/docs_kit/controller.rb
|
|
209
210
|
- lib/docs_kit/engine.rb
|
|
211
|
+
- lib/docs_kit/landing_config.rb
|
|
210
212
|
- lib/docs_kit/llms_text.rb
|
|
211
213
|
- lib/docs_kit/markdown_export.rb
|
|
212
214
|
- lib/docs_kit/markdown_export/blocks.rb
|