daisyui 1.0.5 → 1.1.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.
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module DaisyUI
6
+ # MCP (Model Context Protocol) server for DaisyUI components.
7
+ # Provides component information to AI assistants via JSON-RPC over stdio.
8
+ class McpServer
9
+ PROTOCOL_VERSION = "2024-11-05"
10
+ SERVER_NAME = "daisyui"
11
+ SERVER_VERSION = DaisyUI::VERSION
12
+
13
+ TOOLS = [
14
+ {
15
+ name: "list_components",
16
+ description: "List all available DaisyUI Ruby/Phlex components with their CSS classes and modifiers",
17
+ inputSchema: { type: "object", properties: {}, required: [] }
18
+ },
19
+ {
20
+ name: "get_component",
21
+ description: "Get detailed information about a specific DaisyUI component including all modifiers and usage examples",
22
+ inputSchema: {
23
+ type: "object",
24
+ properties: {
25
+ component: { type: "string", description: "Component name (e.g., 'Button', 'Card', 'Modal')" }
26
+ },
27
+ required: ["component"]
28
+ }
29
+ },
30
+ {
31
+ name: "search_components",
32
+ description: "Search for components by name or modifier",
33
+ inputSchema: {
34
+ type: "object",
35
+ properties: {
36
+ query: { type: "string", description: "Search query (component name or modifier)" }
37
+ },
38
+ required: ["query"]
39
+ }
40
+ }
41
+ ].freeze
42
+
43
+ EXCLUDED_CONSTANTS = %i[Base Configurable Configuration Modifiers VERSION UPDATED_AT McpServer].freeze
44
+
45
+ class << self
46
+ def run
47
+ new.run
48
+ end
49
+ end
50
+
51
+ def run
52
+ warn "DaisyUI MCP Server v#{SERVER_VERSION} starting..."
53
+
54
+ loop do
55
+ line = $stdin.gets
56
+ break if line.nil?
57
+
58
+ request = JSON.parse(line)
59
+ response = handle_request(request)
60
+ $stdout.puts JSON.generate(response)
61
+ $stdout.flush
62
+ rescue JSON::ParserError => e
63
+ warn "JSON parse error: #{e.message}"
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def handle_request(request)
70
+ id = request["id"]
71
+ method = request["method"]
72
+ params = request["params"] || {}
73
+
74
+ result = case method
75
+ when "initialize"
76
+ handle_initialize(params)
77
+ when "tools/list"
78
+ handle_tools_list
79
+ when "tools/call"
80
+ handle_tools_call(params)
81
+ when "notifications/initialized"
82
+ return nil # No response for notifications
83
+ else
84
+ return error_response(id, -32_601, "Method not found: #{method}")
85
+ end
86
+
87
+ { jsonrpc: "2.0", id: id, result: result }
88
+ rescue StandardError => e
89
+ error_response(id, -32_603, "Internal error: #{e.message}")
90
+ end
91
+
92
+ def handle_initialize(_params)
93
+ {
94
+ protocolVersion: PROTOCOL_VERSION,
95
+ capabilities: { tools: {} },
96
+ serverInfo: { name: SERVER_NAME, version: SERVER_VERSION }
97
+ }
98
+ end
99
+
100
+ def handle_tools_list
101
+ { tools: TOOLS }
102
+ end
103
+
104
+ def handle_tools_call(params)
105
+ tool_name = params["name"]
106
+ arguments = params["arguments"] || {}
107
+
108
+ content = case tool_name
109
+ when "list_components"
110
+ list_components
111
+ when "get_component"
112
+ get_component(arguments["component"])
113
+ when "search_components"
114
+ search_components(arguments["query"])
115
+ else
116
+ return { content: [{ type: "text", text: "Unknown tool: #{tool_name}" }], isError: true }
117
+ end
118
+
119
+ { content: [{ type: "text", text: content }] }
120
+ end
121
+
122
+ def list_components
123
+ components = component_classes.map do |name, klass|
124
+ css_class = klass.respond_to?(:component_class) ? klass.component_class : name.downcase
125
+ modifier_count = klass.respond_to?(:modifiers) && klass.modifiers ? klass.modifiers.size : 0
126
+ "- #{name} (#{css_class}) - #{modifier_count} modifiers"
127
+ end
128
+
129
+ <<~TEXT
130
+ # DaisyUI Ruby/Phlex Components
131
+
132
+ #{components.join("\n")}
133
+
134
+ Total: #{components.size} components
135
+
136
+ Use `get_component` for detailed information about a specific component.
137
+ TEXT
138
+ end
139
+
140
+ def get_component(name)
141
+ return "Error: Component name required" if name.nil? || name.empty?
142
+
143
+ # Try exact match first, then case-insensitive
144
+ klass = find_component_class(name)
145
+ return "Component '#{name}' not found. Use `list_components` to see available components." unless klass
146
+
147
+ component_name = klass.name.split("::").last
148
+ css_class = klass.respond_to?(:component_class) ? klass.component_class : component_name.downcase
149
+ modifiers = klass.respond_to?(:modifiers) && klass.modifiers ? klass.modifiers : {}
150
+
151
+ # Group modifiers by category
152
+ modifier_list = modifiers.keys.map(&:to_s).sort.join(", ")
153
+
154
+ <<~TEXT
155
+ # #{component_name} Component
156
+
157
+ **CSS Class:** `#{css_class}`
158
+
159
+ ## Available Modifiers
160
+ #{modifier_list.empty? ? 'No modifiers' : modifier_list}
161
+
162
+ ## Usage Examples
163
+
164
+ ```ruby
165
+ # Basic usage
166
+ #{component_name}() { "Content" }
167
+
168
+ # With modifiers
169
+ #{component_name}(:primary) { "Primary styled" }
170
+ #{component_name}(:lg, :outline) { "Large outline" }
171
+
172
+ # With HTML attributes
173
+ #{component_name}(id: "my-#{css_class}", class: "custom") { "Custom" }
174
+
175
+ # As different element
176
+ #{component_name}(as: :a, href: "/path") { "Link" }
177
+ ```
178
+ TEXT
179
+ end
180
+
181
+ def search_components(query)
182
+ return "Error: Search query required" if query.nil? || query.empty?
183
+
184
+ query_downcase = query.downcase
185
+ matches = []
186
+
187
+ component_classes.each do |name, klass|
188
+ # Match component name
189
+ if name.downcase.include?(query_downcase)
190
+ matches << { name: name, match_type: "name" }
191
+ next
192
+ end
193
+
194
+ # Match modifiers
195
+ next unless klass.respond_to?(:modifiers) && klass.modifiers
196
+
197
+ matching_mods = klass.modifiers.keys.select { |m| m.to_s.include?(query_downcase) }
198
+ matches << { name: name, match_type: "modifier", modifiers: matching_mods } unless matching_mods.empty?
199
+ end
200
+
201
+ if matches.empty?
202
+ "No components found matching '#{query}'"
203
+ else
204
+ results = matches.map do |m|
205
+ if m[:match_type] == "name"
206
+ "- #{m[:name]} (name match)"
207
+ else
208
+ "- #{m[:name]} (modifiers: #{m[:modifiers].join(', ')})"
209
+ end
210
+ end
211
+
212
+ <<~TEXT
213
+ # Search Results for "#{query}"
214
+
215
+ #{results.join("\n")}
216
+
217
+ Found #{matches.size} component(s).
218
+ TEXT
219
+ end
220
+ end
221
+
222
+ def component_classes
223
+ @component_classes ||= DaisyUI.constants.each_with_object({}) do |const_name, hash|
224
+ next if EXCLUDED_CONSTANTS.include?(const_name)
225
+
226
+ klass = DaisyUI.const_get(const_name)
227
+ next unless klass.is_a?(Class) && klass < DaisyUI::Base
228
+
229
+ hash[const_name.to_s] = klass
230
+ end
231
+ end
232
+
233
+ def find_component_class(name)
234
+ # Exact match
235
+ return component_classes[name] if component_classes.key?(name)
236
+
237
+ # Case-insensitive match
238
+ component_classes.find { |k, _| k.downcase == name.downcase }&.last
239
+ end
240
+
241
+ def error_response(id, code, message)
242
+ { jsonrpc: "2.0", id: id, error: { code: code, message: message } }
243
+ end
244
+ end
245
+ end
data/lib/daisy_ui/menu.rb CHANGED
@@ -56,7 +56,18 @@ module DaisyUI
56
56
  # "@md:menu-lg"
