docs-kit 1.0.4 → 1.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b0df82a287c454b9eff2243210da5dca51eb8f218434ede180e1ffdb3be7c941
4
- data.tar.gz: 215324e7604b6dacf964e736b6d29f38b30b4b047541ca5042dc1b1879132cae
3
+ metadata.gz: 51075e7a45365a86a3f97c35341cd2288ad35aba3f9ed1f4e31cea17edcacbfc
4
+ data.tar.gz: b5f49f3397f6b5d9090ee4560d82242a063a2cf8d0a7682c7a2fe7154364541f
5
5
  SHA512:
6
- metadata.gz: 8fd44f0619a0f80d84ece5aff198252e4dd6bd74353d675ada893f585a8a335d456269a4ddcbe36a28c219687891f2058cfb7092748572dfb24ad7183a724931
7
- data.tar.gz: d2229edb327e97156d7e6ebdc593bbcdb52e6a68a6c92734697ca2e5898729cf01b6f192c33be03c1b27f4339ecdae31521c8de8c7c145cc4f6d89c01bc9e037
6
+ metadata.gz: 6b725ba25e7919148f89c6511300a0998e2d6d9d9539d84fa4c845cc8b933a24ba08c79131de936872000c4b56d787e8eeab17f9e46e8fbbe1a03a8b9d6eabf5
7
+ data.tar.gz: 11c6600410734838f11aa53957ccfeb89808996511e4b377e41b23e2d6f6afc23cee5cf161a44e12bb61c48e194843b09ed4550d4c341403b2806804f05bdf1e
data/CHANGELOG.md CHANGED
@@ -18,6 +18,26 @@
18
18
 
19
19
  ### Added
20
20
 
