docyard 0.9.0 → 1.0.1

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.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -1
  3. data/README.md +8 -253
  4. data/exe/docyard +6 -0
  5. data/lib/docyard/build/asset_bundler.rb +24 -2
  6. data/lib/docyard/build/error_page_generator.rb +33 -0
  7. data/lib/docyard/build/file_copier.rb +12 -5
  8. data/lib/docyard/build/file_writer.rb +19 -0
  9. data/lib/docyard/build/llms_txt_generator.rb +103 -0
  10. data/lib/docyard/build/root_fallback_generator.rb +66 -0
  11. data/lib/docyard/build/sitemap_generator.rb +1 -1
  12. data/lib/docyard/build/static_generator.rb +119 -81
  13. data/lib/docyard/builder.rb +6 -2
  14. data/lib/docyard/cli.rb +14 -4
  15. data/lib/docyard/components/processors/callout_processor.rb +1 -1
  16. data/lib/docyard/components/processors/code_block_extended_fence_postprocessor.rb +24 -0
  17. data/lib/docyard/components/processors/code_block_extended_fence_preprocessor.rb +44 -0
  18. data/lib/docyard/components/processors/code_block_options_preprocessor.rb +11 -1
  19. data/lib/docyard/components/processors/code_block_processor.rb +5 -24
  20. data/lib/docyard/components/processors/code_group_processor.rb +6 -22
  21. data/lib/docyard/components/processors/code_snippet_import_preprocessor.rb +1 -0
  22. data/lib/docyard/components/processors/file_tree_processor.rb +1 -2
  23. data/lib/docyard/components/processors/icon_processor.rb +8 -2
  24. data/lib/docyard/components/processors/include_processor.rb +10 -10
  25. data/lib/docyard/components/processors/video_embed_processor.rb +14 -3
  26. data/lib/docyard/components/support/code_block/feature_extractor.rb +3 -1
  27. data/lib/docyard/components/support/code_block/icon_detector.rb +5 -12
  28. data/lib/docyard/components/support/code_block/line_number_resolver.rb +30 -0
  29. data/lib/docyard/components/support/code_detector.rb +2 -12
  30. data/lib/docyard/components/support/code_group/html_builder.rb +2 -6
  31. data/lib/docyard/components/support/tabs/icon_detector.rb +6 -2
  32. data/lib/docyard/components/support/tabs/parser.rb +6 -23
  33. data/lib/docyard/config/analytics_resolver.rb +24 -0
  34. data/lib/docyard/config/branding_resolver.rb +58 -27
  35. data/lib/docyard/config/key_validator.rb +30 -0
  36. data/lib/docyard/config/logo_detector.rb +8 -8
  37. data/lib/docyard/config/schema.rb +39 -0
  38. data/lib/docyard/config/section.rb +21 -0
  39. data/lib/docyard/config/validation_helpers.rb +83 -0
  40. data/lib/docyard/config/validator.rb +45 -144
  41. data/lib/docyard/config/validators/navigation.rb +43 -0
  42. data/lib/docyard/config/validators/section.rb +114 -0
  43. data/lib/docyard/config.rb +46 -102
  44. data/lib/docyard/constants.rb +59 -0
  45. data/lib/docyard/{utils/errors.rb → errors.rb} +6 -0
  46. data/lib/docyard/initializer.rb +100 -49
  47. data/lib/docyard/navigation/breadcrumb_builder.rb +45 -6
  48. data/lib/docyard/navigation/page_navigation_builder.rb +65 -0
  49. data/lib/docyard/navigation/sidebar/auto_builder.rb +107 -0
  50. data/lib/docyard/navigation/sidebar/cache.rb +96 -0
  51. data/lib/docyard/navigation/sidebar/config_builder.rb +179 -0
  52. data/lib/docyard/navigation/sidebar/distributed_builder.rb +145 -0
  53. data/lib/docyard/navigation/sidebar/local_config_loader.rb +69 -3
  54. data/lib/docyard/navigation/sidebar/renderer.rb +12 -1
  55. data/lib/docyard/navigation/sidebar_builder.rb +43 -81
  56. data/lib/docyard/rendering/branding_variables.rb +65 -0
  57. data/lib/docyard/rendering/icon_helpers.rb +14 -1
  58. data/lib/docyard/rendering/icons/devicons.rb +63 -0
  59. data/lib/docyard/rendering/icons.rb +26 -27
  60. data/lib/docyard/rendering/markdown.rb +5 -23
  61. data/lib/docyard/rendering/og_helpers.rb +36 -0
  62. data/lib/docyard/rendering/renderer.rb +96 -61
  63. data/lib/docyard/rendering/template_resolver.rb +14 -0
  64. data/lib/docyard/routing/fallback_resolver.rb +3 -3
  65. data/lib/docyard/search/build_indexer.rb +2 -2
  66. data/lib/docyard/search/dev_indexer.rb +36 -28
  67. data/lib/docyard/search/pagefind_support.rb +1 -1
  68. data/lib/docyard/server/asset_handler.rb +39 -15
  69. data/lib/docyard/server/dev_server.rb +90 -55
  70. data/lib/docyard/server/file_watcher.rb +68 -18
  71. data/lib/docyard/server/pagefind_handler.rb +1 -1
  72. data/lib/docyard/server/preview_server.rb +29 -33
  73. data/lib/docyard/server/rack_application.rb +39 -71
  74. data/lib/docyard/server/router.rb +11 -7
  75. data/lib/docyard/server/sse_server.rb +157 -0
  76. data/lib/docyard/server/static_file_app.rb +42 -0
  77. data/lib/docyard/templates/assets/css/components/banner.css +31 -0
  78. data/lib/docyard/templates/assets/css/components/breadcrumbs.css +2 -1
  79. data/lib/docyard/templates/assets/css/components/callout.css +26 -6
  80. data/lib/docyard/templates/assets/css/components/code-block.css +4 -2
  81. data/lib/docyard/templates/assets/css/components/code-group.css +20 -7
  82. data/lib/docyard/templates/assets/css/components/feedback.css +126 -0
  83. data/lib/docyard/templates/assets/css/components/file-tree.css +5 -4
  84. data/lib/docyard/templates/assets/css/components/heading-anchor.css +2 -2
  85. data/lib/docyard/templates/assets/css/components/icon.css +5 -0
  86. data/lib/docyard/templates/assets/css/components/nav-menu.css +20 -4
  87. data/lib/docyard/templates/assets/css/components/navigation.css +25 -3
  88. data/lib/docyard/templates/assets/css/components/page-actions.css +131 -0
  89. data/lib/docyard/templates/assets/css/components/prev-next.css +14 -7
  90. data/lib/docyard/templates/assets/css/components/search.css +6 -10
  91. data/lib/docyard/templates/assets/css/components/tab-bar.css +9 -6
  92. data/lib/docyard/templates/assets/css/components/table-of-contents.css +63 -17
  93. data/lib/docyard/templates/assets/css/components/tabs.css +12 -4
  94. data/lib/docyard/templates/assets/css/components/theme-toggle.css +3 -1
  95. data/lib/docyard/templates/assets/css/landing.css +82 -13
  96. data/lib/docyard/templates/assets/css/layout.css +32 -16
  97. data/lib/docyard/templates/assets/css/markdown.css +22 -2
  98. data/lib/docyard/templates/assets/css/variables.css +14 -1
  99. data/lib/docyard/templates/assets/js/components/code-group.js +4 -1
  100. data/lib/docyard/templates/assets/js/components/copy-page.js +115 -0
  101. data/lib/docyard/templates/assets/js/components/feedback.js +66 -0
  102. data/lib/docyard/templates/assets/js/components/file-tree.js +5 -5
  103. data/lib/docyard/templates/assets/js/components/navigation.js +3 -3
  104. data/lib/docyard/templates/assets/js/components/search.js +3 -3
  105. data/lib/docyard/templates/assets/js/components/table-of-contents.js +12 -6
  106. data/lib/docyard/templates/assets/js/components/tabs.js +45 -22
  107. data/lib/docyard/templates/assets/js/components/tooltip.js +4 -4
  108. data/lib/docyard/templates/assets/js/hot-reload.js +44 -0
  109. data/lib/docyard/templates/errors/404.html.erb +125 -5
  110. data/lib/docyard/templates/errors/500.html.erb +184 -10
  111. data/lib/docyard/templates/errors/redirect.html.erb +12 -0
  112. data/lib/docyard/templates/init/_sidebar.yml +36 -0
  113. data/lib/docyard/templates/init/docyard.yml +36 -0
  114. data/lib/docyard/templates/init/pages/components.md +146 -0
  115. data/lib/docyard/templates/init/pages/getting-started.md +94 -0
  116. data/lib/docyard/templates/init/pages/index.md +22 -0
  117. data/lib/docyard/templates/layouts/default.html.erb +10 -0
  118. data/lib/docyard/templates/layouts/splash.html.erb +14 -1
  119. data/lib/docyard/templates/partials/_analytics.html.erb +24 -0
  120. data/lib/docyard/templates/partials/_banner.html.erb +1 -1
  121. data/lib/docyard/templates/partials/_code_block.html.erb +1 -1
  122. data/lib/docyard/templates/partials/_feedback.html.erb +14 -0
  123. data/lib/docyard/templates/partials/_footer.html.erb +1 -1
  124. data/lib/docyard/templates/partials/_head.html.erb +80 -5
  125. data/lib/docyard/templates/partials/_icon_library.html.erb +8 -0
  126. data/lib/docyard/templates/partials/_page_actions.html.erb +21 -0
  127. data/lib/docyard/templates/partials/_scripts.html.erb +6 -3
  128. data/lib/docyard/templates/partials/_tabs.html.erb +4 -1
  129. data/lib/docyard/utils/git_info.rb +157 -0
  130. data/lib/docyard/utils/hash_utils.rb +31 -0
  131. data/lib/docyard/utils/html_helpers.rb +8 -0
  132. data/lib/docyard/utils/logging.rb +44 -3
  133. data/lib/docyard/utils/path_resolver.rb +0 -10
  134. data/lib/docyard/utils/path_utils.rb +73 -0
  135. data/lib/docyard/version.rb +1 -1
  136. data/lib/docyard.rb +2 -2
  137. metadata +81 -47
  138. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -31
  139. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  140. data/.github/pull_request_template.md +0 -14
  141. data/.github/workflows/ci.yml +0 -49
  142. data/.rubocop.yml +0 -42
  143. data/CODE_OF_CONDUCT.md +0 -132
  144. data/CONTRIBUTING.md +0 -55
  145. data/LICENSE.vscode-icons +0 -42
  146. data/Rakefile +0 -8
  147. data/lib/docyard/config/constants.rb +0 -31
  148. data/lib/docyard/navigation/sidebar/children_discoverer.rb +0 -51
  149. data/lib/docyard/navigation/sidebar/config_parser.rb +0 -208
  150. data/lib/docyard/navigation/sidebar/file_resolver.rb +0 -90
  151. data/lib/docyard/navigation/sidebar/file_system_scanner.rb +0 -78
  152. data/lib/docyard/navigation/sidebar/metadata_extractor.rb +0 -71
  153. data/lib/docyard/navigation/sidebar/metadata_reader.rb +0 -51
  154. data/lib/docyard/navigation/sidebar/path_prefixer.rb +0 -34
  155. data/lib/docyard/navigation/sidebar/sorter.rb +0 -21
  156. data/lib/docyard/navigation/sidebar/title_extractor.rb +0 -25
  157. data/lib/docyard/navigation/sidebar/tree_builder.rb +0 -140
  158. data/lib/docyard/rendering/icons/LICENSE.phosphor +0 -21
  159. data/lib/docyard/rendering/icons/file_types.rb +0 -79
  160. data/lib/docyard/rendering/icons/phosphor.rb +0 -93
  161. data/lib/docyard/rendering/language_mapping.rb +0 -52
  162. data/lib/docyard/templates/assets/js/reload.js +0 -98
  163. data/lib/docyard/templates/partials/_icon.html.erb +0 -1
  164. data/lib/docyard/templates/partials/_icon_file_extension.html.erb +0 -1
  165. data/sig/docyard.rbs +0 -4
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "json"
5
+
6
+ module Docyard
7
+ class SSEServer
8
+ HEARTBEAT_INTERVAL = 15
9
+ DEFAULT_PORT = 4201
10
+
11
+ def initialize(port: DEFAULT_PORT)
12
+ @port = port
13
+ @connections = []
14
+ @mutex = Mutex.new
15
+ @running = false
16
+ @server = nil
17
+ @accept_thread = nil
18
+ @heartbeat_thread = nil
19
+ end
20
+
21
+ attr_reader :port
22
+
23
+ def start
24
+ @running = true
25
+ @server = TCPServer.new("127.0.0.1", @port)
26
+ @server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
27
+
28
+ start_accept_thread
29
+ start_heartbeat_thread
30
+ end
31
+
32
+ def stop
33
+ @running = false
34
+ close_server
35
+ @accept_thread&.kill
36
+ @heartbeat_thread&.kill
37
+ close_all_connections
38
+ end
39
+
40
+ def broadcast(event_type, data = {})
41
+ message = format_sse_message(event_type, data)
42
+ dead_connections = []
43
+
44
+ @mutex.synchronize do
45
+ @connections.each do |conn|
46
+ write_to_connection(conn, message) or dead_connections << conn
47
+ end
48
+
49
+ dead_connections.each { |conn| remove_connection_unsafe(conn) }
50
+ end
51
+ end
52
+
53
+ def connection_count
54
+ @mutex.synchronize { @connections.size }
55
+ end
56
+
57
+ private
58
+
59
+ def close_server
60
+ @server&.close
61
+ rescue StandardError
62
+ nil
63
+ end
64
+
65
+ def start_accept_thread
66
+ @accept_thread = Thread.new do
67
+ while @running
68
+ begin
69
+ client = @server.accept
70
+ Thread.new { handle_new_connection(client) }
71
+ rescue IOError, Errno::EBADF
72
+ break unless @running
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def handle_new_connection(client)
79
+ request = read_http_request(client)
80
+ return close_client(client) unless valid_sse_request?(request)
81
+
82
+ send_sse_headers(client)
83
+ @mutex.synchronize { @connections << client }
84
+ rescue StandardError
85
+ close_client(client)
86
+ end
87
+
88
+ def close_client(client)
89
+ client.close
90
+ rescue StandardError
91
+ nil
92
+ end
93
+
94
+ def read_http_request(client)
95
+ lines = []
96
+ while (line = client.gets)
97
+ break if line.strip.empty?
98
+
99
+ lines << line
100
+ end
101
+ lines.join
102
+ end
103
+
104
+ def valid_sse_request?(request)
105
+ request.include?("GET /_docyard/events") || request.include?("GET / ")
106
+ end
107
+
108
+ def send_sse_headers(client)
109
+ headers = [
110
+ "HTTP/1.1 200 OK",
111
+ "Content-Type: text/event-stream",
112
+ "Cache-Control: no-cache",
113
+ "Connection: keep-alive",
114
+ "Access-Control-Allow-Origin: *",
115
+ "",
116
+ ""
117
+ ].join("\r\n")
118
+
119
+ client.write(headers)
120
+ client.write("retry: 1000\n\n")
121
+ client.flush
122
+ end
123
+
124
+ def start_heartbeat_thread
125
+ @heartbeat_thread = Thread.new do
126
+ while @running
127
+ sleep HEARTBEAT_INTERVAL
128
+ broadcast("heartbeat", { time: Time.now.to_i }) if @running
129
+ end
130
+ end
131
+ end
132
+
133
+ def write_to_connection(conn, message)
134
+ conn.write_nonblock(message)
135
+ true
136
+ rescue IO::WaitWritable, IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ETIMEDOUT
137
+ false
138
+ end
139
+
140
+ def format_sse_message(event_type, data)
141
+ json_data = data.to_json
142
+ "event: #{event_type}\ndata: #{json_data}\n\n"
143
+ end
144
+
145
+ def remove_connection_unsafe(conn)
146
+ @connections.delete(conn)
147
+ close_client(conn)
148
+ end
149
+
150
+ def close_all_connections
151
+ @mutex.synchronize do
152
+ @connections.each { |conn| close_client(conn) }
153
+ @connections.clear
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/mime"
4
+
5
+ module Docyard
6
+ class StaticFileApp
7
+ def initialize(root)
8
+ @root = root
9
+ end
10
+
11
+ def call(env)
12
+ path = env["PATH_INFO"]
13
+ file_path = File.join(@root, path)
14
+
15
+ if path.end_with?("/") || File.directory?(file_path)
16
+ index_path = File.join(file_path, "index.html")
17
+ return serve_file(index_path) if File.file?(index_path)
18
+ elsif File.file?(file_path)
19
+ return serve_file(file_path)
20
+ end
21
+
22
+ serve_not_found
23
+ end
24
+
25
+ private
26
+
27
+ def serve_file(path)
28
+ content = File.read(path)
29
+ content_type = Rack::Mime.mime_type(File.extname(path), "application/octet-stream")
30
+ [200, { "content-type" => content_type }, [content]]
31
+ end
32
+
33
+ def serve_not_found
34
+ error_page = File.join(@root, "404.html")
35
+ if File.file?(error_page)
36
+ [404, { "content-type" => "text/html; charset=utf-8" }, [File.read(error_page)]]
37
+ else
38
+ [404, { "content-type" => "text/plain" }, ["Not Found"]]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -53,6 +53,10 @@ body.has-announcement .secondary-header {
53
53
  top: calc(var(--header-height) + var(--tab-bar-height) + var(--announcement-height));
54
54
  }