57
57
  # "lg:menu-lg"
58
58
  # "@lg:menu-lg"
59
+ # "xl:menu-lg"
60
+ # "@xl:menu-lg"
59
61
  lg: "menu-lg",
62
+ # "sm:menu-xl"
63
+ # "@sm:menu-xl"
64
+ # "md:menu-xl"
65
+ # "@md:menu-xl"
66
+ # "lg:menu-xl"
67
+ # "@lg:menu-xl"
68
+ # "xl:menu-xl"
69
+ # "@xl:menu-xl"
70
+ xl: "menu-xl",
60
71
  # "sm:menu-vertical"
61
72
  # "@sm:menu-vertical"
62
73
  # "md:menu-vertical"
@@ -70,8 +81,7 @@ module DaisyUI
70
81
  # "@md:menu-horizontal"
71
82
  # "lg:menu-horizontal"
72
83
  # "@lg:menu-horizontal"
73
- horizontal: "menu-horizontal",
74
- **COLOR_MODIFIERS
84
+ horizontal: "menu-horizontal"
75
85
  )
76
86
  end
77
87
  end
@@ -22,27 +22,12 @@ module DaisyUI
22
22
  end
23
23
 
24
24
  register_modifiers(
25
- # "sm:disabled"
26
- # "@sm:disabled"
27
- # "md:disabled"
28
- # "@md:disabled"
29
- # "lg:disabled"
30
- # "@lg:disabled"
31
- disabled: "disabled",
32
- # "sm:active"
33
- # "@sm:active"
34
- # "md:active"
35
- # "@md:active"
36
- # "lg:active"
37
- # "@lg:active"
38
- active: "active",
39
- # "sm:focus"
40
- # "@sm:focus"
41
- # "md:focus"
42
- # "@md:focus"
43
- # "lg:focus"
44
- # "@lg:focus"
45
- focus: "focus"
25
+ # "sm:menu-disabled" "md:menu-disabled" "lg:menu-disabled"
26
+ disabled: "menu-disabled",
27
+ # "sm:menu-active" "md:menu-active" "lg:menu-active"
28
+ active: "menu-active",
29
+ # "sm:menu-focus" "md:menu-focus" "lg:menu-focus"
30
+ focus: "menu-focus"
46
31
  )