21
+ - **`DocsUI::Landing` hero logo (`c.landing.logo`).** The landing hero now takes
22
+ an optional brand mark above the eyebrow, in two forms: an inline single-path
23
+ SVG (`{ svg: "<path d>", viewbox:, label: }`, rendered with `fill: currentColor`
24
+ so it adapts to the theme) or an image (`{ src:, alt: }`, resolved through the
25
+ site's asset pipeline). nil (the default) omits it, so it's backwards-compatible.
26
+ This closes the gap a mounted docs app hit — a hero with no way to show the
27
+ product's own logo — the first consumer to try `DocsUI::Landing` on a real site.
28
+ - **`DocsUI::Landing` — a config-driven marketing landing page.** Every consuming
29
+ site (and this dogfood site) was hand-rolling a home page; now render
30
+ `DocsUI::Landing` and drive it from a new `c.landing` config block
31
+ (`DocsKit::LandingConfig`): a hero (`eyebrow`, `title` — wrap a run in
32
+ `**double asterisks**` to accent it in the primary color, `lead`, an optional
33
+ `install` code snippet, and `ctas`), a `features` card grid, and a
34
+ registry-grouped documentation index built from `nav_groups` (so it never drifts
35
+ from the authored pages). Every field is optional — with an empty `c.landing` it
36
+ still renders a minimal hero (brand + doc index), never a broken page — and its
37
+ `.md`/`.text` twin works like any page. The install generator's `landings#show`
38
+ now renders it and the initializer documents `c.landing`. This is the first
39
+ landing pattern proven on a **mounted** docs app (a docs section inside a larger
40
+ Rails app whose `/` is already taken), contributed back from that use case.
21
41
  - **SEO + social sharing.** Every page now emits a complete SEO `<head>` —
22
42
  meta description, Open Graph, Twitter Card, canonical, favicon, robots, and
23
43
  theme-color — via the new `DocsUI::MetaTags` component, driven entirely by a
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DocsUI
4
+ # The marketing landing page — a hero (an optional brand logo + eyebrow + title +
5
+ # lead + optional install snippet + CTA buttons), a feature-card grid, and a
6
+ # registry-grouped documentation index — rendered inside DocsUI::Shell. Every
7
+ # consuming site was hand-rolling this; drive it from config instead:
8
+ #
9
+ # # config/initializers/docs_kit.rb
10
+ # DocsKit.configure do |c|
11
+ # c.landing.logo = { svg: "M4 2h9l5 5…Z", viewbox: "0 0 22 24", label: "Acme" }
12
+ # c.landing.eyebrow = "Developer Docs"
13
+ # c.landing.title = "Jobs & events on **Postgres**" # ** ** → primary color
14
+ # c.landing.lead = "PostgreSQL-native jobs + event bus for Rails."
15
+ # c.landing.install = { code: 'gem "pgbus"', filename: "Gemfile", lexer: :ruby }
16
+ # c.landing.ctas = [{ label: "Get started", href: "/docs/overview", style: :primary }]
17
+ # c.landing.features = [{ icon: "database", title: "One database", body: "No Redis." }]
18
+ # end
19
+ #
20
+ # # a controller that includes DocsKit::Controller
21
+ # def show = render_page(DocsUI::Landing.new)
22
+ #
23
+ # Everything is optional: with an empty c.landing it still renders a minimal hero
24
+ # (the brand name + the doc index), never a broken page. The doc index is built
25
+ # from DocsKit.configuration.nav_groups — the same registry the sidebar uses — so
26
+ # it never drifts from the authored pages.
27
+ #
28
+ # It IS a full document (composes Shell), so a controller renders it with
29
+ # `layout: false`, exactly like DocsUI::Page (DocsKit::Controller#render_page
30
+ # does this). The `.md`/`.text` twin of the landing works too — MarkdownExport
31
+ # walks the same #docs-content region Shell stamps.
32
+ class Landing < Phlex::HTML
33
+ include Phlex::Rails::Helpers::Request
34
+ # For the image-form hero logo (c.landing.logo = { src: … }): resolve the asset
35
+ # path through the site's pipeline to its digested /assets URL.
36
+ include Phlex::Rails::Helpers::ImageURL
37
+
38
+ def view_template
39
+ render DocsUI::Shell.new(title: landing.eyebrow || config.brand) do
40
+ div(class: "mx-auto max-w-5xl") do
41
+ hero
42
+ feature_grid
43
+ doc_index
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def config = DocsKit.configuration
51
+ def landing = config.landing
52
+
53
+ # --- hero ----------------------------------------------------------------
54
+
55
+ def hero
56
+ div(class: "flex flex-col gap-6") do
57
+ logo
58
+ eyebrow
59
+ heading
60
+ lead
61
+ install_snippet
62
+ ctas
63
+ end
64
+ end
65
+
66
+ # The brand mark — an inline single-path SVG (currentColor, theme-adaptive) or
67
+ # an <img>. Rendered above the eyebrow, like a product wordmark.
68
+ def logo
69
+ return unless (mark = landing.hero_logo)
70
+
71
+ if mark.inline?
72
+ svg(viewbox: mark.viewbox, class: "h-9 w-auto text-primary", fill: "currentColor",
73
+ role: "img", aria_label: mark.label) do |s|
74
+ s.title { mark.label } if mark.label
75
+ s.path(d: mark.svg)
76
+ end
77
+ else
78
+ img(src: image_url(mark.src), alt: mark.alt.to_s, class: "h-9 w-auto")
79
+ end
80
+ end
81
+
82
+ def eyebrow
83
+ return unless (text = landing.eyebrow)
84
+
85
+ p(class: "text-sm font-medium uppercase tracking-wide text-primary") { text }
86
+ end
87
+
88
+ # The <h1>. A **run** wrapped in double asterisks renders in the primary color
89
+ # (the one bit of markdown we honor, so a site can accent a word without HTML).
90
+ def heading
91
+ h1(class: "text-4xl font-bold tracking-tight md:text-5xl") do
92
+ (landing.title || config.brand).to_s.split(/\*\*(.+?)\*\*/).each_with_index do |part, index|
93
+ next if part.empty?
94
+
95
+ index.odd? ? span(class: "text-primary") { part } : plain(part)
96
+ end
97
+ end
98
+ end
99
+
100
+ def lead
101
+ return unless (text = landing.lead || config.tagline)
102
+
103
+ p(class: "max-w-2xl text-lg text-base-content/70") { text }
104
+ end
105
+
106
+ def install_snippet
107
+ return unless (snippet = landing.install_snippet)
108
+
109
+ render DocsUI::Code.new(snippet[:code], lexer: snippet[:lexer], filename: snippet[:filename])
110
+ end
111
+
112
+ def ctas
113
+ buttons = landing.ctas
114
+ return if buttons.empty?
115
+
116
+ div(class: "flex flex-wrap items-center gap-4 pt-2") do
117
+ buttons.each { |cta| cta_button(cta) }
118
+ end
119
+ end
120
+
121
+ def cta_button(cta)
122
+ attrs = { href: cta.href, class: "#{cta.btn_class} gap-2" }
123
+ if cta.external?
124
+ attrs[:target] = "_blank"
125
+ attrs[:rel] = "noopener"
126
+ end
127
+ a(**attrs) do
128
+ render DocsUI::BrandMark.new(cta.icon, class: "size-4", label: cta.label) if cta.icon
129
+ plain cta.label
130
+ end
131
+ end
132
+
133
+ # --- feature grid --------------------------------------------------------
134
+
135
+ def feature_grid
136
+ features = landing.features
137
+ return if features.empty?
138
+
139
+ div(class: "mt-12 grid gap-4 sm:grid-cols-2") do
140
+ features.each { |feature| feature_card(feature) }
141
+ end
142
+ end
143
+
144
+ def feature_card(feature)
145
+ div(class: "rounded-box border border-base-300 bg-base-200/40 p-5") do
146
+ div(class: "flex items-center gap-2 text-primary") do
147
+ render DocsUI::Icon.new(feature.icon, class: "size-5") if feature.icon
148
+ span(class: "font-semibold text-base-content") { feature.title }
149
+ end
150
+ p(class: "mt-2 text-sm text-base-content/70") { feature.body } if feature.body
151
+ end
152
+ end
153
+
154
+ # --- documentation index -------------------------------------------------
155
+
156
+ # The registry-grouped page index. nav_groups is the three-level Hash the
157
+ # sidebar renders ({ heading => { subgroup => [NavItem] } }); the landing
158
+ # flattens each heading's items into a linked column.
159
+ def doc_index
160
+ return if !landing.doc_index? || (groups = flattened_nav).empty?
161
+
162
+ div(class: "mt-16") do
163
+ h2(class: "text-sm font-semibold uppercase tracking-wide text-base-content/50") { "Documentation" }
164
+ div(class: "mt-6 grid gap-8 sm:grid-cols-2") do
165
+ groups.each { |heading, items| doc_index_group(heading, items) }
166
+ end
167
+ end
168
+ end
169
+
170
+ def doc_index_group(heading, items)
171
+ div do
172
+ h3(class: "text-xs font-semibold uppercase tracking-wide text-base-content/40") { heading }
173
+ ul(class: "mt-3 flex flex-col gap-2") do
174
+ items.each { |item| li { a(href: item.href, class: "link link-hover text-sm") { item.label } } }
175
+ end
176
+ end
177
+ end
178
+
179
+ # Collapse nav_groups ({ heading => { subgroup => [item] } }) to
180
+ # { heading => [item, ...] } — the landing shows one flat column per heading.
181
+ def flattened_nav
182
+ config.nav_groups.each_with_object({}) do |(heading, grouped), acc|
183
+ items = Array(grouped).flat_map { |_subgroup, list| Array(list) }
184
+ acc[heading] = items unless items.empty?
185
+ end
186
+ end
187
+ end
188
+ 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,160 @@
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
+ # An optional brand logo/mark rendered at the top of the hero (above the
30
+ # eyebrow), like a product wordmark. A Hash in one of two forms:
31
+ # { svg: "<path d …>", viewbox: "0 0 81 45", label: "Brand" } # inline mark
32
+ # { src: "logo.svg", alt: "Brand" } # image asset/URL
33
+ # The inline `svg` form renders with `fill: currentColor` so it adapts to the
34
+ # theme (light/dark) — best for a single-color mark. nil omits the logo.
35
+ # See #hero_logo (the normalized Logo value object) and DocsUI::Landing.
36
+ attr_writer :logo
37
+
38
+ # A small uppercase kicker above the title (e.g. "Developer Docs"). nil omits it.
39
+ attr_accessor :eyebrow
40
+
41
+ # The hero <h1>. Wrap a run in **double asterisks** to render it in the primary
42
+ # color (e.g. "Jobs & events on **Postgres**"). nil falls back to the brand.
43
+ attr_accessor :title
44
+
45
+ # The muted lead paragraph under the title. nil falls back to the tagline.
46
+ attr_accessor :lead
47
+
48
+ # An optional install/quickstart code block shown in the hero, as a Hash:
49
+ # { code: "gem \"x\"", filename: "Gemfile", lexer: :ruby }
50
+ # nil omits the block. See #install_snippet for the normalized form.
51
+ attr_accessor :install
52
+
53
+ # Whether to render the registry-grouped documentation index below the hero
54
+ # (the "Documentation" section linking every authored page). Default true.
55
+ attr_writer :doc_index
56
+
57
+ # The hero call-to-action buttons, each a Hash normalized into a Cta:
58
+ # { label:, href:, style: :primary|:ghost (default :ghost), icon: (brand/lucide token) }
59
+ attr_writer :ctas
60
+
61
+ # The feature cards shown in a grid under the hero, each a Hash normalized into
62
+ # a Feature: { icon: (lucide name), title:, body: }.
63
+ attr_writer :features
64
+
65
+ def initialize
66
+ @doc_index = true
67
+ @ctas = []
68
+ @features = []
69
+ end
70
+
71
+ # Whether the doc index is shown (default true).
72
+ def doc_index? = @doc_index != false
73
+
74
+ # The CTAs as normalized Cta value objects (empty when unset).
75
+ def ctas
76
+ Array(@ctas).map { |cta| Cta.from(cta) }
77
+ end
78
+
79
+ # The features as normalized Feature value objects (empty when unset).
80
+ def features
81
+ Array(@features).map { |feature| Feature.from(feature) }
82
+ end
83
+
84
+ # The install block normalized to { code:, filename:, lexer: } with a sensible
85
+ # default lexer, or nil when unset.
86
+ def install_snippet
87
+ return if @install.nil?
88
+
89
+ attrs = @install.to_h.transform_keys(&:to_sym)
90
+ { code: attrs[:code].to_s, filename: attrs[:filename], lexer: (attrs[:lexer] || :shell).to_sym }
91
+ end
92
+
93
+ # The hero logo as a normalized Logo value object, or nil when unset.
94
+ def hero_logo
95
+ return if @logo.nil?
96
+
97
+ Logo.from(@logo)
98
+ end
99
+
100
+ # One hero call-to-action button. `style` maps to a daisyUI btn variant
101
+ # (:primary → btn-primary, anything else → btn-ghost). `icon` is an optional
102
+ # brand/lucide token rendered before the label (DocsUI::BrandMark resolves it).
103
+ Cta = Data.define(:label, :href, :style, :icon) do
104
+ def initialize(label:, href:, style: :ghost, icon: nil)
105
+ super(label:, href:, style: style&.to_sym, icon: icon)
106
+ end
107
+
108
+ def self.from(cta)
109
+ return cta if cta.is_a?(self)
110
+
111
+ attrs = cta.to_h.transform_keys(&:to_sym)
112
+ new(label: attrs[:label], href: attrs[:href], style: attrs[:style] || :ghost, icon: attrs[:icon])
113
+ end
114
+
115
+ # The daisyUI button class for this CTA's style.
116
+ def btn_class = style == :primary ? "btn btn-primary" : "btn btn-ghost"
117
+
118
+ # Whether the href points off-site (absolute http/https) — the component adds
119
+ # target=_blank + rel=noopener only for external links.
120
+ def external? = href.to_s.match?(%r{\Ahttps?://}i)
121
+ end
122
+
123
+ # One feature card: a lucide icon name, a title, and a short body.
124
+ Feature = Data.define(:icon, :title, :body) do
125
+ def initialize(title:, icon: nil, body: nil)
126
+ super
127
+ end
128
+
129
+ def self.from(feature)
130
+ return feature if feature.is_a?(self)
131
+
132
+ attrs = feature.to_h.transform_keys(&:to_sym)
133
+ new(icon: attrs[:icon], title: attrs[:title], body: attrs[:body])
134
+ end
135
+ end
136
+
137
+ # The hero brand logo — either an inline single-path SVG mark (`svg` = the
138
+ # `<path d>` data, `viewbox` = its viewBox) rendered with fill: currentColor so
139
+ # it adapts to the theme, OR an image (`src` = an asset path/URL, `alt` = its
140
+ # accessible name). `label` is the accessible name for the inline mark.
141
+ Logo = Data.define(:svg, :viewbox, :src, :alt, :label) do
142
+ def initialize(svg: nil, viewbox: "0 0 24 24", src: nil, alt: nil, label: nil)
143
+ super
144
+ end
145
+
146
+ def self.from(logo)
147
+ return logo if logo.is_a?(self)
148
+
149
+ attrs = logo.to_h.transform_keys(&:to_sym)
150
+ new(
151
+ svg: attrs[:svg], viewbox: attrs[:viewbox] || "0 0 24 24",
152
+ src: attrs[:src], alt: attrs[:alt], label: attrs[:label]
153
+ )
154
+ end
155
+
156
+ # An inline SVG mark (vs. an <img>). True when `svg` path data is present.
157
+ def inline? = !svg.to_s.empty?
158
+ end
159
+ end
160
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DocsKit
4
- VERSION = "1.0.4"
4
+ VERSION = "1.0.6"
5
5
  end
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,28 @@ 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.logo = { svg: "M4 2h9l5 5…Z", viewbox: "0 0 22 24", label: "<%= app_brand %>" }
113
+ # # ^ an inline single-path SVG mark (fill: currentColor, theme-adaptive), OR
114
+ # # an image: { src: "logo.svg", alt: "<%= app_brand %>" }
115
+ # c.landing.eyebrow = "Developer Docs"
116
+ # c.landing.title = "The <%= app_brand %> **API**"
117
+ # c.landing.lead = "One sentence on what your product does."
118
+ # c.landing.install = { code: 'gem "<%= app_brand.downcase %>"', filename: "Gemfile", lexer: :ruby }
119
+ # c.landing.ctas = [
120
+ # { label: "Get started", href: "/docs/overview", style: :primary },
121
+ # { label: "GitHub", href: "https://github.com/OWNER/REPO", style: :ghost, icon: :github },
122
+ # ]
123
+ # c.landing.features = [
124
+ # { icon: "zap", title: "Fast", body: "Why it's fast." },
125
+ # { icon: "database", title: "One database", body: "No extra moving parts." },
126
+ # ]
127
+ # c.landing.doc_index = false # hide the "Documentation" index section
128
+
107
129
  # The sidebar nav derives from the registry — one heading → one registry.
108
130
  # Each registry's authored pages become NavItems automatically (an unwritten
109
131
  # 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. Renders inside DocsUI::Shell (the full document + drawer
6
- # shell); links to the authored docs.
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::Shell.new do
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
4
+ version: 1.0.6
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