poetry-core 0.0.2

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 (166) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/DESIGN.md +145 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +30 -0
  6. data/THIRD_PARTY_NOTICES.md +507 -0
  7. data/app/components/poetry/core/box/component.rb +82 -0
  8. data/app/components/poetry/core/component.rb +390 -0
  9. data/app/components/poetry/core/concerns/agent_tools.rb +418 -0
  10. data/app/components/poetry/core/concerns/declared_attributes.rb +267 -0
  11. data/app/components/poetry/core/concerns/introspection.rb +533 -0
  12. data/app/components/poetry/core/concerns/options.rb +262 -0
  13. data/app/components/poetry/core/concerns/parts.rb +148 -0
  14. data/app/components/poetry/core/concerns/stimulus.rb +363 -0
  15. data/app/components/poetry/core/concerns/styles.rb +323 -0
  16. data/app/components/poetry/core/style.rb +150 -0
  17. data/app/components/poetry/core/wrapper/component.rb +90 -0
  18. data/app/javascript/poetry/core/accordion_controller.js +165 -0
  19. data/app/javascript/poetry/core/action_bar_controller.js +109 -0
  20. data/app/javascript/poetry/core/autocomplete_controller.js +220 -0
  21. data/app/javascript/poetry/core/calendar_controller.js +406 -0
  22. data/app/javascript/poetry/core/carousel_controller.js +167 -0
  23. data/app/javascript/poetry/core/checkbox_group_controller.js +97 -0
  24. data/app/javascript/poetry/core/checked_controller.js +208 -0
  25. data/app/javascript/poetry/core/clipboard_text_controller.js +111 -0
  26. data/app/javascript/poetry/core/combobox_controller.js +1095 -0
  27. data/app/javascript/poetry/core/command_controller.js +510 -0
  28. data/app/javascript/poetry/core/context_menu_controller.js +204 -0
  29. data/app/javascript/poetry/core/date_field_controller.js +584 -0
  30. data/app/javascript/poetry/core/date_picker_controller.js +111 -0
  31. data/app/javascript/poetry/core/deferred_controller.js +111 -0
  32. data/app/javascript/poetry/core/dialog_controller.js +206 -0
  33. data/app/javascript/poetry/core/dismissable_controller.js +182 -0
  34. data/app/javascript/poetry/core/drawer_controller.js +365 -0
  35. data/app/javascript/poetry/core/file_input_controller.js +191 -0
  36. data/app/javascript/poetry/core/focus_scope_controller.js +162 -0
  37. data/app/javascript/poetry/core/helpers/announce.js +228 -0
  38. data/app/javascript/poetry/core/helpers/breakpoint.js +28 -0
  39. data/app/javascript/poetry/core/helpers/collection.js +18 -0
  40. data/app/javascript/poetry/core/helpers/direction.js +15 -0
  41. data/app/javascript/poetry/core/helpers/escape.js +38 -0
  42. data/app/javascript/poetry/core/helpers/filter_rank.js +104 -0
  43. data/app/javascript/poetry/core/helpers/focus_guards.js +46 -0
  44. data/app/javascript/poetry/core/helpers/hotkey.js +43 -0
  45. data/app/javascript/poetry/core/helpers/id_integrity.js +65 -0
  46. data/app/javascript/poetry/core/helpers/incomplete_date.js +328 -0
  47. data/app/javascript/poetry/core/helpers/mask.js +283 -0
  48. data/app/javascript/poetry/core/helpers/portal.js +217 -0
  49. data/app/javascript/poetry/core/helpers/presence.js +210 -0
  50. data/app/javascript/poetry/core/helpers/registration_guard.js +76 -0
  51. data/app/javascript/poetry/core/helpers/scroll_lock.js +67 -0
  52. data/app/javascript/poetry/core/helpers/scroller_geometry.js +422 -0
  53. data/app/javascript/poetry/core/helpers/state.js +80 -0
  54. data/app/javascript/poetry/core/helpers/tabbable.js +56 -0
  55. data/app/javascript/poetry/core/helpers/turbo_cache.js +21 -0
  56. data/app/javascript/poetry/core/helpers/typeahead.js +85 -0
  57. data/app/javascript/poetry/core/hotkey_controller.js +55 -0
  58. data/app/javascript/poetry/core/hover_card_controller.js +431 -0
  59. data/app/javascript/poetry/core/index.js +218 -0
  60. data/app/javascript/poetry/core/mask_controller.js +500 -0
  61. data/app/javascript/poetry/core/menu_controller.js +1050 -0
  62. data/app/javascript/poetry/core/menubar_controller.js +329 -0
  63. data/app/javascript/poetry/core/message_scroller_controller.js +930 -0
  64. data/app/javascript/poetry/core/navigation_menu_controller.js +458 -0
  65. data/app/javascript/poetry/core/number_field_controller.js +404 -0
  66. data/app/javascript/poetry/core/optimistic_form_controller.js +56 -0
  67. data/app/javascript/poetry/core/otp_controller.js +219 -0
  68. data/app/javascript/poetry/core/popover_controller.js +300 -0
  69. data/app/javascript/poetry/core/popper_controller.js +439 -0
  70. data/app/javascript/poetry/core/pressed_controller.js +74 -0
  71. data/app/javascript/poetry/core/questionnaire_controller.js +468 -0
  72. data/app/javascript/poetry/core/radio_group_controller.js +197 -0
  73. data/app/javascript/poetry/core/resizable_controller.js +202 -0
  74. data/app/javascript/poetry/core/roving_focus_controller.js +215 -0
  75. data/app/javascript/poetry/core/scroll_spy_controller.js +84 -0
  76. data/app/javascript/poetry/core/search_field_controller.js +95 -0
  77. data/app/javascript/poetry/core/select_controller.js +924 -0
  78. data/app/javascript/poetry/core/sensitive_input_controller.js +195 -0
  79. data/app/javascript/poetry/core/sheet_controller.js +24 -0
  80. data/app/javascript/poetry/core/sidebar_controller.js +272 -0
  81. data/app/javascript/poetry/core/slider_controller.js +419 -0
  82. data/app/javascript/poetry/core/state_controller.js +82 -0
  83. data/app/javascript/poetry/core/table_selection_controller.js +153 -0
  84. data/app/javascript/poetry/core/tabs_controller.js +151 -0
  85. data/app/javascript/poetry/core/tag_group_controller.js +134 -0
  86. data/app/javascript/poetry/core/toast_controller.js +228 -0
  87. data/app/javascript/poetry/core/toast_trigger_controller.js +26 -0
  88. data/app/javascript/poetry/core/toaster_controller.js +197 -0
  89. data/app/javascript/poetry/core/toggle_group_controller.js +206 -0
  90. data/app/javascript/poetry/core/tooltip_controller.js +541 -0
  91. data/app/javascript/poetry/core/tree_controller.js +265 -0
  92. data/app/javascript/poetry/core/vendor/floating_ui_core.js +1043 -0
  93. data/app/javascript/poetry/core/vendor/floating_ui_dom.js +794 -0
  94. data/app/javascript/poetry/core/vendor/floating_ui_utils.js +141 -0
  95. data/app/javascript/poetry/core/vendor/floating_ui_utils_dom.js +170 -0
  96. data/app/views/poetry/core/preview.html.erb +13 -0
  97. data/config/component_registry.yml +20 -0
  98. data/config/controllers_manifest.json +1528 -0
  99. data/config/importmap.rb +11 -0
  100. data/config/state_vocabulary.json +31 -0
  101. data/lib/active_model/type/list.rb +33 -0
  102. data/lib/active_model/type/symbol.rb +36 -0
  103. data/lib/poetry/core/check/stable_identity.rb +104 -0
  104. data/lib/poetry/core/check.rb +1445 -0
  105. data/lib/poetry/core/config.rb +286 -0
  106. data/lib/poetry/core/contrib/wrapped_helper.rb +68 -0
  107. data/lib/poetry/core/css/bem_merger.rb +45 -0
  108. data/lib/poetry/core/css/bem_reference.rb +87 -0
  109. data/lib/poetry/core/css/override_scan.rb +110 -0
  110. data/lib/poetry/core/css/resolver.rb +161 -0
  111. data/lib/poetry/core/css/safelist.rb +41 -0
  112. data/lib/poetry/core/css/tailwind_merger.rb +102 -0
  113. data/lib/poetry/core/css/template_classes.rb +102 -0
  114. data/lib/poetry/core/css/theme_coverage.rb +72 -0
  115. data/lib/poetry/core/css/var_coverage.rb +75 -0
  116. data/lib/poetry/core/css/verifier.rb +106 -0
  117. data/lib/poetry/core/design_lint.rb +720 -0
  118. data/lib/poetry/core/design_md/import.rb +250 -0
  119. data/lib/poetry/core/design_md.rb +461 -0
  120. data/lib/poetry/core/engine.rb +71 -0
  121. data/lib/poetry/core/errors.rb +53 -0
  122. data/lib/poetry/core/html/attributes.rb +667 -0
  123. data/lib/poetry/core/icons.rb +157 -0
  124. data/lib/poetry/core/llms_text.rb +354 -0
  125. data/lib/poetry/core/page_architectures.rb +304 -0
  126. data/lib/poetry/core/part_contract.rb +285 -0
  127. data/lib/poetry/core/preview/abstract.rb +121 -0
  128. data/lib/poetry/core/preview/base.rb +227 -0
  129. data/lib/poetry/core/preview/sidecarable.rb +136 -0
  130. data/lib/poetry/core/preview/template.rb +186 -0
  131. data/lib/poetry/core/recipe_items.rb +82 -0
  132. data/lib/poetry/core/registry.rb +363 -0
  133. data/lib/poetry/core/registry_address.rb +115 -0
  134. data/lib/poetry/core/registry_client.rb +217 -0
  135. data/lib/poetry/core/registry_installer.rb +234 -0
  136. data/lib/poetry/core/registry_items.rb +184 -0
  137. data/lib/poetry/core/skill_text.rb +288 -0
  138. data/lib/poetry/core/stable_id.rb +110 -0
  139. data/lib/poetry/core/stimulus/builder.rb +395 -0
  140. data/lib/poetry/core/stimulus/declarations.rb +444 -0
  141. data/lib/poetry/core/stimulus/manifest.rb +68 -0
  142. data/lib/poetry/core/stimulus/merger.rb +131 -0
  143. data/lib/poetry/core/stimulus_contract.rb +263 -0
  144. data/lib/poetry/core/tag_helper.rb +10 -0
  145. data/lib/poetry/core/template_compile.rb +74 -0
  146. data/lib/poetry/core/token_import.rb +317 -0
  147. data/lib/poetry/core/tokens/color.rb +215 -0
  148. data/lib/poetry/core/tokens/contrast_gate.rb +120 -0
  149. data/lib/poetry/core/tokens/generator.rb +211 -0
  150. data/lib/poetry/core/tokens.rb +117 -0
  151. data/lib/poetry/core/version.rb +9 -0
  152. data/lib/poetry/core.rb +52 -0
  153. data/lib/poetry-core.rb +5 -0
  154. data/tokens/aliases.css +145 -0
  155. data/tokens/tailwind-theme.css +47 -0
  156. data/tokens/tokens.css +78 -0
  157. data/tokens/tokens.dtcg.json +779 -0
  158. data/vendor/floating-ui/LICENSE +20 -0
  159. data/vendor/floating-ui/VENDORED_VERSIONS +15 -0
  160. data/vendor/shadcn-tailwind/LICENSE +21 -0
  161. data/vendor/shadcn-tailwind/VENDORED_COMMIT +1 -0
  162. data/vendor/shadcn-tailwind/tailwind.css +629 -0
  163. data/vendor/tw-animate-css/LICENSE +21 -0
  164. data/vendor/tw-animate-css/VENDORED_VERSION +1 -0
  165. data/vendor/tw-animate-css/tw-animate.css +1 -0
  166. metadata +267 -0
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # poetry-core's importmap pins (the importmap-first channel): the
4
+ # host app's importmap merges these, so `import { registerPoetryControllers }
5
+ # from "@poetry/controllers"` works with zero build. Subpath specifiers
6
+ # (@poetry/controllers/helpers/state) resolve here via pin_all_from and in
7
+ # bundler hosts via the npm package's exports map - one source, two channels.
8
+
9
+ pin "@poetry/controllers", to: "poetry/core/index.js"
10
+ pin_all_from File.expand_path("../app/javascript/poetry/core", __dir__),
11
+ under: "@poetry/controllers", to: "poetry/core"
@@ -0,0 +1,31 @@
1
+ {
2
+ "keys": [
3
+ "active",
4
+ "checked",
5
+ "closed",
6
+ "inactive",
7
+ "indeterminate",
8
+ "open",
9
+ "panel-closed",
10
+ "panel-open",
11
+ "popup-closed",
12
+ "popup-open",
13
+ "pressed",
14
+ "selected",
15
+ "unchecked",
16
+ "unpressed",
17
+ "unselected"
18
+ ],
19
+ "attributes": [
20
+ "data-active",
21
+ "data-checked",
22
+ "data-closed",
23
+ "data-indeterminate",
24
+ "data-open",
25
+ "data-panel-open",
26
+ "data-popup-open",
27
+ "data-pressed",
28
+ "data-selected",
29
+ "data-unchecked"
30
+ ]
31
+ }
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/type"
4
+
5
+ # Reopened to register poetry's option types alongside Rails' own.
6
+ module ActiveModel
7
+ # Reopened: poetry registers its list (and symbol) attribute types here
8
+ # so the option/style DSLs can declare them.
9
+ module Type
10
+ # A string-array option type (Accordion's open:, future multi-selects).
11
+ # Casts scalars to one-element arrays and stringifies members, so
12
+ # open: :shipping and open: %w[a b] both normalize.
13
+ class List < ActiveModel::Type::Value
14
+ # Reports :list to introspection (prop_definitions, the registry).
15
+ #
16
+ # @return [Symbol]
17
+ def type
18
+ :list
19
+ end
20
+
21
+ # Normalizes any value to an array of strings: nil becomes [], a
22
+ # scalar becomes a one-element array, and members are stringified.
23
+ #
24
+ # @param value [Object] the declared option value
25
+ # @return [Array<String>]
26
+ def cast(value)
27
+ return [] if value.nil?
28
+
29
+ Array(value).map(&:to_s)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/type"
4
+
5
+ module ActiveModel
6
+ module Type
7
+ # A symbol option type for the option DSL: casts anything responding
8
+ # to #to_sym and reports :symbol to introspection.
9
+ #
10
+ # Strict by design: a value that cannot become a Symbol raises at
11
+ # assignment - poetry surfaces bad option values where they are
12
+ # written, not as a validation error nothing reads at render.
13
+ # Leniency is List's job, not this type's.
14
+ class Symbol < ActiveModel::Type::String
15
+ # Without this the type reports :string (inherited) - introspection
16
+ # (prop_definitions, the registry) must see :symbol.
17
+ #
18
+ # @return [Symbol] :symbol
19
+ def type
20
+ :symbol
21
+ end
22
+
23
+ # Casts a value to a Symbol; nil stays nil.
24
+ #
25
+ # @param value [#to_sym, nil] the raw option value
26
+ # @return [Symbol, nil]
27
+ # @raise [ArgumentError] when the value cannot become a Symbol
28
+ def cast(value)
29
+ return nil if value.nil?
30
+ raise ArgumentError, "#{value.inspect} can't become a Symbol (no #to_sym)" unless value.respond_to?(:to_sym)
31
+
32
+ value.to_sym
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poetry
4
+ module Core
5
+ module Check
6
+ # The StableId heuristics: two WARNING-severity rules over
7
+ # raw ERB text, each catching a render that escapes the per-request
8
+ # id story and therefore needs explicit identity (key: or id:).
9
+ #
10
+ # HEURISTIC, by design and by documentation: block extents are
11
+ # matched by scanning scriptlet openers/closers, which covers
12
+ # conventional ERB and misses exotic structure; cross-template
13
+ # composition (a cached partial rendering components defined
14
+ # elsewhere) is invisible to any static check - the dev-mode
15
+ # composed-DOM tripwire is the runtime complement.
16
+ #
17
+ # - stable-identity/cache: a poetry component inside a `<% cache %>`
18
+ # block without key:/id: freezes a random id into the fragment;
19
+ # replays can collide and morphs can't pair it.
20
+ # - stable-identity/collection: a poetry component inside an
21
+ # each/map loop without key:/id: gets a fresh random id per
22
+ # render, so Turbo morph replaces it on any reorder instead of
23
+ # following the record.
24
+ #
25
+ # A component whose registry entry declares "identity" => false
26
+ # renders no poetry-minted id - key:/id: would have nothing to
27
+ # stabilize - so both rules skip it. The registry derivation owns
28
+ # that fact per gem; legacy entries without the key keep warning.
29
+ #
30
+ # @api private
31
+ class StableIdentity
32
+ # A scriptlet that OPENS a Ruby block (needs its own <% end %>):
33
+ # trailing `do |...|`, or a non-modifier if/unless/case/begin.
34
+ BLOCK_OPENER =
35
+ /(?:\bdo\s*(?:\|[^|]*\|)?\s*\z)|(?:\A\s*(?:if|unless|case|while|until|for|begin)\b)/
36
+ CACHE_OPENER = /\A\s*cache[\s(]/
37
+ LOOP_OPENER =
38
+ /\.(?:each|each_with_index|each_with_object|each_slice|map|collect|flat_map)\b.*\bdo\b|\Afor\s.+\sin\s/
39
+ IDENTITY_ARG = /(?:\bkey:|\bid:|["']key["']\s*=>|["']id["']\s*=>)/
40
+
41
+ def initialize(catalog)
42
+ @catalog = catalog
43
+ end
44
+
45
+ # Returns [Finding] for one ERB source. Tracks a stack of open
46
+ # blocks (tagged cache/loop/other) and flags poetry helper calls
47
+ # rendered while a cache or loop frame is open.
48
+ def lint(source)
49
+ findings = []
50
+ stack = []
51
+
52
+ source.each_line.with_index(1) do |line, number|
53
+ line.scan(/<%(?:=|-)?\s*(.*?)\s*(?:-)?%>/m).each do |(code)|
54
+ next if code.nil?
55
+
56
+ flag_helpers(code, number, stack, findings)
57
+
58
+ if code.match?(/\A\s*end\b/)
59
+ stack.pop
60
+ elsif code.match?(BLOCK_OPENER)
61
+ stack << frame_for(code)
62
+ end
63
+ end
64
+ end
65
+
66
+ findings
67
+ end
68
+
69
+ private
70
+
71
+ def frame_for(code)
72
+ return :cache if code.match?(CACHE_OPENER)
73
+ return :loop if code.match?(LOOP_OPENER)
74
+
75
+ :other
76
+ end
77
+
78
+ def flag_helpers(code, line, stack, findings)
79
+ code.scan(/\b(poetry_[a-z0-9_]+)\b/) do |(helper)|
80
+ next unless @catalog.helper?(helper)
81
+ next if @catalog.identity_free?(helper)
82
+ next if code.match?(IDENTITY_ARG)
83
+
84
+ if stack.include?(:cache)
85
+ findings << Finding.new(
86
+ rule: "stable-identity/cache", severity: :warning, line: line,
87
+ message: "#{helper} inside a cache block without key:/id: freezes a random id " \
88
+ "into the fragment - replays can collide and Turbo morph can't pair it " \
89
+ "(heuristic: block extents are scanned, not parsed)"
90
+ )
91
+ elsif stack.include?(:loop)
92
+ findings << Finding.new(
93
+ rule: "stable-identity/collection", severity: :warning, line: line,
94
+ message: "#{helper} inside a collection loop without key:/id: re-randomizes " \
95
+ "per render - a Turbo morph reorder replaces it instead of following " \
96
+ "the record; pass key: record (heuristic)"
97
+ )
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end