47
32
  end
48
33
  end
@@ -42,34 +42,62 @@ module DaisyUI
42
42
  end
43
43
 
44
44
  register_modifiers(
45
+ # Modifier
45
46
  # "sm:modal-open"
46
47
  # "@sm:modal-open"
47
48
  # "md:modal-open"
48
49
  # "@md:modal-open"
49
50
  # "lg:modal-open"
50
51
  # "@lg:modal-open"
52
+ # "xl:modal-open"
53
+ # "@xl:modal-open"
51
54
  open: "modal-open",
55
+ # Placement
52
56
  # "sm:modal-top"
53
57
  # "@sm:modal-top"
54
58
  # "md:modal-top"
55
59
  # "@md:modal-top"
56
60
  # "lg:modal-top"
57
61
  # "@lg:modal-top"
62
+ # "xl:modal-top"
63
+ # "@xl:modal-top"
58
64
  top: "modal-top",
65
+ # "sm:modal-middle"
66
+ # "@sm:modal-middle"
67
+ # "md:modal-middle"
68
+ # "@md:modal-middle"
69
+ # "lg:modal-middle"
70
+ # "@lg:modal-middle"
71
+ # "xl:modal-middle"
72
+ # "@xl:modal-middle"
73
+ middle: "modal-middle",
59
74
  # "sm:modal-bottom"
60
75
  # "@sm:modal-bottom"
61
76
  # "md:modal-bottom"
62
77
  # "@md:modal-bottom"
63
78
  # "lg:modal-bottom"
64
79
  # "@lg:modal-bottom"
80
+ # "xl:modal-bottom"
81
+ # "@xl:modal-bottom"
65
82
  bottom: "modal-bottom",
66
- # "sm:modal-middle"
67
- # "@sm:modal-middle"
68
- # "md:modal-middle"
69
- # "@md:modal-middle"
70
- # "lg:modal-middle"
71
- # "@lg:modal-middle"
72
- middle: "modal-middle"
83
+ # "sm:modal-start"
84
+ # "@sm:modal-start"
85
+ # "md:modal-start"
86
+ # "@md:modal-start"
87
+ # "lg:modal-start"
88
+ # "@lg:modal-start"
89
+ # "xl:modal-start"
90
+ # "@xl:modal-start"
91
+ start: "modal-start",
92
+ # "sm:modal-end"
93
+ # "@sm:modal-end"
94
+ # "md:modal-end"
95
+ # "@md:modal-end"
96
+ # "lg:modal-end"
97
+ # "@lg:modal-end"
98
+ # "xl:modal-end"
99
+ # "@xl:modal-end"
100
+ end: "modal-end"
73
101
  )
