panda-core 0.11.0 → 0.12.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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -0
  3. data/app/assets/tailwind/application.css +5 -0
  4. data/app/models/panda/core/user.rb +20 -11
  5. data/config/brakeman.ignore +51 -0
  6. data/db/migrate/20250809000001_create_panda_core_users.rb +1 -1
  7. data/db/migrate/20251203100000_rename_is_admin_to_admin_in_panda_core_users.rb +18 -0
  8. data/lib/panda/core/configuration.rb +4 -0
  9. data/lib/panda/core/engine/autoload_config.rb +9 -10
  10. data/lib/panda/core/engine/omniauth_config.rb +82 -36
  11. data/lib/panda/core/engine/route_config.rb +37 -0
  12. data/lib/panda/core/engine.rb +46 -41
  13. data/lib/panda/core/middleware.rb +146 -0
  14. data/lib/panda/core/testing/rails_helper.rb +17 -8
  15. data/lib/panda/core/testing/support/authentication_helpers.rb +6 -6
  16. data/lib/panda/core/testing/support/authentication_test_helpers.rb +2 -12
  17. data/lib/panda/core/testing/support/system/browser_console_logger.rb +1 -2
  18. data/lib/panda/core/testing/support/system/chrome_path.rb +38 -0
  19. data/lib/panda/core/testing/support/system/cuprite_helpers.rb +79 -0
  20. data/lib/panda/core/testing/support/system/cuprite_setup.rb +61 -66
  21. data/lib/panda/core/testing/support/system/system_test_helpers.rb +11 -11
  22. data/lib/panda/core/version.rb +1 -1
  23. data/lib/tasks/panda/core/users.rake +3 -3
  24. data/lib/tasks/panda/shared.rake +31 -5
  25. data/public/panda-core-assets/favicons/browserconfig.xml +1 -1
  26. data/public/panda-core-assets/favicons/site.webmanifest +1 -1
  27. data/public/panda-core-assets/panda-core-0.11.0.css +2 -0
  28. data/public/panda-core-assets/panda-core.css +2 -2
  29. metadata +8 -8
  30. data/lib/panda/core/engine/middleware_config.rb +0 -17
  31. data/lib/panda/core/testing/support/system/better_system_tests.rb +0 -180
  32. data/lib/panda/core/testing/support/system/capybara_config.rb +0 -64
  33. data/lib/panda/core/testing/support/system/ci_capybara_config.rb +0 -77
  34. data/public/panda-core-assets/panda-core-0.10.6.css +0 -2
  35. data/public/panda-core-assets/panda-core-0.10.7.css +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecbb8f9e4f925e37db74cbb9bec9b45277feceafc990fbab333433adf86e8e36
4
- data.tar.gz: ab419ee62bb77a3c382b462997439853ec07e438a37ea0d02935eb52d69d7169
3
+ metadata.gz: 1865844f240222fd1894bae360505e3954858ac363b49d4bd0aa59bb8699fdcc
4
+ data.tar.gz: a85741affaa234bd9899c670da3b7a4eab5c7aa65bf09ba89081b49606ecbf66
5
5
  SHA512:
6
- metadata.gz: 79f71a37792d0c3b85edb9d211459e2b1433c3fab96bc8adfa707bb8ce443f11c80cdb12c05a75a03154912908c1bb759fdc2031b3ae1840a045f1629d3f0d14
7
- data.tar.gz: 1d4f21c85d14ee7a474d8a83f1d99bccac31761a2803372a9d10d49156618cc40331d598ea805273040f8b56b5f4562bd873765bde423c76e358a76aab8ca56a
6
+ metadata.gz: a873452e2589d9fc7e4254f7ef81b4bb8cbccc2a57fd19d17f95aed22609d1226711579fe905b5caf269fec5506a203fa93ab16b10073ec372bf033e0ccd70be
7
+ data.tar.gz: c1be83e01a08ff7a6ddce1b2afc321ec780dd5f2b00837220c411838f39e0dd9584419737239c8640a25e15b4d86ea190109fc22c26291ea21d6551317082773
data/README.md CHANGED
@@ -271,6 +271,21 @@ bundle exec rspec spec/generators
271
271
  bundle exec rspec spec/system
