senren-ui 0.1.4 → 0.1.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -2
  3. data/CONTRIBUTING.md +41 -8
  4. data/README.md +73 -11
  5. data/docs/components.md +222 -0
  6. data/docs/performance_testing.md +34 -0
  7. data/lib/commands/senren/add/add_command.rb +35 -0
  8. data/lib/generators/senren/install/install_generator.rb +4 -7
  9. data/lib/generators/senren/install/templates/base_component.rb.tt +39 -6
  10. data/lib/generators/senren/install/templates/conventions.md.tt +22 -8
  11. data/lib/senren/rails/agent_rules_writer.rb +175 -0
  12. data/lib/senren/rails/component_copier.rb +75 -6
  13. data/lib/senren/rails/component_installer.rb +47 -0
  14. data/lib/senren/rails/doctor.rb +26 -13
  15. data/lib/senren/rails/host_paths.rb +12 -3
  16. data/lib/senren/rails/installer.rb +4 -2
  17. data/lib/senren/rails/llms_writer.rb +5 -132
  18. data/lib/senren/rails/registry.rb +63 -31
  19. data/lib/senren/rails/skill_writer.rb +1 -1
  20. data/lib/senren/rails/version.rb +1 -1
  21. data/lib/senren/rails.rb +2 -0
  22. data/lib/tasks/senren.rake +26 -21
  23. data/templates/components/billing_plan_card/billing_plan_card_component.html.erb +1 -1
  24. data/templates/components/breadcrumb/breadcrumb_component.rb +2 -2
  25. data/templates/components/button/button_component.html.erb +1 -1
  26. data/templates/components/carousel/carousel_component.rb +1 -1
  27. data/templates/components/command/command_component.rb +1 -1
  28. data/templates/components/dropdown_menu/dropdown_menu_component.rb +10 -7
  29. data/templates/components/form/form_component.html.erb +8 -1
  30. data/templates/components/form/form_component.rb +3 -1
  31. data/templates/components/input/input_component.html.erb +1 -1
  32. data/templates/components/input/input_component.rb +19 -0
  33. data/templates/components/label/label_component.html.erb +1 -2
  34. data/templates/components/label/label_component.rb +12 -2
  35. data/templates/components/link/link_component.html.erb +1 -1
  36. data/templates/components/native_select/native_select_component.html.erb +19 -5
  37. data/templates/components/native_select/native_select_component.rb +17 -5
  38. data/templates/components/pagination/pagination_component.rb +2 -1
  39. data/templates/components/sidebar/sidebar_component.rb +2 -2
  40. data/templates/components/switch/switch_component.html.erb +2 -2
  41. data/templates/components/top_nav/top_nav_component.rb +2 -2
  42. data/templates/controllers/rich_text_editor_lite_controller.js +12 -2
  43. metadata +23 -4
@@ -13,20 +13,23 @@ module Senren
13
13
  lg: 'h-12 text-base px-4'
14
14
  }.freeze
15
15
 
16
- def initialize(name:, options:, selected: nil, id: nil, prompt: nil, variant: :default, size: :md, class_name: nil,
17
- **html)
16
+ # native_arrow: true → keep browser/OS native arrow (appearance-auto)
17
+ # native_arrow: false → use custom SVG arrow (appearance-none + SVG overlay)
18
+ def initialize(name:, options:, selected: nil, id: nil, prompt: nil, native_arrow: true,
19
+ variant: :default, size: :md, class_name: nil, **html)
18
20
  super(variant: variant, size: size, class_name: class_name, **html)
19
21
  @name = name
20
22
  @options = options
21
23
  @selected = selected
22
24
  @id = id || name.to_s.parameterize
23
25
  @prompt = prompt
26
+ @native_arrow = native_arrow
24
27
  end
25
28
 
26
29
  attr_reader :name, :options, :selected, :id, :prompt
27
30
 
28
- def wrapper_attrs
29
- { class: 'group relative w-full', data: { senren_component: senren_component_name } }
31
+ def native_arrow?
32
+ @native_arrow
30
33
  end
31
34
 
32
35
  def select_attrs
@@ -39,9 +42,18 @@ module Senren
39
42
  )
40
43
  end
41
44
 
45
+ def root_select_attrs
46
+ attrs = select_attrs
47
+ data = (attrs[:data] || {}).merge(senren_component: senren_component_name)
48
+
49
+ attrs.merge(data: data)
50
+ end
51
+
42
52
  def select_classes