55
55
 
56
+ body.has-announcement:not(.has-tabs) .secondary-header {
57
+ top: calc(var(--header-height) + var(--announcement-height));
58
+ }
59
+
56
60
  body.has-announcement .layout {
57
61
  padding-top: calc(var(--header-height) + var(--tab-bar-height) + var(--announcement-height));
58
62
  }
@@ -70,6 +74,33 @@ body.has-announcement:not(.has-tabs) .layout {
70
74
  padding-top: calc(var(--header-height) + var(--announcement-height));
71
75
  }
72
76
 
77
+ @media (max-width: 1280px) and (min-width: 1025px) {
78
+ body.has-announcement .layout {
79
+ padding-top: calc(var(--header-height) + var(--tab-bar-height) + var(--announcement-height) + var(--secondary-header-height));
80
+ }
81
+
82
+ body.has-announcement:not(.has-tabs) .layout {
83
+ padding-top: calc(var(--header-height) + var(--announcement-height) + var(--secondary-header-height));
84
+ }
85
+ }
86
+
87
+ /* Mobile: tab bar is hidden, so don't include tab-bar-height */
88
+ @media (max-width: 1024px) {
89
+ body.has-announcement .secondary-header {
90
+ top: calc(var(--header-height) + var(--announcement-height));
91
+ }
92
+
93
+ body.has-announcement .layout {
94
+ padding-top: calc(var(--header-height) + var(--announcement-height) + var(--secondary-header-height));
95
+ }
96
+
97
+ body.has-announcement .sidebar {
98
+ top: 0;
99
+ height: 100vh;
100
+ height: 100dvh;
101
+ }
102
+ }
103
+
73
104
  .docyard-announcement__content {
74
105
  display: flex;
75
106
  align-items: center;
@@ -40,7 +40,8 @@
40
40
  box-shadow: 0 0 0 var(--ring-width) oklch(from var(--ring) l c h / 50%);
41
41
  }