272
272
  ```
273
273
 
274
+ ### CI/Docker parity
275
+
276
+ The GitHub Actions workflow now builds a reusable CI image (Chrome + PostgreSQL + Ruby). You can reuse it locally:
277
+
278
+ ```
279
+ bin/ci build # build the CI image locally
280
+ bin/ci test # run the suite inside the container
281
+ ```
282
+
283
+ To dry-run the workflow with `act` (uses the locally built image tagged `:local`):
284
+
285
+ ```
286
+ act -j test-stable
287
+ ```
288
+
274
289
  ## Contributing
275
290
 
276
291
  1. Fork it
@@ -153,6 +153,11 @@
153
153
  html[data-theme='sky'] .bg-gradient-admin {
154
154
  background: linear-gradient(to bottom right, rgb(20, 32, 74), rgb(42, 102, 159));
155
155
  }
156
+
157
+ /* Remove rounded bottom-left corner on admin sidebar */
158
+ .bg-gradient-admin {
159
+ border-bottom-left-radius: 0;
160
+ }
156
161
  }
157
162
 
158
163
  /* Form input styles */
@@ -19,16 +19,15 @@ module Panda
19
19
 
20
20
  before_save :downcase_email
21
21
 
22
+ # Determine which column stores admin flag (supports legacy `admin` and new `is_admin`)
23
+ def self.admin_column
24
+ # Prefer canonical `admin` if available, otherwise fall back to legacy `is_admin`
25
+ @admin_column ||= column_names.include?("admin") ? "admin" : "is_admin"
26
+ end
27
+
22
28
  # Scopes
23
- # Support both 'admin' (newer) and 'is_admin' (older) column names
24
29
  scope :admins, -> {
25
- if column_names.include?("admin")
26
- where(admin: true)
27
- elsif column_names.include?("is_admin")
28
- where(is_admin: true)
29
- else
30
- none
31
- end
30
+ where(admin_column => true)
32
31
  }
33
32
 
34
33
  def self.find_or_create_from_auth_hash(auth_hash)
@@ -48,7 +47,7 @@ module Panda
48
47
  email: auth_hash.info.email.downcase,
49
48
  name: auth_hash.info.name || "Unknown User",
50
49
  image_url: auth_hash.info.image,
51
- is_admin: User.count.zero? # First user is admin
50
+ admin_column => User.count.zero? # First user is admin
52
51
  }
53
52
 
54
53
  user = create!(attributes)
@@ -62,10 +61,20 @@ module Panda
62
61
  end
63
62
 
64
63
  # Admin status check
65
- # Note: Column is named 'admin' in newer schemas, 'is_admin' in older ones
66
64
  def admin?
67
- self[:admin] || self[:is_admin] || false
65
+ ActiveRecord::Type::Boolean.new.cast(admin)
66
+ end
67
+
68
+ # Support both legacy `admin` and new `is_admin` columns
69
+ def admin
70
+ self[self.class.admin_column]
71
+ end
72
+
73
+ def admin=(value)
74
+ self[self.class.admin_column] = ActiveRecord::Type::Boolean.new.cast(value)
68
75
  end
76
+ alias_method :is_admin, :admin
77
+ alias_method :is_admin=, :admin=
69
78
 
70
79
  def active_for_authentication?
71
80
  true
@@ -0,0 +1,51 @@
1
+ {
2
+ "ignored_warnings": [
3
+ {
4
+ "warning_type": "Command Injection",
5
+ "warning_code": 14,
6
+ "fingerprint": "29d5b60b583fc98e293f6ac47f153bbf5db48a03963f8c9a7711bd251ecf2d81",
7
+ "check_name": "Execute",
8
+ "message": "Possible command injection",
9
+ "file": "lib/panda/core/testing/support/system/cuprite_helpers.rb",
10
+ "line": 78,
11
+ "link": "https://brakemanscanner.org/docs/warning_types/command_injection/",
12
+ "code": "system(\"ffmpeg -y -framerate 8 -pattern_type glob -i '#{File.join(dir, \"frames\")}/*.png' -c:v libx264 -pix_fmt yuv420p '#{capybara_artifacts_dir.join(\"#{example.metadata[:full_description].parameterize}.mp4\")}'\\n\")",
13
+ "render_path": null,
14
+ "location": {
15
+ "type": "method",
16
+ "class": "Panda::Core::Testing::CupriteHelpers",
17
+ "method": "record_video!"
18
+ },
19
+ "user_input": "File.join(dir, \"frames\")",
20
+ "confidence": "Medium",
21
+ "cwe_id": [
22
+ 77
23
+ ],
24
+ "note": "This does not use user input and is based on environment variables and the current folder"
25
+ },
26
+ {
27
+ "warning_type": "Command Injection",
28
+ "warning_code": 14,
29
+ "fingerprint": "d3c339f054cbd511956e04eaf8763bc722bd2e4559a1c1fec58749abef9792cd",
30
+ "check_name": "Execute",
31
+ "message": "Possible command injection",
32
+ "file": "lib/panda/core/testing/support/system/chrome_verification.rb",
33
+ "line": 43,
34
+ "link": "https://brakemanscanner.org/docs/warning_types/command_injection/",
35
+ "code": "Process.spawn(*[BrowserPath.resolve, v.nil? ? (\"--#{k}\") : (\"--#{k}=#{v}\"), \"about:blank\"], :out => (File::NULL), :err => (File::NULL))",
36
+ "render_path": null,
37
+ "location": {
38
+ "type": "method",
39
+ "class": "Panda::Core::Testing::Support::System::ChromeVerification",
40
+ "method": "s(:self).verify!"
41
+ },
42
+ "user_input": "k",
43
+ "confidence": "Medium",
44
+ "cwe_id": [
45
+ 77
46
+ ],
47
+ "note": "Chrome verification uses a fixed set of browser flags from CHROME_FLAGS constant; no user input is accepted"
48
+ }
49
+ ],
50
+ "brakeman_version": "7.1.1"
51
+ }
@@ -11,7 +11,7 @@ class CreatePandaCoreUsers < ActiveRecord::Migration[7.1]
11
11
  t.string :name
12
12
  t.string :email, null: false
13
13
  t.string :image_url
14
- t.boolean :is_admin, default: false, null: false
14
+ t.boolean :admin, default: false, null: false
15
15
  t.string :current_theme
16
16
  t.string :oauth_avatar_url
17
17
  t.timestamps
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RenameIsAdminToAdminInPandaCoreUsers < ActiveRecord::Migration[7.1]
4
+ def up
5
+ # If apps created an `is_admin` column during the brief rename, move it back
6
+ return unless column_exists?(:panda_core_users, :is_admin)
7
+ return if column_exists?(:panda_core_users, :admin)
8
+
9
+ rename_column :panda_core_users, :is_admin, :admin
10
+ end
11
+
12
+ def down
13
+ return unless column_exists?(:panda_core_users, :admin)
14
+ return if column_exists?(:panda_core_users, :is_admin)
15
+
16
+ rename_column :panda_core_users, :admin, :is_admin
17
+ end
18
+ end
@@ -1,3 +1,5 @@
1
+ require "active_support/core_ext/numeric/bytes"
2
+
1
3
  module Panda
2
4
  module Core
3
5
  class Configuration
@@ -7,6 +9,7 @@ module Panda
7
9
  :cache_store,
8
10
  :parent_controller,
9
11
  :parent_mailer,
12
+ :auto_mount_engine,
10
13
  :mailer_sender,
11
14
  :mailer_default_url_options,
12
15
  :session_token_cookie,
@@ -38,6 +41,7 @@ module Panda
38
41
  @cache_store = :memory_store
39
42
  @parent_controller = "ActionController::API"
40
43
  @parent_mailer = "ActionMailer::Base"
44
+ @auto_mount_engine = true
41
45
  @mailer_sender = "support@example.com"
42
46
  @mailer_default_url_options = {host: "localhost:3000"}
43
47
  @session_token_cookie = :panda_session
@@ -3,21 +3,20 @@
3
3
  module Panda
4
4
  module Core
5
5
  class Engine < ::Rails::Engine
6
- # Autoload paths configuration
7
6
  module AutoloadConfig
8
7
  extend ActiveSupport::Concern
9
8
 
10
9
  included do
11
- config.eager_load_namespaces << Panda::Core::Engine
10
+ # These must run BEFORE initialization, so this is allowed
11
+ config.autoload_paths << root.join("app/builders")
12
+ config.autoload_paths << root.join("app/components")
13
+ config.autoload_paths << root.join("app/services")
14
+ config.autoload_paths << root.join("app/models")
15
+ config.autoload_paths << root.join("app/helpers")
16
+ config.autoload_paths << root.join("app/constraints")
12
17
 
13
- # Add engine's app directories to autoload paths
14
- # Note: Only add the root directories, not nested subdirectories
15
- # Zeitwerk will automatically discover nested modules from these roots
16
- config.autoload_paths += Dir[root.join("app", "models")]
17
- config.autoload_paths += Dir[root.join("app", "controllers")]
18
- config.autoload_paths += Dir[root.join("app", "builders")]
19
- config.autoload_paths += Dir[root.join("app", "components")]
20
- config.autoload_paths += Dir[root.join("app", "services")]
18
+ # Mirror eager-load as needed
19
+ config.eager_load_paths.concat(config.autoload_paths)
21
20
  end
22
21
  end
23
22
  end
@@ -1,51 +1,97 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/concern"
4
+
3
5
  module Panda
4
6
  module Core
5
7
  class Engine < ::Rails::Engine
6
- # OmniAuth configuration
7
8
  module OmniauthConfig
8
9
  extend ActiveSupport::Concern
9
10
 
11
+ PROVIDER_REGISTRY = {
12
+ # Microsoft
13
+ "microsoft" => :microsoft_graph,
14
+ "microsoft_graph" => :microsoft_graph,
15
+
16
+ # Google
17
+ "google" => :google_oauth2,
18
+ "google_oauth2" => :google_oauth2,
19
+ "gmail" => :google_oauth2,
20
+
21
+ # GitHub
22
+ "github" => :github,
23
+ "gh" => :github,
24
+
25
+ # Developer
26
+ "developer" => :developer
27
+ }.freeze
28
+
10
29
  included do
11
- initializer "panda_core.omniauth" do |app|
12
- # Load OAuth provider gems
13
- require_relative "../oauth_providers"
14
- Panda::Core::OAuthProviders.setup
15
-
16
- # Mount OmniAuth at configurable admin path
17
- app.middleware.use OmniAuth::Builder do
18
- # Configure OmniAuth to use the configured admin path
19
- configure do |config|
20
- config.path_prefix = "#{Panda::Core.config.admin_path}/auth"
21
- # POST-only for CSRF protection (CVE-2015-9284)
22
- # All login forms use POST via form_tag method: "post"
23
- config.allowed_request_methods = [:post]
24
- end
25
-
26
- Panda::Core.config.authentication_providers.each do |provider_name, settings|
27
- # Build provider options, allowing custom path name override
28
- provider_options = settings[:options] || {}
29
-
30
- # If path_name is specified, use it to override the default strategy name in URLs
31
- if settings[:path_name].present?
32
- provider_options = provider_options.merge(name: settings[:path_name])
33
- end
34
-
35
- case provider_name.to_s
36
- when "microsoft_graph"
37
- provider :microsoft_graph, settings[:client_id], settings[:client_secret], provider_options
38
- when "google_oauth2"
39
- provider :google_oauth2, settings[:client_id], settings[:client_secret], provider_options
40
- when "github"
41
- provider :github, settings[:client_id], settings[:client_secret], provider_options
42
- when "developer"
43
- provider :developer if Rails.env.development?
44
- end
45
- end
30
+ if respond_to?(:initializer)
31
+ initializer "panda_core.omniauth" do |app|
32
+ require_relative "../oauth_providers"
33
+ Panda::Core::OAuthProviders.setup
34
+
35
+ load_yaml_provider_overrides!
36
+ configure_omniauth_globals
37
+ mount_omniauth_middleware(app)
46
38
  end
47
39
  end
48
40
  end
41
+
42
+ private
43
+
44
+ # 1. YAML overrides
45
+ def load_yaml_provider_overrides!
46
+ path = Panda::Core::Engine.root.join("config/providers.yml")
47
+ return unless File.exist?(path)
48
+
49
+ yaml = YAML.load_file(path) || {}
50
+ (yaml["providers"] || {}).each do |name, settings|
51
+ Panda::Core.config.authentication_providers[name.to_s] ||= {}
52
+ Panda::Core.config.authentication_providers[name.to_s].deep_merge!(settings)
53
+ end
54
+ end
55
+
56
+ # 2. Global settings
57
+ def configure_omniauth_globals
58
+ OmniAuth.configure do |c|
59
+ c.allowed_request_methods = [:post]
60
+ c.path_prefix = "#{Panda::Core.config.admin_path}/auth"
61
+ end
62
+ end
63
+
64
+ # 3. Middleware insertion
65
+ def mount_omniauth_middleware(app)
66
+ ctx = self # Capture the Engine/Concern context
67
+
68
+ Panda::Core::Middleware.use(app, OmniAuth::Builder) do
69
+ Panda::Core.config.authentication_providers.each do |name, settings|
70
+ ctx.send(:configure_provider, self, name, settings)
71
+ end
72
+ end
73
+ end
74
+
75
+ # 4. Provider builder
76
+ def configure_provider(builder, name, settings)
77
+ symbol = PROVIDER_REGISTRY[name.to_s]
78
+
79
+ unless symbol
80
+ Rails.logger.warn("[panda-core] Unknown OmniAuth provider: #{name.inspect}")
81
+ return
82
+ end
83
+
84
+ return if symbol == :developer && !Rails.env.development?
85
+
86
+ options = (settings[:options] || {}).dup
87
+ options[:name] = settings[:path_name] if settings[:path_name].present?
88
+
89
+ if settings[:client_id] && settings[:client_secret]
90
+ builder.provider symbol, settings[:client_id], settings[:client_secret], options
91
+ else
92
+ builder.provider symbol, options
93
+ end
94
+ end
49
95
  end
50
96
  end
51
97
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Panda
4
+ module Core
5
+ class Engine < ::Rails::Engine
6
+ # Automatically mount the engine into host applications
7
+ module RouteConfig
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ # Append Core routes to the host app after initialization so
12
+ # engine routes are available without manual mounting.
13
+ config.after_initialize do |app|
14
+ next unless Panda::Core.config.auto_mount_engine
15
+
16
+ route_set = app.routes
17
+ already_mounted =
18
+ route_set.routes.any? do |route|
19
+ route.app == Panda::Core::Engine ||
20
+ (route.app.respond_to?(:app) && route.app.app == Panda::Core::Engine)
21
+ end
22
+ already_mounted ||= route_set.named_routes.key?(:panda_core)
23
+
24
+ next if already_mounted
25
+
26
+ route_set.append do
27
+ # Re-check inside the mapper to avoid duplicate mounts during reloads
28
+ next if route_set.named_routes.key?(:panda_core)
29
+
30
+ mount Panda::Core::Engine => "/", as: "panda_core"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,82 +1,86 @@
1
- require "rubygems"
2
- require "stringio"
3
- require "rails/engine"
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
4
  require "omniauth"
5
5
 
6
- # Silence ActiveSupport::Configurable deprecation from omniauth-rails_csrf_protection
7
- # This gem uses the deprecated module but hasn't been updated yet
8
- # Issue: https://github.com/cookpad/omniauth-rails_csrf_protection/issues/23
9
- # This can be removed once the gem is updated or Rails 8.2 is released
10
- #
11
- # We suppress the warning by temporarily redirecting stderr since
12
- # ActiveSupport::Deprecation.silence was removed in Rails 8.1
13
- original_stderr = $stderr
14
- $stderr = StringIO.new
15
- begin
16
- require "omniauth/rails_csrf_protection"
17
- ensure
18
- $stderr = original_stderr
19
- end
6
+ require "panda/core/middleware"
7
+ require "panda/core/module_registry"
20
8
 
21
- # Load shared configuration modules
9
+ # Shared engine mixins
22
10
  require_relative "shared/inflections_config"
23
11
  require_relative "shared/generator_config"
24
12
 
25
- # Load engine configuration modules
13
+ # Engine mixins
26
14
  require_relative "engine/autoload_config"
27
- require_relative "engine/middleware_config"
28
15
  require_relative "engine/importmap_config"
29
16
  require_relative "engine/omniauth_config"
30
17
  require_relative "engine/phlex_config"
31
18
  require_relative "engine/admin_controller_config"
32
-
33
- # Load module registry
34
- require_relative "module_registry"
19
+ require_relative "engine/route_config"
35
20
 
36
21
  module Panda
37
22
  module Core
38
23
  class Engine < ::Rails::Engine
39
24
  isolate_namespace Panda::Core
40
25
 
41
- # Include shared configuration modules
26
+ #
27
+ # Include shared behaviours
28
+ #
42
29
  include Shared::InflectionsConfig
43
30
  include Shared::GeneratorConfig
44
31
 
45
- # Include engine-specific configuration modules
32
+ #
33
+ # Include engine-level concerns
34
+ #
46
35
  include AutoloadConfig
47
- include MiddlewareConfig
48
36
  include ImportmapConfig
49
37
  include OmniauthConfig
50
38
  include PhlexConfig
51
39
  include AdminControllerConfig
40
+ include RouteConfig
52
41
 
53
- initializer "panda_core.config" do |app|
54
- # Configuration is already initialized with defaults in Configuration class
42
+ #
43
+ # Misc configuration point
44
+ #
45
+ initializer "panda_core.configuration" do
46
+ # Intentionally quiet — used as a stable anchor point
55
47
  end
56
48
 
57
- # Static asset middleware for serving public files and JavaScript modules
58
- # Must run before Propshaft to intercept /panda/* requests, but we can't
59
- # guarantee Propshaft is in the host application, so just insert it
60
- # high up in the middleware stack
61
- initializer "panda.core.static_assets" do |app|
62
- # Serve public assets (CSS, images, etc.)
63
- app.config.middleware.insert_before ActionDispatch::Static, Rack::Static,
49
+ #
50
+ # Static asset handling for:
51
+ # /panda-core-assets
52
+ #
53
+ initializer "panda_core.static_assets" do |app|
54
+ Panda::Core::Middleware.use(
55
+ app,
56
+ Rack::Static,
64
57
  urls: ["/panda-core-assets"],
65
58
  root: Panda::Core::Engine.root.join("public"),
66
59
  header_rules: [
67
- # Disable caching in development for instant CSS updates
68
- [:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000"}]
60
+ [
61
+ :all,
62
+ {
63
+ "Cache-Control" =>
64
+ Rails.env.development? ?
65
+ "no-cache, no-store, must-revalidate" :
66
+ "public, max-age=31536000"
67
+ }
68
+ ]
69
69
  ]
70
+ )
70
71
 
71
- # Use ModuleRegistry's custom middleware to serve JavaScript from all registered modules
72
- # This middleware checks all modules and serves from the first matching location
73
- app.config.middleware.insert_before ActionDispatch::Static, Panda::Core::ModuleRegistry::JavaScriptMiddleware
72
+ Panda::Core::Middleware.use(
73
+ app,
74
+ Panda::Core::ModuleRegistry::JavaScriptMiddleware
75
+ )
74
76
  end
75
77
  end
76
78
  end
77
79
  end
78
80
 
79
- # Register Core module with ModuleRegistry for JavaScript serving
81
+ #
82
+ # Register engine with ModuleRegistry
83
+ #
80
84
  Panda::Core::ModuleRegistry.register(
81
85
  gem_name: "panda-core",
82
86
  engine: "Panda::Core::Engine",
@@ -85,6 +89,7 @@ Panda::Core::ModuleRegistry.register(
85
89
  components: "app/components/panda/core/**/*.rb",
86
90
  helpers: "app/helpers/panda/core/**/*.rb",
87
91
  views: "app/views/panda/core/**/*.erb",
92
+ layouts: "app/views/layouts/panda/core/**/*.erb",
88
93
  javascripts: "app/assets/javascript/panda/core/**/*.js"
89
94
  }
90
95
  )