53
+ appearance = native_arrow? ? 'appearance-auto' : 'appearance-none pr-9'
43
54
  [
44
- 'flex w-full cursor-pointer appearance-none rounded-(--senren-radius) border bg-[hsl(var(--senren-background))] pr-9 text-[hsl(var(--senren-foreground))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50',
55
+ 'w-full cursor-pointer rounded-(--senren-radius) border bg-[hsl(var(--senren-background))] text-[hsl(var(--senren-foreground))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50',
56
+ appearance,
45
57
  self.class::VARIANTS[variant],
46
58
  self.class::SIZES[size],
47
59
  class_name,
@@ -18,7 +18,8 @@ module Senren
18
18
  def page_url(page)
19
19
  return '#' unless path
20
20
 
21
- path.respond_to?(:call) ? path.call(page) : path.to_s.gsub(':page', page.to_s)
21
+ safe_page = page.to_i.clamp(1, total_pages)
22
+ safe_url(path.respond_to?(:call) ? path.call(safe_page) : path.to_s.gsub(':page', safe_page.to_s))
22
23
  end
23
24
  end
24
25
  end
@@ -24,12 +24,12 @@ module Senren
24
24
  if item.is_a?(Hash)
25
25
  {
26
26
  label: item[:label] || item['label'],
27
- href: item[:href] || item['href'] || '#',
27
+ href: safe_url(item[:href] || item['href']),
28
28
  active: item[:active] || item['active']
29
29
  }
30
30
  else
31
31
  label, href = item
32
- { label: label, href: href || '#', active: false }
32
+ { label: label, href: safe_url(href), active: false }
33
33
  end
34
34
  end
35
35
  end
@@ -1,4 +1,4 @@
1
- <label class="inline-flex items-center gap-3 cursor-pointer">
1
+ <%= tag.label(**root_attrs("inline-flex items-center gap-3 cursor-pointer")) do %>
2
2
  <span class="relative inline-flex h-6 w-11 items-center rounded-full bg-[hsl(var(--senren-muted))] has-[:checked]:bg-[hsl(var(--senren-primary))] transition-colors">
3
3
  <input type="checkbox" role="switch" id="<%= id %>" name="<%= name %>" value="<%= value %>" <%= "checked" if checked %> class="peer sr-only">
4
4
  <span aria-hidden="true" class="absolute left-0.5 top-0.5 inline-block h-5 w-5 transform rounded-full bg-[hsl(var(--senren-background))] transition-transform peer-checked:translate-x-5"></span>
@@ -8,4 +8,4 @@
8
8
  <% else %>
9
9
  <%= content %>
10
10
  <% end %>
11
- </label>
11
+ <% end %>
@@ -31,12 +31,12 @@ module Senren
31
31
  if item.is_a?(Hash)
32
32
  {
33
33
  label: item[:label] || item['label'],
34
- href: item[:href] || item['href'] || '#',
34
+ href: safe_url(item[:href] || item['href']),
35
35
  active: item[:active] || item['active']
36
36
  }
37
37
  else
38
38
  label, href = item
39
- { label: label, href: href || '#', active: false }
39
+ { label: label, href: safe_url(href), active: false }
40
40
  end
41
41
  end
42
42
  end
@@ -1,5 +1,7 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
+ const ALLOWED_LINK_PROTOCOLS = new Set(["http:", "https:", "mailto:", "tel:"])
4
+
3
5
  // senren--rich-text-editor-lite
4
6
  // Local UI: tiny contenteditable toolbar synced to a hidden textarea.
5
7
  export default class extends Controller {
@@ -280,8 +282,16 @@ export default class extends Controller {
280
282
  normalizeUrl(rawUrl) {
281
283
  const url = String(rawUrl || "").trim()
282
284
  if (url.length === 0) return null
283
- if (/^(?:[a-z][a-z0-9+.-]*:|\/|#)/i.test(url)) return url
284
- return `https://${url}`
285
+ if (url.startsWith("#")) return url
286
+ if (url.startsWith("/") && !url.startsWith("//")) return url
287
+
288
+ const candidate = /^[a-z][a-z0-9+.-]*:/i.test(url) ? url : `https://${url}`
289
+ try {
290
+ const parsed = new URL(candidate, window.location.origin)
291
+ return ALLOWED_LINK_PROTOCOLS.has(parsed.protocol) ? parsed.href : null
292
+ } catch (_) {
293
+ return null
294
+ }
285
295
  }
286
296
 
287
297
  activeRange() {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: senren-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - vutt
@@ -9,6 +9,20 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: nokogiri
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 1.19.3
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 1.19.3
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: rails
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -29,14 +43,14 @@ dependencies:
29
43
  requirements:
30
44
  - - ">="
31
45
  - !ruby/object:Gem::Version
32
- version: '3.0'
46
+ version: 4.9.0
33
47
  type: :runtime
34
48
  prerelease: false
35
49
  version_requirements: !ruby/object:Gem::Requirement
36
50
  requirements:
37
51
  - - ">="
38
52
  - !ruby/object:Gem::Version
39
- version: '3.0'
53
+ version: 4.9.0
40
54
  description: |
41
55
  Senren UI is a Rails-native UI component library inspired by the developer
42
56
  experience of shadcn/ui. It ships generators, a registry, and a centralized
@@ -53,7 +67,10 @@ files:
53
67
  - LICENSE
54
68
  - README.md
55
69
  - Rakefile
70
+ - docs/components.md
71
+ - docs/performance_testing.md
56
72
  - docs/visual_style.md
73
+ - lib/commands/senren/add/add_command.rb
57
74
  - lib/generators/senren/component/component_generator.rb
58
75
  - lib/generators/senren/component/templates/component.html.erb.tt
59
76
  - lib/generators/senren/component/templates/component.rb.tt
@@ -66,7 +83,9 @@ files:
66
83
  - lib/generators/senren/install/templates/installed_components.yml.tt
67
84
  - lib/generators/senren/install/templates/senren.css.tt
68
85
  - lib/senren/rails.rb
86
+ - lib/senren/rails/agent_rules_writer.rb
69
87
  - lib/senren/rails/component_copier.rb
88
+ - lib/senren/rails/component_installer.rb
70
89
  - lib/senren/rails/doctor.rb
71
90
  - lib/senren/rails/engine.rb
72
91
  - lib/senren/rails/host_paths.rb
@@ -244,7 +263,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
244
263
  requirements:
245
264
  - - ">="
246
265
  - !ruby/object:Gem::Version
247
- version: 3.1.0
266
+ version: 3.2.0
248
267
  required_rubygems_version: !ruby/object:Gem::Requirement
249
268
  requirements:
250
269
  - - ">="