42
42
 
43
- .breadcrumb-toggle .docyard-icon {
43
+ .breadcrumb-toggle .docyard-icon,
44
+ .breadcrumb-toggle i[class*="ph-"] {
44
45
  width: 18px;
45
46
  height: 18px;
46
47
  }
@@ -15,16 +15,17 @@
15
15
 
16
16
  .docyard-callout__icon {
17
17
  flex-shrink: 0;
18
- width: 1.25rem;
19
- height: 1.25rem;
18
+ width: 1.5rem;
19
+ height: 1.5rem;
20
20
  display: flex;
21
21
  align-items: center;
22
22
  justify-content: center;
23
23
  }
24
24
 
25
- .docyard-callout__icon .docyard-icon {
26
- width: 100%;
27
- height: 100%;
25
+ .docyard-callout__icon .docyard-icon,
26
+ .docyard-callout__icon i[class*="ph-"] {
27
+ font-size: 1.25rem;
28
+ display: inline-block;
28
29
  }
29
30
 
30
31
  .docyard-callout__icon svg {
@@ -98,11 +99,30 @@
98
99
  }
99
100
 
100
101
  .docyard-callout__body :not(pre)>code {
101
- background-color: oklch(from var(--input) l c h / 30%);
102
102
  padding: 0.125rem 0.375rem;
103
103
  border-radius: var(--radius-sm);
104
104
  }
105
105
 
106
+ .docyard-callout--note .docyard-callout__body :not(pre)>code {
107
+ background-color: oklch(from var(--callout-note-border) l c h / 15%);
108
+ }
109
+
110
+ .docyard-callout--tip .docyard-callout__body :not(pre)>code {
111
+ background-color: oklch(from var(--callout-tip-border) l c h / 15%);
112
+ }
113
+
114
+ .docyard-callout--important .docyard-callout__body :not(pre)>code {
115
+ background-color: oklch(from var(--callout-important-border) l c h / 15%);
116
+ }
117
+
118
+ .docyard-callout--warning .docyard-callout__body :not(pre)>code {
119
+ background-color: oklch(from var(--callout-warning-border) l c h / 15%);
120
+ }
121
+
122
+ .docyard-callout--danger .docyard-callout__body :not(pre)>code {
123
+ background-color: oklch(from var(--callout-danger-border) l c h / 15%);
124
+ }
125
+
106
126
  .docyard-callout--note {
107
127
  border-color: var(--callout-note-border);
108
128
  background-color: var(--callout-note-background);
@@ -57,7 +57,8 @@
57
57
  flex-shrink: 0;
58
58
  }
59
59
 
60
- .docyard-code-block__icon .docyard-icon {
60
+ .docyard-code-block__icon .docyard-icon,
61
+ .docyard-code-block__icon i[class*="ph-"] {
61
62
  width: 1rem;
62
63
  height: 1rem;
63
64
  display: inline-flex;
@@ -468,7 +469,8 @@
468
469
  gap: var(--spacing-2);
469
470
  }
470
471
 
471
- .docyard-code-block__icon .docyard-icon {
472
+ .docyard-code-block__icon .docyard-icon,
473
+ .docyard-code-block__icon i[class*="ph-"] {
472
474
  width: 0.875rem;
473
475
  height: 0.875rem;
474
476
  }
@@ -44,7 +44,7 @@
44
44
  .docyard-code-group__tab {
45
45
  display: inline-flex;
46
46
  align-items: center;
47
- justify-content: center;
47
+ justify-content: flex-start;
48
48
  gap: var(--spacing-1-5);
49
49
  padding: var(--spacing-2) var(--spacing-2-5);
50
50
  background: transparent;
@@ -60,9 +60,7 @@
60
60
  flex-shrink: 0;
61
61
  line-height: 1.5;
62
62
  outline: none;
63
- max-width: 12rem;
64
- overflow: hidden;
65
- text-overflow: ellipsis;
63
+ max-width: 14rem;
66
64
  }
67
65
 
68
66
  .docyard-code-group__tab:hover {
@@ -79,7 +77,8 @@
79
77
  border-radius: var(--radius-sm);
80
78
  }
81
79
 
82
- .docyard-code-group__tab .docyard-icon {
80
+ .docyard-code-group__tab .docyard-icon,
81
+ .docyard-code-group__tab i[class*="ph-"] {
83
82
  width: 1rem;
84
83
  height: 1rem;
85
84
  display: inline-flex;
@@ -91,6 +90,13 @@
91
90
  height: 100%;
92
91
  }
93
92
 
93
+ .docyard-code-group__tab-label {
94
+ overflow: hidden;
95
+ text-overflow: ellipsis;
96
+ direction: rtl;
97
+ text-align: left;
98
+ }
99
+
94
100
  .docyard-code-group__indicator {
95
101
  position: absolute;
96
102
  bottom: 0;
@@ -100,12 +106,18 @@
100
106
  border-radius: 1px;
101
107
  pointer-events: none;
102
108
  z-index: 1;
109
+ opacity: 0;
103
110
  transition:
104
111
  transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
105
- width 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
112
+ width 0.3s cubic-bezier(0.34, 1.56, 0.64, 1),
113
+ opacity 0.15s ease;
106
114
  will-change: transform, width;
107
115
  }
108
116
 
117
+ .docyard-code-group__indicator.is-ready {
118
+ opacity: 1;
119
+ }
120
+
109
121
  .docyard-code-group__tabs-scroll-container.can-scroll-left .docyard-code-group__tabs {
110
122
  mask-image: linear-gradient(to right, transparent, black 2rem);
111
123
  -webkit-mask-image: linear-gradient(to right, transparent, black 2rem);
@@ -252,7 +264,8 @@
252
264
  gap: var(--spacing-1);
253
265
  }
254
266
 
255
- .docyard-code-group__tab .docyard-icon {
267
+ .docyard-code-group__tab .docyard-icon,
268
+ .docyard-code-group__tab i[class*="ph-"] {
256
269
  width: 0.875rem;
257
270
  height: 0.875rem;
258
271
  }
@@ -0,0 +1,126 @@
1
+ .feedback {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ gap: var(--spacing-3);
6
+ margin-top: var(--spacing-10);
7
+ padding-top: var(--spacing-6);
8
+ }
9
+
10
+ .feedback__question {
11
+ margin: 0;
12
+ font-size: var(--text-sm);
13
+ font-weight: var(--font-medium);
14
+ color: var(--foreground);
15
+ }
16
+
17
+ .feedback__buttons {
18
+ display: flex;
19
+ gap: var(--spacing-2);
20
+ }
21
+
22
+ .feedback__btn {
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ width: 2.5rem;
27
+ height: 2.5rem;
28
+ padding: 0;
29
+ background-color: var(--background);
30
+ border: 1px solid var(--border);
31
+ border-radius: var(--radius-lg);
32
+ color: var(--muted-foreground);
33
+ cursor: pointer;
34
+ transition:
35
+ transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
36
+ background-color var(--transition-fast),
37
+ border-color var(--transition-fast),
38
+ color var(--transition-fast),
39
+ box-shadow 0.2s ease;
40
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
41
+ }
42
+
43
+ .feedback__btn:hover {
44
+ background-color: var(--background);
45
+ border-color: var(--foreground);
46
+ color: var(--foreground);
47
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
48
+ }
49
+
50
+ .feedback__btn:active {
51
+ transform: scale(0.92);
52
+ transition:
53
+ transform 0.1s cubic-bezier(0.4, 0, 1, 1),
54
+ background-color var(--transition-fast),
55
+ border-color var(--transition-fast),
56
+ color var(--transition-fast);
57
+ box-shadow: 0 0 1px rgba(0, 0, 0, 0.05);
58
+ }
59
+
60
+ .feedback__btn:focus-visible {
61
+ outline: 2px solid var(--primary);
62
+ outline-offset: 2px;
63
+ }
64
+
65
+ .feedback__btn i[class*="ph-"] {
66
+ font-size: 1.125rem;
67
+ transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
68
+ }
69
+
70
+ .feedback__btn.is-selected {
71
+ background-color: var(--primary);
72
+ border-color: var(--primary);
73
+ color: var(--primary-foreground);
74
+ pointer-events: none;
75
+ }
76
+
77
+ .feedback__btn.is-selected i[class*="ph-"] {
78
+ animation: feedbackPop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
79
+ }
80
+
81
+ .feedback__btn.is-not-selected {
82
+ opacity: 0.4;
83
+ pointer-events: none;
84
+ }
85
+
86
+ @keyframes feedbackPop {
87
+ 0% {
88
+ transform: scale(1);
89
+ }
90
+ 50% {
91
+ transform: scale(1.3);
92
+ }
93
+ 100% {
94
+ transform: scale(1);
95
+ }
96
+ }
97
+
98
+ .feedback__thanks {
99
+ margin: 0;
100
+ font-size: var(--text-sm);
101
+ color: var(--muted-foreground);
102
+ animation: feedbackFadeIn 0.3s ease;
103
+ }
104
+
105
+ .feedback__thanks[hidden] {
106
+ display: none;
107
+ }
108
+
109
+ @keyframes feedbackFadeIn {
110
+ from {
111
+ opacity: 0;
112
+ transform: translateY(-4px);
113
+ }
114
+ to {
115
+ opacity: 1;
116
+ transform: translateY(0);
117
+ }
118
+ }
119
+
120
+ .feedback.is-submitted .feedback__question {
121
+ display: none;
122
+ }
123
+
124
+ .feedback.is-submitted .feedback__buttons {
125
+ display: none;
126
+ }
@@ -51,7 +51,8 @@
51
51
  transition: transform 0.1s cubic-bezier(0.4, 0, 1, 1), background-color 0.1s ease;
52
52
  }
53
53
 
54
- .docyard-filetree__entry .docyard-icon {
54
+ .docyard-filetree__entry .docyard-icon,
55
+ .docyard-filetree__entry i[class*="ph-"] {
55
56
  flex-shrink: 0;
56
57
  display: inline-flex;
57
58
  align-items: center;
@@ -66,12 +67,12 @@
66
67
  height: 100%;
67
68
  }
68
69
 
69
- .docyard-filetree__entry .docyard-icon-folder-open,
70
- .docyard-filetree__entry .docyard-icon-folder {
70
+ .docyard-filetree__entry .ph-folder-open,
71
+ .docyard-filetree__entry .ph-folder {
71
72
  color: var(--muted-foreground);
72
73
  }
73
74
 
74
- .docyard-filetree__entry .docyard-icon-file-text {
75
+ .docyard-filetree__entry .ph-file-text {
75
76
  color: var(--muted-foreground);
76
77
  opacity: 0.6;
77
78
  }
@@ -12,7 +12,7 @@
12
12
  .has-tabs .content h4[id],
13
13
  .has-tabs .content h5[id],
14
14
  .has-tabs .content h6[id] {
15
- scroll-margin-top: calc(var(--header-height) + 3rem + var(--spacing-6));
15
+ scroll-margin-top: calc(var(--header-height) + var(--tab-bar-height) + var(--spacing-6));
16
16
  }
17
17
 
18
18
  @media (max-width: 1280px) and (min-width: 1025px) {
@@ -22,7 +22,7 @@
22
22
  .content h4[id],
23
23
  .content h5[id],
24
24
  .content h6[id] {
25
- scroll-margin-top: calc(var(--header-height) + 3rem + var(--spacing-6));
25
+ scroll-margin-top: calc(var(--header-height) + var(--secondary-header-height) + var(--spacing-6));
26
26
  }
27
27
  }
28
28
 
@@ -1,3 +1,8 @@
1
+ i[class*="ph-"] {
2
+ font-size: 1.15em;
3
+ vertical-align: -0.15em;
4
+ }
5
+
1
6
  .docyard-icon {
2
7
  display: inline-flex;
3
8
  align-items: center;
@@ -33,17 +33,23 @@
33
33
  background: var(--background);
34
34
  border-bottom: 1px solid var(--border);
35
35
  z-index: calc(var(--z-dropdown) + 1);
36
- transform: translateY(-8px);
36
+ transform: scale(0.98) translateY(-8px);
37
+ transform-origin: top center;
37
38
  opacity: 0;
38
39
  pointer-events: none;
39
- transition: transform 0.15s ease-out, opacity 0.15s ease-out;
40
+ transition:
41
+ transform 0.15s cubic-bezier(0.4, 0, 0.2, 1),
42
+ opacity 0.12s ease-out;
40
43
  box-shadow: var(--shadow-lg);
41
44
  }
42
45
 
43
46
  .nav-menu-dropdown.is-open {
44
- transform: translateY(0);
47
+ transform: scale(1) translateY(0);
45
48
  opacity: 1;
46
49
  pointer-events: auto;
50
+ transition:
51
+ transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),
52
+ opacity 0.15s ease-out;
47
53
  }
48
54
 
49
55
  .nav-menu-content {
@@ -96,7 +102,8 @@
96
102
  opacity: 0.7;
97
103
  }
98
104
 
99
- .nav-menu-item-icon .docyard-icon {
105
+ .nav-menu-item-icon .docyard-icon,
106
+ .nav-menu-item-icon i[class*="ph-"] {
100
107
  width: 18px;
101
108
  height: 18px;
102
109
  }
@@ -225,6 +232,15 @@
225
232
  }
226
233
  }
227
234
 
235
+ /* Adjust dropdown position when announcement banner is visible */
236
+ body.has-announcement .nav-menu-overlay {
237
+ top: calc(var(--header-height) + var(--announcement-height));
238
+ }
239
+
240
+ body.has-announcement .nav-menu-dropdown {
241
+ top: calc(var(--header-height) + var(--announcement-height));
242
+ }
243
+
228
244
  /* Reduced motion preference */
229
245
  @media (prefers-reduced-motion: reduce) {
230
246
  .nav-menu-overlay,
@@ -43,6 +43,10 @@
43
43
  height: 1rem;
44
44
  }
45
45
 
46
+ .nav-section-icon i[class*="ph-"] {
47
+ font-size: 1rem;
48
+ }
49
+
46
50
  .sidebar nav ul {
47
51
  list-style: none;
48
52
  padding: 0;
@@ -242,11 +246,19 @@
242
246
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
243
247
  }
244
248
 
245
- [data-nav-toggle][aria-expanded="true"] .nav-group-icon .docyard-icon {
249
+ .nav-group-icon i[class*="ph-"] {
250
+ display: inline-block;
251
+ font-size: 1rem;
252
+ transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
253
+ }
254
+
255
+ [data-nav-toggle][aria-expanded="true"] .nav-group-icon .docyard-icon,
256
+ [data-nav-toggle][aria-expanded="true"] .nav-group-icon i[class*="ph-"] {
246
257
  transform: rotate(90deg);
247
258
  }
248
259
 
249
- [data-nav-toggle][aria-expanded="false"] .nav-group-icon .docyard-icon {
260
+ [data-nav-toggle][aria-expanded="false"] .nav-group-icon .docyard-icon,
261
+ [data-nav-toggle][aria-expanded="false"] .nav-group-icon i[class*="ph-"] {
250
262
  transition: transform 0.2s cubic-bezier(0.4, 0, 1, 1);
251
263
  }
252
264
 
@@ -302,6 +314,10 @@
302
314
  height: 1rem;
303
315
  }
304
316
 
317
+ .nav-item-icon i[class*="ph-"] {
318
+ font-size: 1rem;
319
+ }
320
+
305
321
  .nav-item-text {
306
322
  flex: 1;
307
323
  min-width: 0;
@@ -322,17 +338,23 @@
322
338
  height: 0.75rem;
323
339
  }
324
340
 
341
+ .nav-item-external i[class*="ph-"] {
342
+ font-size: 0.75rem;
343
+ }
344
+
325
345
  @media (prefers-reduced-motion: reduce) {
326
346
 
327
347
  .sidebar nav a,
328
348
  .nav-group-header,
329
349
  .nav-group-icon .docyard-icon,
350
+ .nav-group-icon i[class*="ph-"],
330
351
  .nav-group-children,
331
352
  .mobile-menu-toggle {
332
353
  transition: none;
333
354
  }
334
355
 
335
- [data-nav-toggle][aria-expanded="true"] .nav-group-icon .docyard-icon {
356
+ [data-nav-toggle][aria-expanded="true"] .nav-group-icon .docyard-icon,
357
+ [data-nav-toggle][aria-expanded="true"] .nav-group-icon i[class*="ph-"] {
336
358
  transform: none;
337
359
  }
338
360
  }