74
102
  end
75
103
  end
@@ -8,6 +8,11 @@ module DaisyUI
8
8
  public_send(as, class: classes, **attributes, &)
9
9
  end
10
10
 
11
- register_modifiers({})
11
+ register_modifiers(
12
+ # "sm:skeleton-text" "@sm:skeleton-text"
13
+ # "md:skeleton-text" "@md:skeleton-text"
14
+ # "lg:skeleton-text" "@lg:skeleton-text"
15
+ text: "skeleton-text"
16
+ )
12
17
  end
13
18
  end
@@ -9,14 +9,41 @@ module DaisyUI
9
9
  super
10
10
  end
11
11
 
12
- def view_template(&)
13
- public_send(as, class: classes, **attributes, &)
12
+ def view_template
13
+ public_send(as, class: classes, **attributes) do
14
+ yield self if block_given?
15
+ end
14
16
  end
15
17
 
16
- def step(**options, &)
17
- li(class: component_classes("step", options:), **options, &)
18
+ def step(*step_modifiers, **options, &)
19
+ step_classes = build_step_classes(step_modifiers, options)
20
+ li(class: step_classes, **options, &)
18
21
  end
19
22
 
23
+ private
24
+
25
+ def build_step_classes(step_modifiers, options)
26
+ base = apply_prefix("step")
27
+ mod_classes = step_modifiers.filter_map { |m| apply_prefix(step_modifier_map[m]) }
28
+ custom = options.delete(:class)
29
+ merge_classes(base, *mod_classes, custom)
30
+ end
31
+
32
+ def step_modifier_map
33
+ {
34
+ primary: "step-primary",
35
+ secondary: "step-secondary",
36
+ accent: "step-accent",
37
+ neutral: "step-neutral",
38
+ info: "step-info",
39
+ success: "step-success",
40
+ warning: "step-warning",
41
+ error: "step-error"
42
+ }
43
+ end
44
+
45
+ public
46
+
20
47
  def icon(**options, &)
21
48
  div(class: component_classes("step-icon", options:), **options, &)
22
49
  end
@@ -73,7 +73,18 @@ module DaisyUI
73
73
  # "@md:table-lg"
74
74
  # "lg:table-lg"
75
75
  # "@lg:table-lg"
