markdowndocs 0.3.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a49d71e98d368188a3ed6a4437651f03880e691a8f520ee1067cd62c5fa4882
4
- data.tar.gz: 7b3646a743326288758bbfef6322b27a3e9ed75dd54c2c8edcec6f4671428f8c
3
+ metadata.gz: 1dea93a9abe255c4c491211a174eea97d253c4a817d7bd5811b21dab335e3052
4
+ data.tar.gz: 2ef4fd88793a8600f815a7bfe4826c3fb312ab4dda38771b3cc5b8163a33929c
5
5
  SHA512:
6
- metadata.gz: 2b7b057ab57104c261bdd5b6d3d5d06527450a2b68469d42af79f58652e9f3a752b4dbd69b4c386d3e323cfb6bb7f55a4279e9fe324e6b8d5bee211bdfc12f0b
7
- data.tar.gz: 0aa8ea43096b87a21396084f59648d8f23762af311e1459703c2074196d6d02cc69dfe950b84b2849e4aff513aacf80d6a40c2f38b98f03417248fa35be7d76b
6
+ metadata.gz: 65669141ca36724f5e9d70ac6ed57d252ee93c33087d0d602391131049c2e4d258733f398d0c9dfcadeaa5f4ffd2b8a27ed55e4e083472848b67c3a9b16f3c4b
7
+ data.tar.gz: 135b31877bd880d9716f9dd897a25c57c162dfe434be98914e24df9da12ee5b4dc5dce20c061c2e1dc87db0d38eafbbb57912d60a4b845ee0863db0995951e35
data/CHANGELOG.md CHANGED
@@ -5,6 +5,54 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.0] - 2026-05-05
9
+
10
+ ### Added
11
+
12
+ - Dark mode support across all docs templates. Every `bg-*`, `text-*`, and
13
+ `border-*` class now has a paired `dark:` variant chosen for WCAG 2.2 AAA
14
+ contrast (7:1) on dark surfaces. Host apps with `class="dark"` on `<html>`
15
+ (or `prefers-color-scheme: dark`) will see proper dark theming without
16
+ needing to override gem views.
17
+
18
+ ### Changed
19
+
20
+ - Bumped indigo link colors from `text-indigo-600` to `text-indigo-700` (light)
21
+ and `text-indigo-300` (dark) so links pass AAA contrast on both surfaces.
22
+ - Selected-state mode-switcher cyan text bumped from `text-cyan-700`/`-800` to
23
+ `text-cyan-900` (light) and `text-cyan-100` (dark) for AAA against the
24
+ selected card's `bg-cyan-50`/`bg-cyan-900/40` background.
25
+ - `prose-indigo` content area now also applies `dark:prose-invert` so the
26
+ rendered Markdown body inverts cleanly in dark mode.
27
+
28
+ ## [0.4.0] - 2026-03-20
29
+
30
+ ### Changed
31
+
32
+ - Engine now uses its own layout instead of inheriting the host app's layout, eliminating route helper conflicts caused by `isolate_namespace`. Host apps can override at `app/views/layouts/markdowndocs/application.html.erb` or configure via `config.layout`.
33
+ - Supports `content_for` blocks (`:docs_header`, `:docs_footer`, `:head`, `:title`) for customization.
34
+ - Removed `ensure_host_route_helpers` `before_action` — no longer needed with isolated layout.
35
+
36
+ ## [0.3.1] - 2026-03-20
37
+
38
+ ### Changed
39
+
40
+ - Fenced code block content is now indexed as a low-boost (0.5x) search field, making class names, methods, and config keys in examples discoverable.
41
+ - Visible cards are now reordered by MiniSearch relevance score within each category.
42
+ - Search debounce reduced from 150ms to 50ms for snappier results.
43
+
44
+ ## [0.3.0] - 2026-03-20
45
+
46
+ ### Added
47
+
48
+ - Keywords frontmatter support (`keywords: [login, signin, ...]`) indexed by full-text search with highest boost (4x), improving search relevance for docs with explicit keyword tags.
49
+
50
+ ## [0.2.3] - 2026-03-20
51
+
52
+ ### Fixed
53
+
54
+ - Host app route helpers (e.g., `about_path`) no longer resolve against the engine namespace. Replaced `method_missing` delegation with explicit `define_method` overrides built lazily on first request via `before_action`.
55
+
8
56
  ## [0.2.2] - 2026-03-18
