daisyui 1.0.5 → 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.
@@ -8,11 +8,15 @@ module DaisyUI
8
8
  super
9
9
  end
10
10
 
11
- def view_template(&)
11
+ def view_template
12
12
  if tap_to_close?
13
- details(class: classes, **attributes, &)
13
+ details(class: classes, **attributes) do
14
+ yield self if block_given?
15
+ end
14
16
  else
15
- public_send(as, class: classes, **attributes, &)
17
+ public_send(as, class: classes, **attributes) do
18
+ yield self if block_given?
19
+ end
16
20
  end
17
21
  end
18
22
 
@@ -49,12 +53,33 @@ module DaisyUI
49
53
  end
50
54
 
51
55
  register_modifiers(
52
- # "sm:dropdown-end"
53
- # "@sm:dropdown-end"
54
- # "md:dropdown-end"
55
- # "@md:dropdown-end"
56
- # "lg:dropdown-end"
57
- # "@lg:dropdown-end"
56
+ # Placement
57
+ # "sm:dropdown-start"
58
+ # "@sm:dropdown-start"
59
+ # "md:dropdown-start"
60
+ # "@md:dropdown-start"
61
+ # "lg:dropdown-start"
62
+ # "@lg:dropdown-start"
63
+ # "xl:dropdown-start"
64
+ # "@xl:dropdown-start"
65
+ start: "dropdown-start",
66
+ # "sm:dropdown-center"
67
+ # "@sm:dropdown-center"
68
+ # "md:dropdown-center"
69
+ # "@md:dropdown-center"
70
+ # "lg:dropdown-center"
71
+ # "@lg:dropdown-center"
72
+ # "xl:dropdown-center"
73
+ # "@xl:dropdown-center"
74
+ center: "dropdown-center",
75
+ # "sm:dropdown-end"
76
+ # "@sm:dropdown-end"
77
+ # "md:dropdown-end"
78
+ # "@md:dropdown-end"
79
+ # "lg:dropdown-end"
80
+ # "@lg:dropdown-end"
81
+ # "xl:dropdown-end"
82
+ # "@xl:dropdown-end"
58
83
  end: "dropdown-end",
59
84
  # "sm:dropdown-top"
60
85
  # "@sm:dropdown-top"
@@ -62,6 +87,8 @@ module DaisyUI
62
87
  # "@md:dropdown-top"
63
88
  # "lg:dropdown-top"
64
89
  # "@lg:dropdown-top"
90
+ # "xl:dropdown-top"
91
+ # "@xl:dropdown-top"
65
92
  top: "dropdown-top",
66
93
  # "sm:dropdown-bottom"
67
94
  # "@sm:dropdown-bottom"
@@ -69,6 +96,8 @@ module DaisyUI
69
96
  # "@md:dropdown-bottom"
70
97
  # "lg:dropdown-bottom"
71
98
  # "@lg:dropdown-bottom"
99
+ # "xl:dropdown-bottom"
100
+ # "@xl:dropdown-bottom"
72
101
  bottom: "dropdown-bottom",
73
102
  # "sm:dropdown-left"
74
103
  # "@sm:dropdown-left"
@@ -76,6 +105,8 @@ module DaisyUI
76
105
  # "@md:dropdown-left"
77
106
  # "lg:dropdown-left"
78
107
  # "@lg:dropdown-left"
108
+ # "xl:dropdown-left"
109
+ # "@xl:dropdown-left"
79
110
  left: "dropdown-left",
80
111
  # "sm:dropdown-right"
81
112
  # "@sm:dropdown-right"
@@ -83,13 +114,18 @@ module DaisyUI
83
114
  # "@md:dropdown-right"
84
115
  # "lg:dropdown-right"
85
116
  # "@lg:dropdown-right"
117
+ # "xl:dropdown-right"
118
+ # "@xl:dropdown-right"
86
119
  right: "dropdown-right",
120
+ # Behavior
87
121
  # "sm:dropdown-hover"
88
122
  # "@sm:dropdown-hover"
89
123
  # "md:dropdown-hover"
90
124
  # "@md:dropdown-hover"
91
125
  # "lg:dropdown-hover"
92
126
  # "@lg:dropdown-hover"
127
+ # "xl:dropdown-hover"
128
+ # "@xl:dropdown-hover"
93
129
  hover: "dropdown-hover",
94
130
  # "sm:dropdown-open"
95
131
  # "@sm:dropdown-open"
@@ -97,6 +133,8 @@ module DaisyUI
97
133
  # "@md:dropdown-open"
98
134
  # "lg:dropdown-open"
99
135
  # "@lg:dropdown-open"
136
+ # "xl:dropdown-open"
137
+ # "@xl:dropdown-open"
100
138
  open: "dropdown-open"
101
139
  )
102
140
  end
data/lib/daisy_ui/hero.rb CHANGED
@@ -5,8 +5,10 @@ module DaisyUI
5
5
  class Hero < Base
6
6
  self.component_class = :hero
7
7
 
8
- def view_template(&)
9
- public_send(as, class: classes, **attributes, &)
8
+ def view_template
9
+ public_send(as, class: classes, **attributes) do
10
+ yield self if block_given?
11
+ end
10
12
  end
11
13
 
12
14
  def content(**options, &)
@@ -5,12 +5,27 @@ module DaisyUI
5
5
  class Indicator < Base
6
6
  self.component_class = :indicator
7
7
 
8
- def view_template(&)
9
- public_send(as, class: classes, **attributes, &)
8
+ def view_template
9
+ public_send(as, class: classes, **attributes) do
10
+ yield self if block_given?
11
+ end
10
12
  end
11
13
 
12
- def item(**options, &)
13
- span(class: component_classes("indicator-item", options:), **options, &)
14
+ def item(*item_modifiers, **options, &)
15
+ item_classes = build_item_classes(item_modifiers, options)
16
+ span(class: item_classes, **options, &)
17
+ end
18
+
19
+ private
20
+
21
+ def build_item_classes(item_modifiers, options)
22
+ # Start with base class
23
+ base = apply_prefix("indicator-item")
24
+ # Add modifier classes from position modifiers
25
+ mod_classes = item_modifiers.filter_map { |m| apply_prefix(modifier_map[m]) }
26
+ # Add custom class if provided
27
+ custom = options.delete(:class)
28
+ merge_classes(base, *mod_classes, custom)
14
29
  end
15
30
 
16
31
  register_modifiers(
@@ -82,7 +82,18 @@ module DaisyUI
82
82
  # "@md:loading-lg"
83
83
  # "lg:loading-lg"
84
84
  # "@lg:loading-lg"
85
+ # "xl:loading-lg"
86
+ # "@xl:loading-lg"
85
87
  lg: "loading-lg",
88
+ # "sm:loading-xl"
89
+ # "@sm:loading-xl"
90
+ # "md:loading-xl"
91
+ # "@md:loading-xl"
92
+ # "lg:loading-xl"
93
+ # "@lg:loading-xl"
94
+ # "xl:loading-xl"
95
+ # "@xl:loading-xl"
96
+ xl: "loading-xl",
86
97
  # "sm:text-primary"
87
98
  # "@sm:text-primary"
88
99
  # "md:text-primary"
@@ -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"
@@ -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
@@ -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"
@@ -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"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DaisyUI
4
- VERSION = "1.0.5"
4
+ VERSION = "1.0.6"
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"