76
- lg: "table-lg"
76
+ # "xl:table-lg"
77
+ # "@xl:table-lg"
78
+ lg: "table-lg",
79
+ # "sm:table-xl"
80
+ # "@sm:table-xl"
81
+ # "md:table-xl"
82
+ # "@md:table-xl"
83
+ # "lg:table-xl"
84
+ # "@lg:table-xl"
85
+ # "xl:table-xl"
86
+ # "@xl:table-xl"
87
+ xl: "table-xl"
77
88
  )
78
89
  end
79
90
  end
data/lib/daisy_ui/tabs.rb CHANGED
@@ -9,8 +9,10 @@ module DaisyUI
9
9
  @id = id
10
10
  end
11
11
 
12
- def view_template(&)
13
- public_send(as, role: :tablist, class: classes, **attributes, &)
12
+ def view_template
13
+ public_send(as, role: :tablist, class: classes, **attributes) do
14
+ yield self if block_given?
15
+ end
14
16
  end
15
17
 
16
18
  def tab(*args, label: nil, **, &)
@@ -81,7 +83,18 @@ module DaisyUI
81
83
  # "@md:tabs-lg"
82
84
  # "lg:tabs-lg"
83
85
  # "@lg:tabs-lg"
86
+ # "xl:tabs-lg"
87
+ # "@xl:tabs-lg"
84
88
  lg: "tabs-lg",
89
+ # "sm:tabs-xl"
90
+ # "@sm:tabs-xl"
91
+ # "md:tabs-xl"
92
+ # "@md:tabs-xl"
93
+ # "lg:tabs-xl"
94
+ # "@lg:tabs-xl"
95
+ # "xl:tabs-xl"
96
+ # "@xl:tabs-xl"
97
+ xl: "tabs-xl",
85
98
  # Placement modifiers
86
99
  # "sm:tabs-top"
87
100
  # "@sm:tabs-top"
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DaisyUI
4
+ class TextRotate < Base
5
+ self.component_class = "text-rotate"
6
+
7
+ def initialize(*, as: :span, **)
8
+ super
9
+ end
10
+
11
+ def view_template(&)
12
+ public_send(as, class: classes, **attributes) do
13
+ span(&)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -9,8 +9,10 @@ module DaisyUI
9
9
  super
10
10
  end
11
11
 
12
- def view_template(&)
13
- public_send(as, class: classes, **attributes, &)
12
+ def view_template
13
+ public_send(as, class: classes, **attributes) do
14
+ yield self if block_given?
15
+ end
14
16
  end
15
17
 
16
18
  def start(**options, &)
@@ -32,6 +32,13 @@ module DaisyUI
32
32
  # "lg:tooltip-top"
33
33
  # "@lg:tooltip-top"
34
34
  top: "tooltip-top",
35
+ # "sm:tooltip-neutral"
36
+ # "@sm:tooltip-neutral"
37
+ # "md:tooltip-neutral"
38
+ # "@md:tooltip-neutral"
39
+ # "lg:tooltip-neutral"
40
+ # "@lg:tooltip-neutral"
41
+ neutral: "tooltip-neutral",
35
42
  # "sm:tooltip-bottom"
36
43
  # "@sm:tooltip-bottom"
37
44
  # "md:tooltip-bottom"
@@ -3,5 +3,5 @@
3
3
  module DaisyUI
4
4
  # This timestamp is automatically updated when releasing a new version
5
5
  # Format: YYYY-MM-DD HH:MM:SS UTC
6
- UPDATED_AT = "2025-11-08 01:13:57 UTC"
6
+ UPDATED_AT = "2026-04-05 05:58:09 UTC"
7
7
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DaisyUI
4
- VERSION = "1.0.5"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/daisy_ui.rb CHANGED
@@ -9,6 +9,7 @@ loader = Zeitwerk::Loader.for_gem
9
9
  loader.inflector.inflect(
10
10
  "daisy_ui" => "DaisyUI"
11
11
  )
12
+ loader.ignore("#{__dir__}/daisyui.rb")
12
13
  loader.ignore("#{__dir__}/daisy_ui/updated_at.rb")