9
57
 
10
58
  ### Changed
@@ -84,6 +132,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
84
132
  - i18n support for all UI strings
85
133
  - Install generator (`rails generate markdowndocs:install`)
86
134
 
135
+ [0.5.0]: https://github.com/dschmura/markdowndocs/releases/tag/v0.5.0
136
+ [0.4.0]: https://github.com/dschmura/markdowndocs/releases/tag/v0.4.0
137
+ [0.3.1]: https://github.com/dschmura/markdowndocs/releases/tag/v0.3.1
138
+ [0.3.0]: https://github.com/dschmura/markdowndocs/releases/tag/v0.3.0
139
+ [0.2.3]: https://github.com/dschmura/markdowndocs/releases/tag/v0.2.3
87
140
  [0.2.2]: https://github.com/dschmura/markdowndocs/releases/tag/v0.2.2
88
141
  [0.2.1]: https://github.com/dschmura/markdowndocs/releases/tag/v0.2.1
89
142
  [0.2.0]: https://github.com/dschmura/markdowndocs/releases/tag/v0.2.0
@@ -23,10 +23,10 @@ export default class extends Controller {
23
23
  const MiniSearch = (await this.loadMiniSearch()).default || (await this.loadMiniSearch())
24
24
 
25
25
  this.miniSearch = new MiniSearch({
26
- fields: ["title", "description", "content", "keywords"],
26
+ fields: ["title", "description", "content", "keywords", "code"],
27
27
  storeFields: ["title", "description"],
28
28
  searchOptions: {
29
- boost: { title: 3, description: 2, keywords: 4 },
29
+ boost: { title: 3, description: 2, keywords: 4, code: 0.5 },
30
30
  fuzzy: 0.2,
31
31
  prefix: true
32
32
  }
@@ -58,7 +58,7 @@ export default class extends Controller {
58
58
 
59
59
  search() {
60
60
  if (this.debounceTimer) clearTimeout(this.debounceTimer)
61
- this.debounceTimer = setTimeout(() => this.performSearch(), 150)
61
+ this.debounceTimer = setTimeout(() => this.performSearch(), 50)
62
62
  }
63
63
 
64
64
  performSearch() {
@@ -71,16 +71,21 @@ export default class extends Controller {
71
71
 
72
72
  const results = this.miniSearch.search(query)
73
73
  const matchingSlugs = new Set(results.map(r => r.id))
74
+ const scoreBySlug = new Map(results.map(r => [r.id, r.score]))
74
75
 
75
- this.cardTargets.forEach(card => {
76
- const slug = card.dataset.slug
77
- card.classList.toggle("hidden", !matchingSlugs.has(slug))
78
- })
79
-
76
+ // Reorder cards within each category by relevance score
80
77
  this.categoryTargets.forEach(section => {
81
- const cards = section.querySelectorAll("[data-docs-search-target='card']")
82
- const hasVisible = Array.from(cards).some(c => !c.classList.contains("hidden"))
83
- section.classList.toggle("hidden", !hasVisible)
78
+ const cards = Array.from(section.querySelectorAll("[data-docs-search-target='card']"))
79
+ cards.forEach(card => {
80
+ const slug = card.dataset.slug
81
+ card.classList.toggle("hidden", !matchingSlugs.has(slug))
82
+ })
83
+
84
+ const visibleCards = cards.filter(c => !c.classList.contains("hidden"))
85
+ visibleCards.sort((a, b) => (scoreBySlug.get(b.dataset.slug) || 0) - (scoreBySlug.get(a.dataset.slug) || 0))
86
+ visibleCards.forEach(card => card.parentElement.appendChild(card))
87
+
88
+ section.classList.toggle("hidden", visibleCards.length === 0)
84
89
  })
85
90
 
86
91
  const hasResults = matchingSlugs.size > 0
@@ -91,7 +96,13 @@ export default class extends Controller {
91
96
 
92
97
  showAll() {
93
98
  this.cardTargets.forEach(card => card.classList.remove("hidden"))
94
- this.categoryTargets.forEach(section => section.classList.remove("hidden"))
99
+ this.categoryTargets.forEach(section => {
100
+ section.classList.remove("hidden")
101
+ // Restore original order by slug
102
+ const cards = Array.from(section.querySelectorAll("[data-docs-search-target='card']"))
103
+ cards.sort((a, b) => a.dataset.slug.localeCompare(b.dataset.slug))
104
+ cards.forEach(card => card.parentElement.appendChild(card))
105
+ })
95
106
  if (this.hasNoResultsTarget) {
96
107
  this.noResultsTarget.classList.add("hidden")
97
108
  }
@@ -4,19 +4,20 @@ module Markdowndocs
4
4
  class ApplicationController < ::ApplicationController
5
5
  protect_from_forgery with: :exception
6
6
 
7
- # Fix route helper isolation caused by isolate_namespace.
7
+ # Use the engine's own layout to avoid route helper conflicts.
8
+ # Host apps that shared their application layout with the engine
9
+ # would see route helpers (about_path, signout_path, etc.) resolve
10
+ # against the engine's namespace instead of the main app.
8
11
  #
9
- # The problem: host app route helpers (about_path, contact_path, etc.)
10
- # exist in the view context but resolve against the engine's namespace,
11
- # so about_path returns "/docs/about" instead of "/about".
12
+ # Host apps can customize by overriding this layout at:
13
+ # app/views/layouts/markdowndocs/application.html.erb
12
14
  #
13
- # method_missing doesn't help because the methods already exist —
14
- # they just resolve wrong. We need to override them explicitly.
15
- #
16
- # This before_action builds a helper module on first request (when
17
- # routes are fully loaded) that defines every host app route helper
18
- # as a delegation to main_app.
19
- before_action :ensure_host_route_helpers
15
+ # The layout provides content_for blocks:
16
+ # :docs_header rendered above main content (for nav/header)
17
+ # :docs_footer — rendered below main content (for footer)
18
+ # :head — injected into <head> (for extra stylesheets/scripts)
19
+ # :title — page title (defaults to "Documentation")
20
+ layout -> { Markdowndocs.config.layout }
20
21
 
21
22
  # Support Rails 8 built-in authentication (allow_unauthenticated_access)
22
23
  # without requiring it — works with any auth system or none at all
@@ -26,21 +27,5 @@ module Markdowndocs
26
27
 
27
28
  # Resume session if the host app supports it (Rails 8 auth)
28
29
  before_action :resume_session, if: -> { respond_to?(:resume_session, true) }
29
-
30
- private
31
-
32
- def ensure_host_route_helpers
33
- return if self.class.instance_variable_get(:@host_routes_delegated)
34
-
35
- helper_module = Module.new do
36
- Rails.application.routes.named_routes.names.each do |name|
37
- define_method(:"#{name}_path") { |*args, **kwargs| main_app.send(:"#{name}_path", *args, **kwargs) }
38
- define_method(:"#{name}_url") { |*args, **kwargs| main_app.send(:"#{name}_url", *args, **kwargs) }
39
- end
40
- end
41
-
42
- self.class.helper(helper_module)
43
- self.class.instance_variable_set(:@host_routes_delegated, true)
44
- end
45
30
  end
46
31
  end
@@ -27,7 +27,8 @@ module Markdowndocs
27
27
  title: doc.title,
28
28
  description: doc.description,
29
29
  content: doc.plain_text_content,
30
- keywords: doc.keywords.join(" ")
30
+ keywords: doc.keywords.join(" "),
31
+ code: doc.code_content
31
32
  }
32
33
  end.to_json
33
34
  end
@@ -98,6 +98,13 @@ module Markdowndocs
98
98
  text.strip
99
99
  end
100
100
 
101
+ # Returns text extracted from fenced code blocks for search indexing.
102
+ def code_content
103
+ parsed = parse_frontmatter
104
+ blocks = parsed[:markdown].scan(/```\w*\n([\s\S]*?)```/)
105
+ blocks.flatten.join(" ").gsub(/\s+/, " ").strip
106
+ end
107
+
101
108
  private
102
109
 
103
110
  def derive_slug
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title><%= content_for(:title) || "Documentation" %></title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <%= csrf_meta_tags %>
7
+ <%= csp_meta_tag %>
8
+ <%= yield :head %>
9
+ <%= stylesheet_link_tag "application", "data-turbo-track": "reload" rescue nil %>
10
+ <%= javascript_importmap_tags rescue nil %>
11
+ </head>
12
+
13
+ <body class="min-h-screen flex flex-col bg-gray-50">
14
+ <%= yield :docs_header %>
15
+
16
+ <main id="main-content" role="main" class="flex-1">
17
+ <%= yield %>
18
+ </main>
19
+
20
+ <%= yield :docs_footer %>
21
+ </body>
22
+ </html>
@@ -7,21 +7,21 @@
7
7
  ]
8
8
  %>
9
9
  <nav aria-label="Breadcrumb" class="mb-6">
10
- <ol class="flex items-center space-x-2 text-sm text-gray-600">
10
+ <ol class="flex items-center space-x-2 text-sm text-gray-700 dark:text-slate-300">
11
11
  <% breadcrumb_items.each_with_index do |item, index| %>
12
12
  <li class="flex items-center">
13
13
  <% if index > 0 %>
14
- <svg class="w-4 h-4 mx-2 text-gray-700" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
14
+ <svg class="w-4 h-4 mx-2 text-gray-700 dark:text-slate-300" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
15
15
  <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
16
16
  </svg>
17
17
  <% end %>
18
18
 
19
19
  <% if item[:current] %>
20
- <span class="font-medium text-gray-900" aria-current="page"><%= item[:name] %></span>
20
+ <span class="font-medium text-gray-900 dark:text-slate-100" aria-current="page"><%= item[:name] %></span>
21
21
  <% elsif item[:path] %>
22
- <%= link_to item[:name], item[:path], class: "hover:text-indigo-600 transition-colors" %>
22
+ <%= link_to item[:name], item[:path], class: "hover:text-indigo-700 dark:hover:text-indigo-300 transition-colors" %>
23
23
  <% else %>
24
- <span class="text-gray-700"><%= item[:name] %></span>
24
+ <span class="text-gray-700 dark:text-slate-300"><%= item[:name] %></span>
25
25
  <% end %>
26
26
  </li>
27
27
  <% end %>
@@ -1,7 +1,7 @@
1
1
  <%# locals: (doc:) %>
2
- <div class="bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow duration-200 p-6 h-full flex flex-col">
3
- <h3 class="text-lg font-semibold text-gray-900 mb-2 hover:text-indigo-600 transition-colors">
2
+ <div class="bg-white dark:bg-slate-800 rounded-lg shadow-sm hover:shadow-md transition-shadow duration-200 p-6 h-full flex flex-col">
3
+ <h3 class="text-lg font-semibold text-gray-900 dark:text-slate-100 mb-2 hover:text-indigo-700 dark:hover:text-indigo-300 transition-colors">
4
4
  <%= link_to doc.title, markdowndocs.doc_path(doc.slug) %>
5
5
  </h3>
6
- <p class="text-gray-600 text-sm line-clamp-3"><%= doc.description %></p>
6
+ <p class="text-gray-700 dark:text-slate-300 text-sm line-clamp-3"><%= doc.description %></p>
7
7
  </div>
@@ -3,9 +3,11 @@
3
3
  id="docs-mode-switcher"
4
4
  class="
5
5
  bg-white
6
+ dark:bg-slate-800
6
7
  rounded-lg
7
8
  border
8
9
  border-slate-200
10
+ dark:border-slate-700
9
11
  p-4
10
12
  "
11
13
  data-controller="docs-mode"
@@ -15,6 +17,7 @@
15
17
  text-sm
16
18
  font-medium
17
19
  text-slate-700
20
+ dark:text-slate-300
18
21
  mb-3
19
22
  ">
20
23
  <%= t("markdowndocs.viewing_mode") %>
@@ -46,13 +49,13 @@
46
49
  border-2
47
50
  transition-all
48
51
  duration-150
49
- <%= (current_mode == mode) ? 'border-cyan-500 bg-cyan-50' : 'border-slate-200 hover:border-slate-300 hover:bg-slate-50' %>
52
+ <%= (current_mode == mode) ? 'border-cyan-500 bg-cyan-50 dark:bg-cyan-900/40 dark:border-cyan-400' : 'border-slate-200 hover:border-slate-300 hover:bg-slate-50 dark:border-slate-700 dark:hover:border-slate-500 dark:hover:bg-slate-700' %>
50
53
  "
51
54
  >
52
55
  <span class="
53
56
  block
54
57
  font-medium
55
- <%= (current_mode == mode) ? 'text-cyan-700' : 'text-slate-900' %>
58
+ <%= (current_mode == mode) ? 'text-cyan-900 dark:text-cyan-100' : 'text-slate-900 dark:text-slate-100' %>
56
59
  ">
57
60
  <%= t("markdowndocs.modes.#{mode}", default: mode.titleize) %>
58
61
  </span>
@@ -61,7 +64,7 @@
61
64
  <p class="
62
65
  text-xs
63
66
  mt-1
64
- <%= (current_mode == mode) ? 'text-cyan-800' : 'text-slate-600' %>
67
+ <%= (current_mode == mode) ? 'text-cyan-900 dark:text-cyan-100' : 'text-slate-700 dark:text-slate-300' %>
65
68
  ">
66
69
  <%= description %>
67
70
  </p>
@@ -7,13 +7,13 @@
7
7
  <% end %>
8
8
 
9
9
  <% if toc_items.length >= 3 %>
10
- <div class="bg-white rounded-lg shadow-sm p-6">
10
+ <div class="bg-white dark:bg-slate-800 rounded-lg shadow-sm p-6">
11
11
  <nav aria-label="Table of Contents">
12
- <h2 class="text-lg font-semibold text-gray-900 mb-4">On this page</h2>
12
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-slate-100 mb-4">On this page</h2>
13
13
  <ul class="space-y-2">
14
14
  <% toc_items.each do |item| %>
15
15
  <li class="<%= 'ml-4' if item[:level] == 3 %>">
16
- <a href="#<%= item[:slug] %>" class="text-sm text-gray-600 hover:text-indigo-600 transition-colors block py-1">
16
+ <a href="#<%= item[:slug] %>" class="text-sm text-gray-700 dark:text-slate-300 hover:text-indigo-700 dark:hover:text-indigo-300 transition-colors block py-1">
17
17
  <%= item[:text] %>
18
18
  </a>
19
19
  </li>
@@ -24,13 +24,13 @@
24
24
  <% end %>
25
25
 
26
26
  <% if related_docs.any? %>
27
- <div class="bg-white rounded-lg shadow-sm p-6 related-docs">
28
- <h2 class="text-lg font-semibold text-gray-900 mb-4">Related Documentation</h2>
27
+ <div class="bg-white dark:bg-slate-800 rounded-lg shadow-sm p-6 related-docs">
28
+ <h2 class="text-lg font-semibold text-gray-900 dark:text-slate-100 mb-4">Related Documentation</h2>
29
29
  <ul class="space-y-3">
30
30
  <% related_docs.each do |related_doc| %>
31
31
  <li>
32
- <%= link_to related_doc.title, markdowndocs.doc_path(related_doc.slug), class: "text-sm text-indigo-600 hover:text-indigo-700 font-medium transition-colors" %>
33
- <p class="text-xs text-gray-600 mt-1"><%= related_doc.description %></p>
32
+ <%= link_to related_doc.title, markdowndocs.doc_path(related_doc.slug), class: "text-sm text-indigo-700 dark:text-indigo-300 hover:text-indigo-800 dark:hover:text-indigo-200 font-medium transition-colors" %>
33
+ <p class="text-xs text-gray-700 dark:text-slate-300 mt-1"><%= related_doc.description %></p>
34
34
  </li>
35
35
  <% end %>
36
36
  </ul>
@@ -1,4 +1,4 @@
1
- <div class="min-h-screen bg-gray-50">
1
+ <div class="min-h-screen bg-gray-50 dark:bg-slate-900">
2
2
  <div class="max-w-7xl mx-auto px-4 py-12
3
3
  sm:px-6
4
4
  lg:px-8"
@@ -8,14 +8,14 @@
8
8
  <% end %>>
9
9
  <!-- Hero Section -->
10
10
  <div class="text-center mb-12">
11
- <h1 class="text-4xl font-bold text-gray-900 mb-4">Documentation</h1>
12
- <p class="text-xl text-gray-600">Browse our comprehensive documentation and guides</p>
11
+ <h1 class="text-4xl font-bold text-gray-900 dark:text-slate-100 mb-4">Documentation</h1>
12
+ <p class="text-xl text-gray-700 dark:text-slate-300">Browse our comprehensive documentation and guides</p>
13
13
 
14
14
  <% if @search_enabled %>
15
15
  <!-- Search input -->
16
16
  <div class="mt-6 max-w-lg mx-auto">
17
17
  <div class="relative">
18
- <svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
18
+ <svg class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-700 dark:text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
19
19
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
20
20
  </svg>
21
21
  <input
@@ -23,7 +23,7 @@
23
23
  data-docs-search-target="input"
24
24
  data-action="input->docs-search#search"
25
25
  placeholder="<%= t("markdowndocs.search_placeholder", default: "Search documentation...") %>"
26
- class="w-full pl-10 pr-4 py-3 rounded-lg border border-gray-300 shadow-sm text-gray-900 placeholder-gray-400 transition-colors
26
+ class="w-full pl-10 pr-4 py-3 rounded-lg border border-gray-300 dark:border-slate-700 shadow-sm bg-white dark:bg-slate-800 text-gray-900 dark:text-slate-100 placeholder-gray-700 dark:placeholder-slate-400 transition-colors
27
27
  focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
28
28
  autocomplete="off" />
29
29
  </div>
@@ -35,11 +35,11 @@
35
35
  <!-- No results message -->
36
36
  <% if @search_enabled %>
37
37
  <div data-docs-search-target="noResults" class="hidden text-center py-12">
38
- <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
38
+ <svg class="mx-auto h-12 w-12 text-gray-700 dark:text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
39
39
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
40
40
  </svg>
41
- <h3 class="mt-2 text-sm font-medium text-gray-900"><%= t("markdowndocs.no_search_results", default: "No matching documents") %></h3>
42
- <p class="mt-1 text-sm text-gray-600"><%= t("markdowndocs.try_different_search", default: "Try a different search term") %></p>
41
+ <h3 class="mt-2 text-sm font-medium text-gray-900 dark:text-slate-100"><%= t("markdowndocs.no_search_results", default: "No matching documents") %></h3>
42
+ <p class="mt-1 text-sm text-gray-700 dark:text-slate-300"><%= t("markdowndocs.try_different_search", default: "Try a different search term") %></p>
43
43
  </div>
44
44
  <% end %>
45
45
 
@@ -48,7 +48,7 @@
48
48
  <% if docs.any? %>
49
49
  <div id="<%= category.parameterize %>" class="mb-12 scroll-mt-6"
50
50
  <% if @search_enabled %>data-docs-search-target="category"<% end %>>
51
- <h2 class="text-2xl font-semibold text-gray-900 mb-6"><%= category %></h2>
51
+ <h2 class="text-2xl font-semibold text-gray-900 dark:text-slate-100 mb-6"><%= category %></h2>
52
52
  <div class="grid grid-cols-1 gap-6
53
53
  md:grid-cols-2
54
54
  lg:grid-cols-3">
@@ -63,15 +63,15 @@
63
63
  <% end %>
64
64
  <% else %>
65
65
  <!-- Empty State -->
66
- <div class="bg-white rounded-lg shadow-sm">
66
+ <div class="bg-white dark:bg-slate-800 rounded-lg shadow-sm">
67
67
  <div class="px-4 py-5
68
68
  sm:p-6">
69
69
  <div class="text-center py-12">
70
- <svg class="mx-auto h-12 w-12 text-gray-700" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
70
+ <svg class="mx-auto h-12 w-12 text-gray-700 dark:text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
71
71
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
72
72
  </svg>
73
- <h3 class="mt-2 text-sm font-medium text-gray-900">No documentation available</h3>
74
- <p class="mt-1 text-sm text-gray-700">Documentation will appear here once markdown files are added.</p>
73
+ <h3 class="mt-2 text-sm font-medium text-gray-900 dark:text-slate-100">No documentation available</h3>
74
+ <p class="mt-1 text-sm text-gray-700 dark:text-slate-300">Documentation will appear here once markdown files are added.</p>
75
75
  </div>
76
76
  </div>
77
77
  </div>
@@ -1,11 +1,11 @@
1
- <div class="min-h-screen bg-gray-50">
1
+ <div class="min-h-screen bg-gray-50 dark:bg-slate-900">
2
2
  <div class="max-w-7xl mx-auto px-4 py-12
3
3
  sm:px-6
4
4
  lg:px-8">
5
5
  <!-- Back link -->
6
6
  <div class="mb-6">
7
- <%= link_to markdowndocs.root_path, class: "inline-flex items-center text-indigo-600 transition-colors
8
- hover:text-indigo-700" do %>
7
+ <%= link_to markdowndocs.root_path, class: "inline-flex items-center text-indigo-700 transition-colors
8
+ hover:text-indigo-800 dark:text-indigo-300 dark:hover:text-indigo-200" do %>
9
9
  <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
10
10
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
11
11
  </svg>
@@ -21,8 +21,8 @@
21
21
  <button
22
22
  id="sidebar-toggle"
23
23
  type="button"
24
- class="w-full px-4 py-2 bg-white rounded-lg shadow-sm text-gray-700 transition-colors flex items-center justify-between
25
- hover:bg-gray-50"
24
+ class="w-full px-4 py-2 bg-white dark:bg-slate-800 rounded-lg shadow-sm text-gray-700 dark:text-slate-300 transition-colors flex items-center justify-between
25
+ hover:bg-gray-50 dark:hover:bg-slate-700"
26
26
  aria-label="<%= t("markdowndocs.navigation_sidebar") %>"
27
27
  aria-expanded="false"
28
28
  aria-controls="mobile-sidebar"
@@ -66,8 +66,8 @@
66
66
  lg:grid-cols-12">
67
67
  <!-- Main content -->
68
68
  <main class="lg:col-span-8">
69
- <article class="bg-white rounded-lg shadow-sm p-8">
70
- <div class="prose prose-indigo max-w-none">
69
+ <article class="bg-white dark:bg-slate-800 rounded-lg shadow-sm p-8">
70
+ <div class="prose prose-indigo dark:prose-invert max-w-none">
71
71
  <%= raw @rendered_content %>
72
72
  </div>
73
73
  </article>
@@ -4,7 +4,8 @@ module Markdowndocs
4
4
  class Configuration
5
5
  attr_accessor :docs_path, :categories, :modes, :default_mode,
6
6
  :markdown_options, :rouge_theme, :cache_expiry,
7
- :user_mode_resolver, :user_mode_saver, :search_enabled
7
+ :user_mode_resolver, :user_mode_saver, :search_enabled,
8
+ :layout
8
9
 
9
10
  def initialize
10
11
  @docs_path = nil # Resolved lazily so Rails.root is available
@@ -17,6 +18,7 @@ module Markdowndocs
17
18
  @user_mode_resolver = nil
18
19
  @user_mode_saver = nil
19
20
  @search_enabled = false
21
+ @layout = "markdowndocs/application"
20
22
  end
21
23
 
22
24
  # Lazily resolve docs_path so Rails.root is available
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Markdowndocs
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdowndocs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Chmura
@@ -87,6 +87,7 @@ files:
87
87
  - app/helpers/markdowndocs/docs_helper.rb
88
88
  - app/models/markdowndocs/documentation.rb
89
89
  - app/services/markdowndocs/markdown_renderer.rb
90
+ - app/views/layouts/markdowndocs/application.html.erb
90
91
  - app/views/markdowndocs/docs/_breadcrumb.html.erb
91
92
  - app/views/markdowndocs/docs/_card.html.erb
92
93
  - app/views/markdowndocs/docs/_mode_switcher.html.erb