13
14
  loader.setup # ready!
14
15
  loader.load_file("#{__dir__}/daisy_ui/base.rb")
data/lib/daisyui.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "daisy_ui"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: daisyui
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
8
- bindir: bin
8
+ bindir: exe
9
9
  cert_chain: []
10
- date: 2025-11-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: phlex
@@ -47,10 +47,12 @@ description: DaisyUI is a comprehensive Ruby UI component library that brings Da
47
47
  beautiful Tailwind CSS components to your Ruby applications using Phlex. Build modern,
48
48
  accessible web interfaces with ease.
49
49
  email: mikael@zoolutions.llc
50
- executables: []
50
+ executables:
51
+ - daisyui-mcp
51
52
  extensions: []
52
53
  extra_rdoc_files: []
53
54
  files:
55
+ - exe/daisyui-mcp
54
56
  - lib/daisy_ui.rb
55
57
  - lib/daisy_ui/accordion.rb
56
58
  - lib/daisy_ui/alert.rb
@@ -81,6 +83,7 @@ files:
81
83
  - lib/daisy_ui/footer.rb
82
84
  - lib/daisy_ui/form_control.rb
83
85
  - lib/daisy_ui/hero.rb
86
+ - lib/daisy_ui/hover3d.rb
84
87
  - lib/daisy_ui/hover_gallery.rb
85
88
  - lib/daisy_ui/indicator.rb
86
89
  - lib/daisy_ui/input.rb
@@ -91,6 +94,7 @@ files:
91
94
  - lib/daisy_ui/list.rb
92
95
  - lib/daisy_ui/loading.rb
93
96
  - lib/daisy_ui/mask.rb
97
+ - lib/daisy_ui/mcp_server.rb
94
98
  - lib/daisy_ui/menu.rb
95
99
  - lib/daisy_ui/menu_item.rb
96
100
  - lib/daisy_ui/mockup_browser.rb
@@ -119,6 +123,7 @@ files:
119
123
  - lib/daisy_ui/table.rb
120
124
  - lib/daisy_ui/table_row.rb
121
125
  - lib/daisy_ui/tabs.rb
126
+ - lib/daisy_ui/text_rotate.rb
122
127
  - lib/daisy_ui/textarea.rb
123
128
  - lib/daisy_ui/theme_controller.rb
124
129
  - lib/daisy_ui/timeline.rb
@@ -128,14 +133,14 @@ files:
128
133
  - lib/daisy_ui/updated_at.rb
129
134
  - lib/daisy_ui/validator.rb
130
135
  - lib/daisy_ui/version.rb
131
- homepage: https://github.com/mhenrixon/daisy_ui
136
+ - lib/daisyui.rb
137
+ homepage: https://github.com/mhenrixon/daisyui
132
138
  licenses:
133
139
  - MIT
134
140
  metadata:
135
- homepage_uri: https://github.com/mhenrixon/daisy_ui
136
- source_code_uri: https://github.com/mhenrixon/daisy_ui
137
- changelog_uri: https://github.com/mhenrixon/daisy_ui/blob/main/CHANGELOG.md
138
- bug_tracker_uri: https://github.com/mhenrixon/daisy_ui/issues
141
+ source_code_uri: https://github.com/mhenrixon/daisyui
142
+ changelog_uri: https://github.com/mhenrixon/daisyui/blob/main/CHANGELOG.md
143
+ bug_tracker_uri: https://github.com/mhenrixon/daisyui/issues
139
144
  rubygems_mfa_required: 'true'
140
145
  rdoc_options: []
141
146
  require_paths:
@@ -151,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
156
  - !ruby/object:Gem::Version
152
157
  version: '0'
153
158
  requirements: []
154
- rubygems_version: 3.6.2
159
+ rubygems_version: 3.6.9
155
160
  specification_version: 4
156
161
  summary: Ruby UI components for DaisyUI built with Phlex
157
162
  test_files: []