diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 88273be3..00000000
--- a/.gitignore
+++ /dev/null
@@ -1,15 +0,0 @@
-*.gem
-*.sw?
-.DS_Store
-.bundle/*
-.idea
-.rvmrc
-Gemfile.lock
-coverage
-junk.*
-pkg
-rdoc
-vendor/bundle
-gemfiles/.bundle
-gemfiles/vendor
-gemfiles/*.lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index f7eff493..00000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,417 +0,0 @@
-# active_hash Changelog
-
-## Version [4.0.0] - 2025-07-30
-
-### Added
-
-- Add i18n support [#230](https://github.com/active-hash/active_hash/pull/230) @ryu-sato @Yuki-Inoue
-- Add `column_names` method [#311](https://github.com/active-hash/active_hash/pull/311) @hatsu38
-- Add block support to `count` [#317](https://github.com/active-hash/active_hash/pull/317) @ashleyHutton
-- Support ruby 3.4 [#328](https://github.com/active-hash/active_hash/pull/328) @flavorjones
-- Add `:alias` to `has_many :through` [#329](https://github.com/active-hash/active_hash/pull/329) @alexgriff
-- Add Active Record 8.0 [#324](https://github.com/active-hash/active_hash/pull/324) @flavorjones
-
-### Fixed
-
-- Fix Do not suppress load errors[#309](https://github.com/active-hash/active_hash/pull/309) @andreynering
-- Ensure `field_names` are all strings [#312](https://github.com/active-hash/active_hash/pull/312) @flavorjones
-- Hide private `add_default_value` [#314](https://github.com/active-hash/active_hash/pull/314) @kbrock
-- Fix `exists?(nil)` [#320](https://github.com/active-hash/active_hash/pull/320) @y-yagi
-- Enance Enum support [#321](https://github.com/active-hash/active_hash/pull/321) @hatsu38
-- Updated docs [#326](https://github.com/active-hash/active_hash/pull/326) @y-yagi
-
-### Removed
-
-- Drop Active Record < 6.1. Ruby < 3.0 [#324](https://github.com/active-hash/active_hash/pull/324) @flavorjones
-
-## Version [3.3.1] - 2024-05-03
-
-### Fixed
-
-- Fix `hash_many :through` associations which specify a scope. [#306](https://github.com/active-hash/active_hash/pull/306) @sontixyou
-
-
-## Version [3.3.0] - 2024-04-30
-
-### Added
-
-- Ruby 3.3 support [#298](https://github.com/active-hash/active_hash/pull/298) @m-nakamura145
-- Support `has_many :through` associations [#296](https://github.com/active-hash/active_hash/pull/296) @flavorjones
-- Rails 7.1 support [#291](https://github.com/active-hash/active_hash/pull/291) @y-yagi
-
-### Fixed
-
-- Rails 7.1: fix sqlite3 issue [#303](https://github.com/active-hash/active_hash/pull/303) @flavorjones
-- Rails 7.1.3: add missing `has_query_constraints?` [#300](https://github.com/active-hash/active_hash/pull/300) @flavorjones
-- `Array#pluck` supports methods [#299](https://github.com/active-hash/active_hash/pull/299) @iberianpig
-- Prefer `safe_constantize` over `constantize` [#297](https://github.com/active-hash/active_hash/pull/297) @flavorjones
-- Treat `nil` and `blank?` as different values [#295](https://github.com/active-hash/active_hash/pull/295) @kbrock
-- Fix `#where` for string keys [#292](https://github.com/active-hash/active_hash/pull/292) @usernam3
-
-## Version [3.2.1] - 2023-08-31
-
-### Added
-
-- Improve `pp` output for `ActiveHash::Relation`. [#288](https://github.com/active-hash/active_hash/pull/288) @flavorjones
-
-### Fixed
-
-- Fix relation matching when attribute name collides with a method. [#281](https://github.com/active-hash/active_hash/pull/281) @flavorjones
-- Fix association reflection in applications that don't use ActiveHash::Associations. [#286](https://github.com/active-hash/active_hash/pull/286) @iberianpig
-- Fix `ActiveHash::Relation#method_missing` and `#respond_to_missing?` without scopes. [#278](https://github.com/active-hash/active_hash/pull/278) @julianrubisch
-
-
-## Version [3.2.0] - 2023-05-06
-
-- Add Ruby 3.2 to the CI matrix [#275](https://github.com/active-hash/active_hash/pull/275) @petergoldstein
-- Handle default value of `false` [#274](https://github.com/active-hash/active_hash/pull/274) @ihollander
-- Run CI only one time per commit [#273](https://github.com/active-hash/active_hash/pull/273) @flavorjones
-- Rails 7 support [#272](https://github.com/active-hash/active_hash/pull/272) @flavorjones
-- Avoid interfere with AR's belongs_to arguments. [#270](https://github.com/active-hash/active_hash/pull/270) @koyo-miyamura
-- Fix broken #pluck method with 3+ attrs specified [#269](https://github.com/active-hash/active_hash/pull/269) @h6ah4i
-- Fix relations for Rails 7 support, and not modifying conditions [#268](https://github.com/active-hash/active_hash/pull/268) @pfeiffer
-- docs: Remove the string 'F' [#264](https://github.com/active-hash/active_hash/pull/264) @tbotaq
-- Show example using regex in where query [#263](https://github.com/active-hash/active_hash/pull/263) @scottharvey
-- Improve performance of exists? [#262](https://github.com/active-hash/active_hash/pull/262) @ise-tang
-- Remove redundant ActiveRecord version check [#260](https://github.com/active-hash/active_hash/pull/260) @yujideveloper
-- Fix deprecation warnings [#259](https://github.com/active-hash/active_hash/pull/259) @yujideveloper
-- Fix rspec config when SKIP_ACTIVE_RECORD enabled [#258](https://github.com/active-hash/active_hash/pull/258) @yujideveloper
-- isolate tests with temporary classes [#256](https://github.com/active-hash/active_hash/pull/256) @machisuke
-- Avoid ActiveRecordExtensions affects AR's belongs_to method. [#255](https://github.com/active-hash/active_hash/pull/255) @machisuke
-- add option to disable erb parsing [#202](https://github.com/active-hash/active_hash/pull/202) @reedlaw
-- add collection singular ids for associations [#237](https://github.com/active-hash/active_hash/pull/237) @1160054
-- Fix the thread-safe spec for the updated cities fixture @adampal
-- Add thread-safety to ActiveFile [#229](https://github.com/active-hash/active_hash/pull/229) @dmitriy-kiriyenko
-
-## Version [3.1.1] - 2022-07-14
-
- - Make scopes chainable [#248](https://github.com/active-hash/active_hash/pull/248) @andreynering
- - Set default key attributes [#251](https://github.com/active-hash/active_hash/pull/251/commits/68a0a121d110ac83f4bbf0024f027714fd24debf) @adampal
- - Migrate from Travis to GitHub Actions for CI @kbrock
- - Add primary_key support for has_one [#218](https://github.com/active-hash/active_hash/pull/218) @yujideveloper
- - Return a chainable relation when using .not [#205](https://github.com/active-hash/active_hash/pull/205) @pfeiffer
- - Correct fields with YAML aliases in array style [#226](https://github.com/active-hash/active_hash/pull/226) @stomk
- - Add ActiveHash::Relation#size method for compatibily [#227](https://github.com/active-hash/active_hash/pull/227) @sluceno
- - Implement ActiveRecord::RecordNotFound interface [#207](https://github.com/active-hash/active_hash/pull/207) @ChrisBr
- - Fix find_by_id with filter chain [#210](https://github.com/active-hash/active_hash/pull/210) @ChrisBr
- - Suppress Ruby 2.7 kwargs warnings [#206](https://github.com/active-hash/active_hash/pull/206) @yhirano55
- - Call reload if @records is not defined [#208](https://github.com/active-hash/active_hash/pull/208) @jonmagic
- - Switch to rspec3 (and update the Gemfile) [#209](https://github.com/active-hash/active_hash/pull/209) @djberg96
- - Implement filter by RegEx [#211](https://github.com/active-hash/active_hash/pull/211) @ChrisBr
- - Supports .pick method [#195](https://github.com/active-hash/active_hash/pull/195/files) @yhirano55
- - Lots of other small performance improvements, documentation and testing. Thanks to everyone who contributed!
-
-## Version [3.1.0] - 2020-01-15
-
- - Add ActiveHash::Base.order method inspired by ActiveRecord [#177](https://github.com/active-hash/active_hash/pull/177)
- - Add #to_ary to ActiveHash::Relation [#182](https://github.com/active-hash/active_hash/pull/182)
- - Allow #find to behave like Enumerable#find if id is nil and a block is given [#183](https://github.com/active-hash/active_hash/pull/183)
- - Delegate :sample to `records` [#189](https://github.com/active-hash/active_hash/pull/189)
-
-## Version [3.0.0] - 2019-09-28
-
- - Make #where chainable [#178](https://github.com/active-hash/active_hash/pull/178)
-
-## Version [2.3.0] - 2019-09-28
-
- - Add ::scope method (inspired by ActiveRecord) [#173](https://github.com/active-hash/active_hash/pull/173)
- - Let `.find(nil)` raise ActiveHash::RecordNotFound (inspired by ActiveRecord) [#174](https://github.com/active-hash/active_hash/pull/174)
- - `where` clause now works with range argument [#175](https://github.com/active-hash/active_hash/pull/175)
-
-## Version [2.2.1] - 2019-03-06
-
- - Allow empty YAML [#171](https://github.com/active-hash/active_hash/pull/171) Thanks, @ppworks
-
-## Version [2.2.0] - 2018-11-22
-
- - Support pluck method [#164](https://github.com/active-hash/active_hash/pull/164) Thanks, @ihatov08
- - Support where.not method [#167](https://github.com/active-hash/active_hash/pull/167) Thanks, @DialBird
-
-## Version [2.1.0] - 2018-04-05
-
- - Allow to use ERB (embedded ruby) in yml files [#160](https://github.com/active-hash/active_hash/pull/160) Thanks, @UgoMare
- - Add `ActiveHash::Base.polymorphic_name` [#162](https://github.com/active-hash/active_hash/pull/162)
- - Fix to be able to use enum accessor constant with same name as top-level constant[#161](https://github.com/active-hash/active_hash/pull/161) Thanks, @yujideveloper
-
-## Version [2.0.0] - 2018-02-27
-
- - Drop old Ruby and Rails support [#157](https://github.com/active-hash/active_hash/pull/157)
- - Don't generate instance accessors for class attributes [#136](https://github.com/active-hash/active_hash/pull/136) Thanks, @rainhead
-
-## Version [1.5.3] - 2017-06-14
-
- - Support symbol values in where and find_by [#156](https://github.com/active-hash/active_hash/pull/156) Thanks, @south37
-
-## Version [1.5.2] - 2017-06-14
-
- - Fix find_by when passed an invalid id [#152](https://github.com/active-hash/active_hash/pull/152) Thanks, @davidstosik
-
-## Version [1.5.1] - 2017-04-20
-
- - Fix a bug on `.where` [#147](https://github.com/active-hash/active_hash/pull/147)
-
-## Version [1.5.0] - 2017-03-24
-
- - add support for `.find_by!`(@syguer)
-
-## Version [1.4.1] - 2015-09-13
-
- - fix bug where `#attributes` didn't contain default values [#107](https://github.com/active-hash/active_hash/pull/107)
- - add support for `.find_by` and `#_read_attribute`. Thanks, @andrewfader
-
-## Version [1.4.0] - 2014-09-03
-
- - support Rails 4.2 @agraves, @al2o3cr
-
-## Version [1.3.0] - 2014-02-18
-
- - fix bug where including ActiveHash associations would make `belongs_to :imageable, polymorphic: true` blow up
- - fixed several bugs that prevented active hash from being used without active record / active model
- - add support for splitting up data sources into multiple files @rheaton
- - add support for storing data in json files @rheaton
-
-## Version [1.2.3] - 2013-11-29
-
- - fix bug where active hash would call `.all` on models when setting has_many @grosser
-
-## Version [1.2.2] - 2013-11-05
-
- - fix bug in gemspec that made it impossible to use w/ Rails 4
-
-## Version [1.2.1] - 2013-10-24
-
- - fixed nasty bug in belongs_to that would prevent users from passing procs @freebird0221
- - fixed bug where passing in a separate class name to belongs_to_active_hash would raise an exception @mauriciopasquier
-
-## Version [1.2.0] - 2013-10-01
-
- - belongs_to is back!
- - added support for primary key options for belongs_to @tomtaylor
-
-## Version [1.0.2] - 2013-09-09
-
- - `where(nil)` returns all results, like ActiveRecord @kugaevsky
-
-## Version [1.0.1] - 2013-07-15
-
- - Travis CI for ActiveHash + Ruby 2, 1.8.7, Rubinius and JRuby support @mattheworiordan
- - no longer need to call .all before executing `find_by_*` or `where` methods @mattheworiordan
-
-## Version [1.0.0] - 2013-06-24
-
- - save is a no-op on existing records, instead of raising an error (issue #63)
-
-## Version [0.10.0] - 2013-06-24
-
- - added ActiveYaml::Aliases module so you can DRY up your repetitive yaml @brett-richardson
-
-## Version [0.9.14] - 2013-05-23
-
- - enum_accessor can now take multiple field names when generating the constant
- - temporarily disabled rails edge specs since there's an appraisal issue with minitest
-
-## Version [0.9.13] 2013-01-22
- - Fix find_by_id and find method returning nil unless .all called in ActiveYaml @mattheworiordan
-
-## Version [0.9.12] 2012-07-25
- - Make find_by_id lookups faster by indexing records by id @desmondmonster
-
-## Version [0.9.11] 2012-07-16
- - Validate IDs are unique by caching them in a set @desmondmonster
-
-## Version [0.9.10] 2012-04-14
- - Support for has_one associations @kbrock
-
-## Version [0.9.9] 2012-04-05
-
- - Allow gems like simple_form to read metadata about belongs_to associations that point to active hash objects @kbrock
- - Move specs to appraisal @flavorjones
-
-## Version [0.9.8] - 2012-01-18
-
- - Make ActiveHash.find with array raise an exception when record cannot be found @mocoso
-
-## Version [0.9.7] - 2011-09-18
-
- - Fixing the setting of a `belongs_to_active_hash` association by association (not id).
-
-## Version [0.9.6] - 2011-08-31
- - added a module which adds a .belongs_to_active_hash method to ActiveRecord, since it was broken for Rails 3.1 @felixclack
-
-## Version [0.9.5] - 2011-06-07
- - fixed bug where .find would not work if you defined your ids as strings
-
-## Version [0.9.4] - 2011-06-05
- - fixed deprecation warnings for class_inheritable_accessor @scudco
- - added basic compatibility with the `where` method from Arel @rgarver
-
-## Version [0.9.3] - 2011-04-19
- - better dependency management and compatibility with ActiveSupport 2.x @vandrijevik
-
-## Version [0.9.2] - 2011-01-22
- - improved method_missing errors for dynamic finders
- - prevent users from trying to overwrite :attributes [#33](https://github.com/active-hash/active_hash/issues/33)
-
-## Version [0.9.1] 2010-12-08
- - ruby 1.9.2 compatibility
-
-## Version [0.9.0] 2010-12-06
- - added dependency on ActiveModel
- - add persisted? method to ActiveHash::Base
- - ActiveHash::Base#save takes \*args to be compatible with ActiveModel
- - ActiveHash::Base#to_param returns nil if the object hasn't been saved
-
-## Version [0.8.7] 2010-11-09
- - Use Ruby's definition of "word character" (numbers, underscores) when forming ActiveHash::Enum constants @tstuart
-
-## Version [0.8.6] 2010-11-07
- - Get ActiveHash::Associations to return a scope for has_many active record relationships @mocoso
-
-## Version [0.8.5] 2010-10-20
- - Allow find_by_* methods to accept an options hash, so rails associations don't blow up
-
-## Version [0.8.4] 2010-10-07
- - Add conditions to ActiveHash#all (Ryan Garver)
- - Add #cache_key to ActiveHash::Base (Tom Stuart)
- - Add banged dynamic finder support to ActiveHash::Base (Tom Stuart)
-
-## Version [0.8.3] 2010-09-16
- - Enum format now uses underscores instead of removing all characters
- - Removed test dependency on acts_as_fu
-
-## Version [0.8.2] 2010-05-26
- - Silence metaclass deprecation warnings in active support 2.3.8
-
-## Version [0.8.1] 2010-05-04
- - When calling ActiveFile::Base.reload do not actually perform the reload if nothing has been modified unless you call reload(true) to force (Michael Schubert)
-
-## Version [0.8.0] 2010-04-25
- - When ActiveRecord model belongs_to an ActiveHash and the associated id is nil, returns nil instead of raising RecordNotFound (Jeremy Weiskotten)
- - Merged Nakajima's "add" alias for "create" - gotta save those ASCII characters :)
-
-## Version [0.7.9] 2010-03-01
- - Removed "extend"-related deprecations - they didn't play well with rails class loading
-
-## Version [0.7.8] 2010-01-18
- - Added stub for #destroyed? method, since Rails associations now depend on it
-
-## Version [0.7.7] 2009-12-19
- - Deprecated include ActiveHash::Associations in favor of extend ActiveHash::Associations
-
-## Version [0.7.6] 2009-12-19
- - Added ActiveHash::Enum (John Pignata)
- - Fixed bug where you can't set nil to an association
- - Calling #belongs_to now creates the underlying field if it's not already there (belongs_to :city will create the :city_id field)
-
-## Version [0.7.5] 2009-12-10
- - Fixed a bug where belongs_to associations would raise an error instead of returning nil when the parent object didn't exist.
- - Added #[] and #[]= accessors for more ActiveRecord-esque-ness. (Pat Nakajima & Dave Yeu)
-
-## Version [0.7.4] 2009-12-01
- - Add marked_for_destruction? to be compatible with nested attributes (Brandon Keene)
- - Added second parameter to respond_to? and cleaned up specs (Brian Takita)
- - Find with an id that does not exist now raises a RecordNotFound exception to mimic ActiveRecord (Pat Nakajima)
-
-## Version [0.7.3] 2009-10-22
- - added setters to ActiveHash::Base for all fields
- - instantiating an ActiveHash object with a hash calls the setter methods on the object
- - boolean default values now work
-
-## Version [0.7.2] 2009-10-21
- - Removed auto-reloading of files based on mtime - maybe it will come back later
- - Made ActiveFile::Base.all a bit more sane
-
-## Version 0.7.1 2009-10-13
- - added ActiveHash::Base.has_many, which works with ActiveRecord or ActiveHash classes @baldwindavid
- - added ActiveHash::Base.belongs_to, which works with ActiveRecord or ActiveHash classes @baldwindavid
- - added .delete_all method that clears the in-memory array
- - added support for Hash-style yaml (think, Rails fixtures)
- - added setter for parent object on belongs_to ( `city = City.new; city.state = State.first; city.state_id == State.first.id` )
-
-## Version [0.7.0] 2009-10-12
- - auto-assign fields after calling data= instead of after calling .all
- - remove require 'rubygems', so folks with non-gem setups can still use AH
- - added more specific activesupport dependency to ensure that metaclass is available
- - AH no longer calls to_i on ids. If you pass in a string as an id, you'll get a string back
- - Fancy finders, such as find_all_by_id_and_name, will compare the to_s values of the fields, so you can pass in strings
- - You can now use ActiveHash models as the parents of polymorphic belongs_to associations
- - save, save!, create and create! now add items to the in-memory collection, and naively adds autoincrementing id
- - new_record? returns false if the record is part of the collection
- - ActiveHash now works with Fixjour!
-
-## Version [0.6.1] 2009-08-19
- - Added custom finders for multiple fields, such as .find_all_by_name_and_age
-
-## Version 0.5.0 2009-07-23
- - Added support for auto-defining methods based on hash keys in ActiveHash::Base
- - Changed the :field and :fields API so that they don't overwrite existing methods (useful when ActiveHash auto-defines methods)
- - Fixed a bug where ActiveFile incorrectly set the root_path to be the path in the gem directory, not the current working directory
-
-## Version 0.4.0 2009-07-24
- - ActiveFile no longer reloads files by default
- - Added ActiveFile.reload_active_file= so you can cause ActiveFile to reload
- - Setting data to nil correctly causes .all to return an empty array
- - Added reload(force) method, so that you can force a reload from files in ActiveFile, useful for tests
-
-[HEAD]: https://github.com/active-hash/active_hash/compare/v3.4.0...HEAD
-[3.3.2]: https://github.com/active-hash/active_hash/compare/v3.3.1...v3.4.0
-[3.3.1]: https://github.com/active-hash/active_hash/compare/v3.3.0...v3.3.1
-[3.3.0]: https://github.com/active-hash/active_hash/compare/v3.2.1...v3.3.0
-[3.2.1]: https://github.com/active-hash/active_hash/compare/v3.2.0...v3.2.1
-[3.2.0]: https://github.com/active-hash/active_hash/compare/v3.1.1...v3.2.0
-[3.1.1]: https://github.com/active-hash/active_hash/compare/v3.1.0...v3.1.1
-[3.1.0]: https://github.com/active-hash/active_hash/compare/v3.0.0...v3.1.0
-[3.0.0]: https://github.com/active-hash/active_hash/compare/v2.3.0...v3.0.0
-[2.3.0]: https://github.com/active-hash/active_hash/compare/v2.2.1...v2.3.0
-[2.2.1]: https://github.com/active-hash/active_hash/compare/v2.2.0...v2.2.1
-[2.2.0]: https://github.com/active-hash/active_hash/compare/v2.1.0...v2.2.0
-[2.1.0]: https://github.com/active-hash/active_hash/compare/v2.0.0...v2.1.0
-[2.0.0]: https://github.com/active-hash/active_hash/compare/v1.5.3...v2.0.0
-[1.5.3]: https://github.com/active-hash/active_hash/compare/v1.5.2...v1.5.3
-[1.5.2]: https://github.com/active-hash/active_hash/compare/v1.5.1...v1.5.2
-[1.5.1]: https://github.com/active-hash/active_hash/compare/v1.5.0...v1.5.1
-[1.5.0]: https://github.com/active-hash/active_hash/compare/v1.4.1...v1.5.0
-[1.4.1]: https://github.com/active-hash/active_hash/compare/v1.4.0...v1.4.1
-[1.4.0]: https://github.com/active-hash/active_hash/compare/v1.3.0...v1.4.0
-[1.3.0]: https://github.com/active-hash/active_hash/compare/v1.2.3...v1.3.0
-[1.2.3]: https://github.com/active-hash/active_hash/compare/v1.2.2...v1.2.3
-[1.2.2]: https://github.com/active-hash/active_hash/compare/v1.2.1...v1.2.2
-[1.2.1]: https://github.com/active-hash/active_hash/compare/v1.2.0...v1.2.1
-[1.2.0]: https://github.com/active-hash/active_hash/compare/v1.0.2...v1.2.0
-[1.0.2]: https://github.com/active-hash/active_hash/compare/v1.0.1...v1.0.2
-[1.0.1]: https://github.com/active-hash/active_hash/compare/v1.0.0...v1.0.1
-[1.0.0]: https://github.com/active-hash/active_hash/compare/v0.10.0...v1.0.0
-[0.10.0]: https://github.com/active-hash/active_hash/compare/v0.9.14...v0.10.0
-[0.9.14]: https://github.com/active-hash/active_hash/compare/v0.9.13...v0.9.14
-[0.9.13]: https://github.com/active-hash/active_hash/compare/v0.9.12...v0.9.13
-[0.9.12]: https://github.com/active-hash/active_hash/compare/v0.9.11...v0.9.12
-[0.9.11]: https://github.com/active-hash/active_hash/compare/v0.9.10...v0.9.11
-[0.9.10]: https://github.com/active-hash/active_hash/compare/v0.9.9...v0.9.10
-[0.9.9]: https://github.com/active-hash/active_hash/compare/v0.9.8...v0.9.9
-[0.9.8]: https://github.com/active-hash/active_hash/compare/v0.9.7...v0.9.8
-[0.9.7]: https://github.com/active-hash/active_hash/compare/v0.9.6...v0.9.7
-[0.9.6]: https://github.com/active-hash/active_hash/compare/v0.9.5...v0.9.6
-[0.9.5]: https://github.com/active-hash/active_hash/compare/v0.9.4...v0.9.5
-[0.9.4]: https://github.com/active-hash/active_hash/compare/v0.9.3...v0.9.4
-[0.9.3]: https://github.com/active-hash/active_hash/compare/v0.9.2...v0.9.3
-[0.9.2]: https://github.com/active-hash/active_hash/compare/v0.9.1...v0.9.2
-[0.9.1]: https://github.com/active-hash/active_hash/compare/v0.9.0...v0.9.1
-[0.9.0]: https://github.com/active-hash/active_hash/compare/v0.8.7...v0.9.0
-[0.8.7]: https://github.com/active-hash/active_hash/compare/v0.8.6...v0.8.7
-[0.8.6]: https://github.com/active-hash/active_hash/compare/v0.8.5...v0.8.6
-[0.8.5]: https://github.com/active-hash/active_hash/compare/v0.8.4...v0.8.5
-[0.8.4]: https://github.com/active-hash/active_hash/compare/v0.8.3...v0.8.4
-[0.8.3]: https://github.com/active-hash/active_hash/compare/v0.8.2...v0.8.3
-[0.8.2]: https://github.com/active-hash/active_hash/compare/v0.8.1...v0.8.2
-[0.8.1]: https://github.com/active-hash/active_hash/compare/v0.8.0...v0.8.1
-[0.8.0]: https://github.com/active-hash/active_hash/compare/v0.7.9...v0.8.0
-[0.7.9]: https://github.com/active-hash/active_hash/compare/v0.7.8...v0.7.9
-[0.7.8]: https://github.com/active-hash/active_hash/compare/v0.7.7...v0.7.8
-[0.7.7]: https://github.com/active-hash/active_hash/compare/v0.7.6...v0.7.7
-[0.7.6]: https://github.com/active-hash/active_hash/compare/v0.7.5...v0.7.6
-[0.7.5]: https://github.com/active-hash/active_hash/compare/v0.7.4...v0.7.5
-[0.7.4]: https://github.com/active-hash/active_hash/compare/v0.7.3...v0.7.4
-[0.7.3]: https://github.com/active-hash/active_hash/compare/v0.7.2...v0.7.3
-[0.7.2]: https://github.com/active-hash/active_hash/compare/v0.7.0...v0.7.2
-[0.7.0]: https://github.com/active-hash/active_hash/compare/v0.6.1...v0.7.0
-[0.6.1]: https://github.com/active-hash/active_hash/compare/v0.6.0...v0.6.1
diff --git a/Gemfile b/Gemfile
deleted file mode 100644
index 86d84b3c..00000000
--- a/Gemfile
+++ /dev/null
@@ -1,18 +0,0 @@
-source "http://rubygems.org/"
-
-gemspec
-
-gem 'rspec', '~> 3.9'
-gem 'rake'
-gem 'test-unit'
-gem 'json'
-
-platforms :jruby do
- gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6'
-end
-
-platforms :ruby do
- gem 'sqlite3', '~> 1.4', '< 2.0' # can allow 2.0 once Rails's sqlite adapter allows it
-end
-
-gem 'activerecord', '>= 6.1.0'
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index e4ee32a2..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2009 Jeff Dean
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Rakefile b/Rakefile
deleted file mode 100644
index 2697bc46..00000000
--- a/Rakefile
+++ /dev/null
@@ -1,9 +0,0 @@
-require 'bundler/setup'
-require 'bundler/gem_tasks'
-
-require 'rspec/core/rake_task'
-RSpec::Core::RakeTask.new(:spec) do |spec|
- spec.pattern = 'spec/**/*_spec.rb'
-end
-
-task :default => :spec
diff --git a/active_hash.gemspec b/active_hash.gemspec
deleted file mode 100644
index 5c7d3008..00000000
--- a/active_hash.gemspec
+++ /dev/null
@@ -1,57 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-$:.push File.expand_path("../lib", __FILE__)
-require "active_hash/version"
-
-Gem::Specification.new do |s|
- s.name = "active_hash"
- s.version = ActiveHash::Gem::VERSION
- s.authors = [
- "Jeff Dean",
- "Mike Dalessio",
- "Corey Innis",
- "Peter Jaros",
- "Brandon Keene",
- "Brian Takita",
- "Pat Nakajima",
- "John Pignata",
- "Michael Schubert",
- "Jeremy Weiskotten",
- "Ryan Garver",
- "Tom Stuart",
- "Joel Chippindale",
- "Kevin Olsen",
- "Vladimir Andrijevik",
- "Adam Anderson",
- "Keenan Brock",
- "Desmond Bowe",
- "Matthew O'Riordan",
- "Brett Richardson",
- "Rachel Heaton",
- "Keisuke Izumiya"
- ]
- s.email = %q{jeff@zilkey.com}
- s.summary = %q{An ActiveRecord-like model that uses a hash or file as a datasource}
- s.description = %q{Includes the ability to specify data using hashes, yml files or JSON files}
- s.homepage = %q{http://github.com/active-hash/active_hash}
- s.license = "MIT"
-
- s.metadata = {
- "homepage_uri" => s.homepage,
- "changelog_uri" => "https://github.com/active-hash/active_hash/blob/master/CHANGELOG.md",
- "source_code_uri" => s.homepage,
- "bug_tracker_uri" => "https://github.com/active-hash/active_hash/issues",
- }
-
- s.files = [
- "CHANGELOG.md",
- "LICENSE",
- "README.md",
- "active_hash.gemspec",
- Dir.glob("lib/**/*")
- ].flatten
- s.test_files = s.files.grep(%r{^(test|spec|features)/})
- s.add_runtime_dependency('activesupport', '>= 6.1.0')
- s.add_development_dependency "pry"
- s.required_ruby_version = '>= 3.0.0'
-end
diff --git a/app/globals.css b/app/globals.css
new file mode 100644
index 00000000..0ed28879
--- /dev/null
+++ b/app/globals.css
@@ -0,0 +1,92 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer utilities {
+ .text-balance {
+ text-wrap: balance;
+ }
+}
+
+@layer base {
+ :root {
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+ --background: 0 0% 100%;
+ --foreground: 0 0% 3.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 0 0% 3.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 0 0% 3.9%;
+ --primary: 0 0% 9%;
+ --primary-foreground: 0 0% 98%;
+ --secondary: 0 0% 96.1%;
+ --secondary-foreground: 0 0% 9%;
+ --muted: 0 0% 96.1%;
+ --muted-foreground: 0 0% 45.1%;
+ --accent: 0 0% 96.1%;
+ --accent-foreground: 0 0% 9%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 89.8%;
+ --input: 0 0% 89.8%;
+ --ring: 0 0% 3.9%;
+ --chart-1: 12 76% 61%;
+ --chart-2: 173 58% 39%;
+ --chart-3: 197 37% 24%;
+ --chart-4: 43 74% 66%;
+ --chart-5: 27 87% 67%;
+ --radius: 0.5rem;
+ --sidebar-background: 0 0% 98%;
+ --sidebar-foreground: 240 5.3% 26.1%;
+ --sidebar-primary: 240 5.9% 10%;
+ --sidebar-primary-foreground: 0 0% 98%;
+ --sidebar-accent: 240 4.8% 95.9%;
+ --sidebar-accent-foreground: 240 5.9% 10%;
+ --sidebar-border: 220 13% 91%;
+ --sidebar-ring: 217.2 91.2% 59.8%;
+ }
+ .dark {
+ --background: 0 0% 3.9%;
+ --foreground: 0 0% 98%;
+ --card: 0 0% 3.9%;
+ --card-foreground: 0 0% 98%;
+ --popover: 0 0% 3.9%;
+ --popover-foreground: 0 0% 98%;
+ --primary: 0 0% 98%;
+ --primary-foreground: 0 0% 9%;
+ --secondary: 0 0% 14.9%;
+ --secondary-foreground: 0 0% 98%;
+ --muted: 0 0% 14.9%;
+ --muted-foreground: 0 0% 63.9%;
+ --accent: 0 0% 14.9%;
+ --accent-foreground: 0 0% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 14.9%;
+ --input: 0 0% 14.9%;
+ --ring: 0 0% 83.1%;
+ --chart-1: 220 70% 50%;
+ --chart-2: 160 60% 45%;
+ --chart-3: 30 80% 55%;
+ --chart-4: 280 65% 60%;
+ --chart-5: 340 75% 55%;
+ --sidebar-background: 240 5.9% 10%;
+ --sidebar-foreground: 240 4.8% 95.9%;
+ --sidebar-primary: 224.3 76.3% 48%;
+ --sidebar-primary-foreground: 0 0% 100%;
+ --sidebar-accent: 240 3.7% 15.9%;
+ --sidebar-accent-foreground: 240 4.8% 95.9%;
+ --sidebar-border: 240 3.7% 15.9%;
+ --sidebar-ring: 217.2 91.2% 59.8%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
\ No newline at end of file
diff --git a/app/layout.tsx b/app/layout.tsx
new file mode 100644
index 00000000..4489638d
--- /dev/null
+++ b/app/layout.tsx
@@ -0,0 +1,45 @@
+import type { Metadata } from 'next'
+import { Geist, Geist_Mono } from 'next/font/google'
+import { Analytics } from '@vercel/analytics/next'
+import './globals.css'
+
+const _geist = Geist({ subsets: ["latin"] });
+const _geistMono = Geist_Mono({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+ title: 'v0 App',
+ description: 'Created with v0',
+ generator: 'v0.app',
+ icons: {
+ icon: [
+ {
+ url: '/icon-light-32x32.png',
+ media: '(prefers-color-scheme: light)',
+ },
+ {
+ url: '/icon-dark-32x32.png',
+ media: '(prefers-color-scheme: dark)',
+ },
+ {
+ url: '/icon.svg',
+ type: 'image/svg+xml',
+ },
+ ],
+ apple: '/apple-icon.png',
+ },
+}
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode
+}>) {
+ return (
+
+
+ {children}
+
+
+
+ )
+}
diff --git a/app/page.tsx b/app/page.tsx
new file mode 100644
index 00000000..2a0ecd97
--- /dev/null
+++ b/app/page.tsx
@@ -0,0 +1,173 @@
+import Link from "next/link"
+import { Button } from "@/components/ui/button"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Instagram, Linkedin, Menu, Plus, Twitter, Youtube } from "lucide-react"
+import SocialMediaCard from "@/components/social-media-card"
+import ContentCreator from "@/components/content-creator"
+import StudioSelector from "@/components/studio-selector"
+import MobileNavigation from "@/components/mobile-navigation"
+import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
+
+export default function Dashboard() {
+ return (
+
+ {/* Glassmorphic container */}
+
+ {/* Header */}
+
+
+
+ {/* Sidebar - Desktop only */}
+
+
+
+ Dashboard
+
+
+ Analytics
+
+
+ Calendar
+
+
+ Messages
+
+
+
+
+
PLATFORMS
+
+
+ Instagram
+
+
+ Twitter
+
+
+ LinkedIn
+
+
+ YouTube
+
+
+
+
+
+ {/* Main content */}
+
+
+
CONNECTED ACCOUNTS
+
+
}
+ color="bg-gradient-to-br from-purple-500 to-pink-500"
+ />
+
}
+ color="bg-blue-400"
+ />
+
}
+ color="bg-blue-600"
+ />
+
+
+ Add Platform
+
+
+
+
+
+
CREATE CONTENT
+
+
+
+ Post
+
+
+ Story
+
+
+ Video
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CONTENT STUDIO
+
+
+
+
+
+
+ )
+}
diff --git a/app/studio/audio/page.tsx b/app/studio/audio/page.tsx
new file mode 100644
index 00000000..147c2731
--- /dev/null
+++ b/app/studio/audio/page.tsx
@@ -0,0 +1,362 @@
+"use client"
+
+import type React from "react"
+
+import { useState, useRef, useEffect } from "react"
+import { Card } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+import { Slider } from "@/components/ui/slider"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Switch } from "@/components/ui/switch"
+import { Label } from "@/components/ui/label"
+import { Textarea } from "@/components/ui/textarea"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
+import {
+ Play,
+ Pause,
+ SkipBack,
+ SkipForward,
+ Upload,
+ Save,
+ Download,
+ Mic,
+ Music,
+ Volume2,
+ VolumeX,
+ Wand2,
+ Sparkles,
+ MessageSquare,
+ Loader2,
+} from "lucide-react"
+
+export default function AudioStudioPage() {
+ const [isPlaying, setIsPlaying] = useState(false)
+ const [volume, setVolume] = useState(75)
+ const [audioFile, setAudioFile] = useState(null)
+ const [isProcessing, setIsProcessing] = useState(false)
+ const [aiPrompt, setAiPrompt] = useState("")
+ const [aiResponse, setAiResponse] = useState("")
+ const canvasRef = useRef(null)
+
+ // Simulate waveform visualization
+ useEffect(() => {
+ const canvas = canvasRef.current
+ if (!canvas) return
+
+ const ctx = canvas.getContext("2d")
+ if (!ctx) return
+
+ const width = canvas.width
+ const height = canvas.height
+
+ ctx.clearRect(0, 0, width, height)
+ ctx.fillStyle = "#000"
+
+ // Draw a simulated waveform
+ const barWidth = 3
+ const gap = 2
+ const bars = Math.floor(width / (barWidth + gap))
+
+ for (let i = 0; i < bars; i++) {
+ // Generate random heights for the waveform visualization
+ const barHeight = Math.random() * (height * 0.8) + height * 0.1
+ ctx.fillRect(i * (barWidth + gap), (height - barHeight) / 2, barWidth, barHeight)
+ }
+ }, [])
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ setAudioFile(e.target.files[0])
+ }
+ }
+
+ const handleAIProcess = () => {
+ if (!aiPrompt.trim()) return
+
+ setIsProcessing(true)
+
+ // Simulate AI processing
+ setTimeout(() => {
+ setAiResponse(
+ "I've analyzed your audio and made the following adjustments: enhanced bass frequencies, reduced background noise, and improved vocal clarity. The processed audio has a more balanced EQ profile and sounds more professional.",
+ )
+ setIsProcessing(false)
+ }, 2000)
+ }
+
+ return (
+
+
AUDIO STUDIO
+
+
+
+ {/* Audio upload section */}
+
+ Audio Source
+
+ {audioFile ? (
+
+
+
{audioFile.name}
+
{(audioFile.size / (1024 * 1024)).toFixed(2)} MB
+
+ ) : (
+ <>
+
+
Drop your audio file here or
+
document.getElementById("audio-upload")?.click()}
+ >
+ Select File
+
+
+ >
+ )}
+
+
+
+
+ Record
+
+
+ Stock Audio
+
+
+ Generate
+
+
+ Enhance
+
+
+
+
+ {/* Waveform visualization */}
+
+ Audio Editor
+
+
+
+
+
+ {/* Playback controls */}
+
+
+
+
+
setIsPlaying(!isPlaying)}
+ >
+ {isPlaying ? : }
+
+
+
+
+
+
+
+
+
+
Volume
+
+ {volume === 0 ? : }
+ setVolume(value[0])}
+ />
+ {volume}%
+
+
+
+
+
Playback Speed
+
+
+ 100%
+
+
+
+
+
+ {/* AI Audio Processing */}
+
+
+
+
AI Audio Assistant
+
+
+
+
+ What would you like to do with your audio?
+
+
+
+
+ {isProcessing ? (
+ <>
+ Processing...
+ >
+ ) : (
+ <>
+ Process with AI
+ >
+ )}
+
+
+
+ {aiResponse && (
+
+
+
+
+
AI Assistant
+
{aiResponse}
+
+
+
+ )}
+
+
+
+
+ {/* Controls panel */}
+
+
+
+
Audio Controls
+
+
+
+
+
+ Mixer
+
+
+ Effects
+
+
+
+
+
+ Bass
+
+
+
+
+ Mid
+
+
+
+
+ Treble
+
+
+
+
+ Gain
+
+
+
+
+
+
+ Reverb
+
+
+
+
+ Delay
+
+
+
+
+ Compression
+
+
+
+
+ Noise Reduction
+
+
+
+
+
+
+
+
+
+
Export Options
+
+
+
+ File Format
+
+
+
+
+
+ MP3
+ WAV
+ AAC
+ FLAC
+
+
+
+
+
+ Quality
+
+
+
+
+
+ Low (128kbps)
+ Medium (256kbps)
+ High (320kbps)
+ Lossless
+
+
+
+
+
+
+ Save Project
+
+
+ Export Audio
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/studio/image/page.tsx b/app/studio/image/page.tsx
new file mode 100644
index 00000000..99c2da9a
--- /dev/null
+++ b/app/studio/image/page.tsx
@@ -0,0 +1,528 @@
+"use client"
+
+import type React from "react"
+
+import { useState, useRef, useEffect } from "react"
+import { Card } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+import { Slider } from "@/components/ui/slider"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Switch } from "@/components/ui/switch"
+import { Label } from "@/components/ui/label"
+import { Textarea } from "@/components/ui/textarea"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
+import {
+ Upload,
+ Save,
+ Download,
+ Crop,
+ Move,
+ Layers,
+ ImageIcon,
+ Paintbrush,
+ Eraser,
+ RotateCcw,
+ Wand2,
+ Sparkles,
+ MessageSquare,
+ Loader2,
+} from "lucide-react"
+
+export default function ImageStudioPage() {
+ const [imageFile, setImageFile] = useState(null)
+ const [imagePreview, setImagePreview] = useState(null)
+ const [isProcessing, setIsProcessing] = useState(false)
+ const [aiPrompt, setAiPrompt] = useState("")
+ const [aiResponse, setAiResponse] = useState("")
+ const [activeFilter, setActiveFilter] = useState("none")
+ const canvasRef = useRef(null)
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ const file = e.target.files[0]
+ setImageFile(file)
+
+ // Create preview URL
+ const reader = new FileReader()
+ reader.onload = (e) => {
+ if (e.target?.result) {
+ setImagePreview(e.target.result as string)
+ }
+ }
+ reader.readAsDataURL(file)
+ }
+ }
+
+ // Apply filter effect to canvas when filter changes
+ useEffect(() => {
+ if (!imagePreview || !canvasRef.current) return
+
+ const canvas = canvasRef.current
+ const ctx = canvas.getContext("2d")
+ if (!ctx) return
+
+ const img = new Image()
+ img.crossOrigin = "anonymous"
+ img.onload = () => {
+ // Set canvas dimensions to match image
+ canvas.width = img.width
+ canvas.height = img.height
+
+ // Draw original image
+ ctx.drawImage(img, 0, 0)
+
+ // Apply filter based on selection
+ switch (activeFilter) {
+ case "grayscale":
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
+ const data = imageData.data
+ for (let i = 0; i < data.length; i += 4) {
+ const avg = (data[i] + data[i + 1] + data[i + 2]) / 3
+ data[i] = avg // red
+ data[i + 1] = avg // green
+ data[i + 2] = avg // blue
+ }
+ ctx.putImageData(imageData, 0, 0)
+ break
+
+ case "sepia":
+ const sepiaData = ctx.getImageData(0, 0, canvas.width, canvas.height)
+ const sepiaPixels = sepiaData.data
+ for (let i = 0; i < sepiaPixels.length; i += 4) {
+ const r = sepiaPixels[i]
+ const g = sepiaPixels[i + 1]
+ const b = sepiaPixels[i + 2]
+
+ sepiaPixels[i] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189)
+ sepiaPixels[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168)
+ sepiaPixels[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131)
+ }
+ ctx.putImageData(sepiaData, 0, 0)
+ break
+
+ case "invert":
+ const invertData = ctx.getImageData(0, 0, canvas.width, canvas.height)
+ const invertPixels = invertData.data
+ for (let i = 0; i < invertPixels.length; i += 4) {
+ invertPixels[i] = 255 - invertPixels[i] // red
+ invertPixels[i + 1] = 255 - invertPixels[i + 1] // green
+ invertPixels[i + 2] = 255 - invertPixels[i + 2] // blue
+ }
+ ctx.putImageData(invertData, 0, 0)
+ break
+
+ case "none":
+ default:
+ // No filter, already drawn original image
+ break
+ }
+ }
+ img.src = imagePreview
+ }, [imagePreview, activeFilter])
+
+ const handleAIProcess = () => {
+ if (!aiPrompt.trim()) return
+
+ setIsProcessing(true)
+
+ // Simulate AI processing
+ setTimeout(() => {
+ setAiResponse(
+ "I've analyzed your image and made the following adjustments: enhanced colors, removed background distractions, and improved overall clarity. I've also applied a subtle vignette effect to draw focus to the main subject.",
+ )
+ setIsProcessing(false)
+ }, 2000)
+ }
+
+ const handleAIGenerate = () => {
+ if (!aiPrompt.trim()) return
+
+ setIsProcessing(true)
+
+ // Simulate AI processing
+ setTimeout(() => {
+ setAiResponse(
+ "I've generated a new image based on your prompt. The image features the elements you described with a cohesive style and composition. You can now edit this image further using the tools in the Image Studio.",
+ )
+ setIsProcessing(false)
+
+ // Here we would normally set the generated image, but for this demo we'll just use a placeholder
+ // In a real implementation, this would be the result from an image generation API
+ }, 2000)
+ }
+
+ return (
+
+
IMAGE STUDIO
+
+
+
+ {/* Image upload/generation section */}
+
+
+
+
+ Upload
+
+
+ Generate
+
+
+
+
+ Image Source
+ {imagePreview ? (
+
+
+
+ ) : (
+
+
+
Drop your image here or
+
document.getElementById("image-upload")?.click()}
+ >
+ Select Image
+
+
+
+ )}
+
+
+
+ Stock Images
+
+
+ Crop
+
+
+ Rotate
+
+
+ Enhance
+
+
+
+
+
+ AI Image Generation
+
+
+ Describe the image you want to create
+
+
+
+
+ Style
+
+
+
+
+
+ Realistic
+ Cartoon
+ Abstract
+ Digital Art
+ Oil Painting
+
+
+
+
+
+ Aspect Ratio
+
+
+
+
+
+ 1:1 (Square)
+ 4:5 (Portrait)
+ 16:9 (Landscape)
+ 21:9 (Widescreen)
+
+
+
+
+
+
+
+ {isProcessing ? (
+ <>
+ Generating...
+ >
+ ) : (
+ <>
+ Generate Image
+ >
+ )}
+
+
+
+
+
+
+
+ {/* Quick tools */}
+ {imagePreview && (
+
+ Quick Tools
+
+
+ Crop
+
+
+ Rotate
+
+
+ Draw
+
+
+ Erase
+
+
+ Layers
+
+
+ Move
+
+
+ Filters
+
+
+ Enhance
+
+
+
+ )}
+
+ {/* AI Image Processing */}
+
+
+
+
AI Image Assistant
+
+
+
+
+ What would you like to do with your image?
+
+
+
+
+ {isProcessing ? (
+ <>
+ Processing...
+ >
+ ) : (
+ <>
+ Process with AI
+ >
+ )}
+
+
+
+ {aiResponse && (
+
+
+
+
+
AI Assistant
+
{aiResponse}
+
+
+
+ )}
+
+
+
+
+ {/* Controls panel */}
+
+
+
+
Image Controls
+
+
+
+
+
+ Adjust
+
+
+ Filters
+
+
+ Resize
+
+
+
+
+
+ Brightness
+
+
+
+
+ Contrast
+
+
+
+
+ Saturation
+
+
+
+
+ Sharpness
+
+
+
+
+
+
+ Filter
+
+
+
+
+
+ None
+ Grayscale
+ Sepia
+ Invert
+ Vintage
+ HDR
+
+
+
+
+
+ Intensity
+
+
+
+
+ Vignette
+
+
+
+
+
+
+
+
+
+
+ Lock Aspect Ratio
+
+
+
+
+
+
+
+
+
+
Export Options
+
+
+
+ File Format
+
+
+
+
+
+ PNG
+ JPG
+ WebP
+ SVG
+
+
+
+
+
+ Quality
+
+
+
+
+
+ Low
+ Medium
+ High
+ Maximum
+
+
+
+
+
+
+ Save Project
+
+
+ Export Image
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/studio/layout.tsx b/app/studio/layout.tsx
new file mode 100644
index 00000000..1f6a6139
--- /dev/null
+++ b/app/studio/layout.tsx
@@ -0,0 +1,38 @@
+import type React from "react"
+import Link from "next/link"
+import { Button } from "@/components/ui/button"
+import { ArrowLeft, Home } from "lucide-react"
+
+export default function StudioLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+ {/* Glassmorphic container */}
+
+ {/* Header */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
POSTCRAFT STUDIO
+
+
+
+
+
{children}
+
+
+ )
+}
diff --git a/app/studio/page.tsx b/app/studio/page.tsx
new file mode 100644
index 00000000..14ff1cf8
--- /dev/null
+++ b/app/studio/page.tsx
@@ -0,0 +1,62 @@
+"use client"
+import { Card } from "@/components/ui/card"
+import { Image, Music, Video, FileText } from "lucide-react"
+import Link from "next/link"
+
+export default function StudioPage() {
+ const studioOptions = [
+ {
+ type: "audio",
+ name: "Audio Studio",
+ description: "Mix, master, and edit audio content with AI assistance",
+ icon: ,
+ color: "bg-gradient-to-br from-pink-500 to-orange-500",
+ href: "/studio/audio",
+ },
+ {
+ type: "video",
+ name: "Video Studio",
+ description: "Edit, trim, and enhance video content with AI tools",
+ icon: ,
+ color: "bg-gradient-to-br from-blue-500 to-purple-500",
+ href: "/studio/video",
+ },
+ {
+ type: "image",
+ name: "Image Studio",
+ description: "Edit and enhance images with AI-powered features",
+ icon: ,
+ color: "bg-gradient-to-br from-green-500 to-teal-500",
+ href: "/studio/image",
+ },
+ {
+ type: "text",
+ name: "Text Studio",
+ description: "Create and optimize text content with AI assistance",
+ icon: ,
+ color: "bg-gradient-to-br from-yellow-500 to-amber-500",
+ href: "/studio/text",
+ },
+ ]
+
+ return (
+
+
SELECT A STUDIO
+
+ {studioOptions.map((studio) => (
+
+
+
+ {studio.icon}
+
{studio.name}
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/app/studio/text/page.tsx b/app/studio/text/page.tsx
new file mode 100644
index 00000000..774b7941
--- /dev/null
+++ b/app/studio/text/page.tsx
@@ -0,0 +1,448 @@
+"use client"
+
+import type React from "react"
+
+import { useState } from "react"
+import { Card } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Switch } from "@/components/ui/switch"
+import { Label } from "@/components/ui/label"
+import { Textarea } from "@/components/ui/textarea"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
+import { Save, Download, Wand2, Sparkles, MessageSquare, Loader2, Check, Copy, RotateCcw } from "lucide-react"
+
+export default function TextStudioPage() {
+ const [textContent, setTextContent] = useState("")
+ const [isProcessing, setIsProcessing] = useState(false)
+ const [aiPrompt, setAiPrompt] = useState("")
+ const [aiResponse, setAiResponse] = useState("")
+ const [generationPrompt, setGenerationPrompt] = useState("")
+ const [generatedText, setGeneratedText] = useState("")
+ const [textStats, setTextStats] = useState({
+ words: 0,
+ characters: 0,
+ sentences: 0,
+ readingTime: "0 min",
+ })
+
+ const updateTextStats = (text: string) => {
+ const words = text.trim().split(/\s+/).filter(Boolean).length
+ const characters = text.length
+ const sentences = text.split(/[.!?]+/).filter(Boolean).length
+ const readingTime = Math.ceil(words / 200) // Assuming 200 words per minute reading speed
+
+ setTextStats({
+ words,
+ characters,
+ sentences,
+ readingTime: `${readingTime} min`,
+ })
+ }
+
+ const handleTextChange = (e: React.ChangeEvent) => {
+ const newText = e.target.value
+ setTextContent(newText)
+ updateTextStats(newText)
+ }
+
+ const handleAIProcess = () => {
+ if (!aiPrompt.trim() || !textContent.trim()) return
+
+ setIsProcessing(true)
+
+ // Simulate AI processing
+ setTimeout(() => {
+ setAiResponse(
+ "I've analyzed your text and made the following improvements: fixed grammar issues, improved sentence structure, and enhanced overall readability. The tone is now more professional and engaging.",
+ )
+
+ // In a real implementation, this would be the result from an LLM
+ setTextContent(textContent) // Here we'd update with the improved text
+ updateTextStats(textContent)
+
+ setIsProcessing(false)
+ }, 2000)
+ }
+
+ const handleAIGenerate = () => {
+ if (!generationPrompt.trim()) return
+
+ setIsProcessing(true)
+
+ // Simulate AI processing
+ setTimeout(() => {
+ const generatedContent =
+ "This is a sample of AI-generated text based on your prompt. In a real implementation, this would be content created by a language model that follows your specific instructions and requirements. The text would be tailored to your desired tone, style, and purpose."
+
+ setGeneratedText(generatedContent)
+ updateTextStats(generatedContent)
+ setAiResponse(
+ "I've generated text based on your prompt. The content follows your specified parameters and is ready for further editing or refinement.",
+ )
+
+ setIsProcessing(false)
+ }, 2000)
+ }
+
+ const handleCopyText = () => {
+ navigator.clipboard.writeText(textContent || generatedText)
+ }
+
+ return (
+
+
TEXT STUDIO
+
+
+
+ {/* Text editor section */}
+
+
+
+
+ Edit Text
+
+
+ Generate Text
+
+
+
+
+
+
Text Editor
+
+ Words: {textStats.words}
+ •
+ Reading time: {textStats.readingTime}
+
+
+
+
+
+
+
+ Spell Check
+
+
+ Undo
+
+
+ Copy
+
+
+ Enhance
+
+
+
+
+
+ AI Text Generation
+
+
+ What would you like to create?
+
+
+
+
+ Tone
+
+
+
+
+
+ Professional
+ Casual
+ Friendly
+ Formal
+ Persuasive
+
+
+
+
+
+ Length
+
+
+
+
+
+ Short (100 words)
+ Medium (300 words)
+ Long (500 words)
+ Custom
+
+
+
+
+
+
+
+ {isProcessing ? (
+ <>
+ Generating...
+ >
+ ) : (
+ <>
+ Generate Text
+ >
+ )}
+
+
+
+ {generatedText && (
+
+
+
Generated Text
+
+ Words: {textStats.words}
+ •
+ Reading time: {textStats.readingTime}
+
+
+
+
+
+ Copy
+
+
+
+ )}
+
+
+
+
+
+ {/* AI Text Processing */}
+
+
+
+
AI Text Assistant
+
+
+
+
+ What would you like to do with your text?
+
+
+
+
+ {isProcessing ? (
+ <>
+ Processing...
+ >
+ ) : (
+ <>
+ Process with AI
+ >
+ )}
+
+
+
+ {aiResponse && (
+
+
+
+
+
AI Assistant
+
{aiResponse}
+
+
+
+ )}
+
+
+
+
+ {/* Controls panel */}
+
+
+
+
Text Controls
+
+
+
+
+
+ Format
+
+
+ Analyze
+
+
+
+
+
+ Text Style
+
+
+
+
+
+ Paragraph
+ Bullet Points
+ Numbered List
+ Headings
+
+
+
+
+
+ Bold Important Points
+
+
+
+
+ Auto-Capitalize
+
+
+
+
+ Smart Quotes
+
+
+
+
+ Text Case
+
+
+
+
+
+ Sentence case
+ lowercase
+ UPPERCASE
+ Title Case
+
+
+
+
+
+
+
+
+ Words
+ {textStats.words}
+
+
+ Characters
+ {textStats.characters}
+
+
+ Sentences
+ {textStats.sentences}
+
+
+ Reading Time
+ {textStats.readingTime}
+
+
+
+
+
Readability
+
+
+ Easy
+ Medium
+ Complex
+
+
+
+
+ Detailed Analysis
+
+
+
+
+
+
+
+
+
Export Options
+
+
+
+ File Format
+
+
+
+
+
+ Plain Text (.txt)
+ Word Document (.docx)
+ PDF Document (.pdf)
+ Markdown (.md)
+
+
+
+
+
+
+ Save Project
+
+
+ Export Text
+
+
+ Copy to Clipboard
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/studio/video/page.tsx b/app/studio/video/page.tsx
new file mode 100644
index 00000000..b28eb1d9
--- /dev/null
+++ b/app/studio/video/page.tsx
@@ -0,0 +1,392 @@
+"use client"
+
+import type React from "react"
+
+import { useState, useRef } from "react"
+import { Card } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+import { Slider } from "@/components/ui/slider"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Switch } from "@/components/ui/switch"
+import { Label } from "@/components/ui/label"
+import { Textarea } from "@/components/ui/textarea"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
+import {
+ Play,
+ Pause,
+ SkipBack,
+ SkipForward,
+ Upload,
+ Save,
+ Download,
+ Scissors,
+ Layers,
+ Film,
+ Text,
+ ImageIcon,
+ Wand2,
+ Sparkles,
+ MessageSquare,
+ Loader2,
+} from "lucide-react"
+
+export default function VideoStudioPage() {
+ const [isPlaying, setIsPlaying] = useState(false)
+ const [videoFile, setVideoFile] = useState(null)
+ const [isProcessing, setIsProcessing] = useState(false)
+ const [aiPrompt, setAiPrompt] = useState("")
+ const [aiResponse, setAiResponse] = useState("")
+ const videoRef = useRef(null)
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ setVideoFile(e.target.files[0])
+
+ // Create object URL for preview
+ const videoElement = videoRef.current
+ if (videoElement) {
+ videoElement.src = URL.createObjectURL(e.target.files[0])
+ }
+ }
+ }
+
+ const togglePlay = () => {
+ const videoElement = videoRef.current
+ if (!videoElement) return
+
+ if (isPlaying) {
+ videoElement.pause()
+ } else {
+ videoElement.play()
+ }
+
+ setIsPlaying(!isPlaying)
+ }
+
+ const handleAIProcess = () => {
+ if (!aiPrompt.trim()) return
+
+ setIsProcessing(true)
+
+ // Simulate AI processing
+ setTimeout(() => {
+ setAiResponse(
+ "I've analyzed your video and made the following adjustments: enhanced colors, stabilized shaky footage, and improved lighting. I've also generated captions based on the audio content and added them to the timeline.",
+ )
+ setIsProcessing(false)
+ }, 2000)
+ }
+
+ return (
+
+
VIDEO STUDIO
+
+
+
+ {/* Video upload section */}
+
+ Video Source
+ {videoFile ? (
+
+
+
+ ) : (
+
+
+
Drop your video file here or
+
document.getElementById("video-upload")?.click()}
+ >
+ Select Video
+
+
+
+ )}
+
+
+
+ Record
+
+
+ Stock Video
+
+
+ Generate
+
+
+ Enhance
+
+
+
+
+ {/* Timeline */}
+ {videoFile && (
+
+ Timeline
+
+
+ {/* Timeline tracks would go here */}
+
+
+
+ {/* Playback controls */}
+
+
+
+
+
+ {isPlaying ? : }
+
+
+
+
+
+
+
+
+
+
Playback Speed
+
+
+ 100%
+
+
+
+
+
+
+ )}
+
+ {/* AI Video Processing */}
+
+
+
+
AI Video Assistant
+
+
+
+
+ What would you like to do with your video?
+
+
+
+
+ {isProcessing ? (
+ <>
+ Processing...
+ >
+ ) : (
+ <>
+ Process with AI
+ >
+ )}
+
+
+
+ {aiResponse && (
+
+
+
+
+
AI Assistant
+
{aiResponse}
+
+
+
+ )}
+
+
+
+
+ {/* Controls panel */}
+
+
+
+
Video Controls
+
+
+
+
+
+ Edit
+
+
+ Effects
+
+
+ Text
+
+
+
+
+
+ Split Clip
+
+
+
+ Add Layer
+
+
+
+
Crop
+
+
+ 16:9
+
+
+ 1:1
+
+
+ 4:5
+
+
+ 9:16
+
+
+
+
+
+
+
+ Brightness
+
+
+
+
+ Contrast
+
+
+
+
+ Saturation
+
+
+
+
+ Stabilization
+
+
+
+
+
+
+ Add Title
+
+
+
+ Add Caption
+
+
+
+ Add Sticker
+
+
+
+ Auto-Generate Captions
+
+
+
+
+
+ English
+ Spanish
+ French
+ German
+
+
+
+
+
+
+
+
+
+
+
Export Options
+
+
+
+ File Format
+
+
+
+
+
+ MP4
+ MOV
+ WebM
+ GIF
+
+
+
+
+
+ Resolution
+
+
+
+
+
+ 720p
+ 1080p (Full HD)
+ 2K
+ 4K
+
+
+
+
+
+
+ Save Project
+
+
+ Export Video
+
+
+
+
+
+
+
+ )
+}
diff --git a/components.json b/components.json
new file mode 100644
index 00000000..4ee62ee1
--- /dev/null
+++ b/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "app/globals.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "lucide"
+}
diff --git a/components/content-creator.tsx b/components/content-creator.tsx
new file mode 100644
index 00000000..db2ea8dd
--- /dev/null
+++ b/components/content-creator.tsx
@@ -0,0 +1,247 @@
+"use client"
+
+import { useState } from "react"
+import { Button } from "@/components/ui/button"
+import { Card } from "@/components/ui/card"
+import { Textarea } from "@/components/ui/textarea"
+import { Instagram, Linkedin, Twitter, Youtube, ImageIcon, Calendar, Send } from "lucide-react"
+import { Switch } from "@/components/ui/switch"
+import { Label } from "@/components/ui/label"
+import { Slider } from "@/components/ui/slider"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
+import { ChevronDown } from "lucide-react"
+
+interface ContentCreatorProps {
+ type: "post" | "story" | "video"
+}
+
+export default function ContentCreator({ type }: ContentCreatorProps) {
+ const [selectedPlatforms, setSelectedPlatforms] = useState({
+ instagram: true,
+ twitter: true,
+ linkedin: false,
+ youtube: false,
+ })
+
+ const togglePlatform = (platform: keyof typeof selectedPlatforms) => {
+ setSelectedPlatforms({
+ ...selectedPlatforms,
+ [platform]: !selectedPlatforms[platform],
+ })
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ Add Media
+
+
+ Schedule
+
+
+
+ {type === "post" && (
+
+
Post Settings
+
+
+
+ Filters
+
+
+ Hashtags
+
+
+ Advanced
+
+
+
+
+
+ Brightness
+
+
+
+ Contrast
+
+
+
+ Saturation
+
+
+
+
+
+
+
+
+
+
+ Hide Likes
+
+
+
+ Turn Off Comments
+
+
+
+ Alt Text
+
+
+
+
+
+
+ )}
+
+ {type === "story" && (
+
+
Story Settings
+
+
+ Add Sticker
+
+
+ Add Text
+
+
+ Add Music
+
+
+ Add Poll
+
+
+
+ )}
+
+ {type === "video" && (
+
+
Video Settings
+
+
Thumbnail
+
+
+
+ Drop your thumbnail here or browse
+
+
+
+
+ Title
+
+
+
+ )}
+
+ {/* Mobile-only platforms section */}
+
+
+
+ Platforms
+
+
+
+
+
+
+ Instagram
+
+
togglePlatform("instagram")} />
+
+
+
+
+ Twitter
+
+
togglePlatform("twitter")} />
+
+
+
+
+ LinkedIn
+
+
togglePlatform("linkedin")} />
+
+
+
+
+ YouTube
+
+
togglePlatform("youtube")} />
+
+
+ Publish Now
+
+
+
+
+
+
+ {/* Desktop-only platforms section */}
+
+
+ PLATFORMS
+
+
+
+
+ Instagram
+
+
togglePlatform("instagram")} />
+
+
+
+
+ Twitter
+
+
togglePlatform("twitter")} />
+
+
+
+
+ LinkedIn
+
+
togglePlatform("linkedin")} />
+
+
+
+
+ YouTube
+
+
togglePlatform("youtube")} />
+
+
+
+
+
+ Publish Now
+
+
+
+ )
+}
diff --git a/components/mobile-navigation.tsx b/components/mobile-navigation.tsx
new file mode 100644
index 00000000..8bc748ce
--- /dev/null
+++ b/components/mobile-navigation.tsx
@@ -0,0 +1,59 @@
+import Link from "next/link"
+import { Button } from "@/components/ui/button"
+import { Instagram, Linkedin, Twitter, Youtube } from "lucide-react"
+
+export default function MobileNavigation() {
+ return (
+
+
+
POSTCRAFT
+
+
+
+
+
+ Dashboard
+
+
+ Analytics
+
+
+ Calendar
+
+
+ Messages
+
+
+
+
+
PLATFORMS
+
+
+ Instagram
+
+
+ Twitter
+
+
+ LinkedIn
+
+
+ YouTube
+
+
+
+
+
+
+
+
+ Connect
+
+
+ Settings
+
+
+
+
+ )
+}
diff --git a/components/social-media-card.tsx b/components/social-media-card.tsx
new file mode 100644
index 00000000..8f3ced80
--- /dev/null
+++ b/components/social-media-card.tsx
@@ -0,0 +1,41 @@
+import type React from "react"
+import { Card } from "@/components/ui/card"
+import { cn } from "@/lib/utils"
+
+interface SocialMediaCardProps {
+ platform: string
+ username: string
+ icon: React.ReactNode
+ color: string
+}
+
+export default function SocialMediaCard({ platform, username, icon, color }: SocialMediaCardProps) {
+ return (
+
+
+
+ {icon}
+ Connected
+
+
{platform}
+
{username}
+
+
+
+ )
+}
diff --git a/components/studio-selector.tsx b/components/studio-selector.tsx
new file mode 100644
index 00000000..190d4e83
--- /dev/null
+++ b/components/studio-selector.tsx
@@ -0,0 +1,60 @@
+"use client"
+
+import { Card } from "@/components/ui/card"
+import { Image, Music, Video, FileText } from "lucide-react"
+import Link from "next/link"
+
+export default function StudioSelector() {
+ const studioOptions = [
+ {
+ type: "audio",
+ name: "Audio Studio",
+ description: "Mix, master, and edit audio content with AI assistance",
+ icon: ,
+ color: "bg-gradient-to-br from-pink-500 to-orange-500",
+ href: "/studio/audio",
+ },
+ {
+ type: "video",
+ name: "Video Studio",
+ description: "Edit, trim, and enhance video content with AI tools",
+ icon: ,
+ color: "bg-gradient-to-br from-blue-500 to-purple-500",
+ href: "/studio/video",
+ },
+ {
+ type: "image",
+ name: "Image Studio",
+ description: "Edit and enhance images with AI-powered features",
+ icon: ,
+ color: "bg-gradient-to-br from-green-500 to-teal-500",
+ href: "/studio/image",
+ },
+ {
+ type: "text",
+ name: "Text Studio",
+ description: "Create and optimize text content with AI assistance",
+ icon: ,
+ color: "bg-gradient-to-br from-yellow-500 to-amber-500",
+ href: "/studio/text",
+ },
+ ]
+
+ return (
+
+ {studioOptions.map((studio) => (
+
+
+
+ {studio.icon}
+
{studio.name}
+
+
+
+
+ ))}
+
+ )
+}
diff --git a/components/studios/audio-studio.tsx b/components/studios/audio-studio.tsx
new file mode 100644
index 00000000..124a1258
--- /dev/null
+++ b/components/studios/audio-studio.tsx
@@ -0,0 +1,281 @@
+"use client"
+
+import type React from "react"
+
+import { useState, useRef, useEffect } from "react"
+import { Card } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+import { Slider } from "@/components/ui/slider"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Switch } from "@/components/ui/switch"
+import { Label } from "@/components/ui/label"
+import {
+ Play,
+ Pause,
+ SkipBack,
+ SkipForward,
+ Upload,
+ Save,
+ Download,
+ Music,
+ Volume2,
+ VolumeX,
+ ChevronDown,
+} from "lucide-react"
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
+
+interface AudioStudioProps {
+ onBack: () => void
+}
+
+export default function AudioStudio({ onBack }: AudioStudioProps) {
+ const [isPlaying, setIsPlaying] = useState(false)
+ const [volume, setVolume] = useState(75)
+ const [audioFile, setAudioFile] = useState(null)
+ const canvasRef = useRef(null)
+
+ // Simulate waveform visualization
+ useEffect(() => {
+ const canvas = canvasRef.current
+ if (!canvas) return
+
+ const ctx = canvas.getContext("2d")
+ if (!ctx) return
+
+ const width = canvas.width
+ const height = canvas.height
+
+ ctx.clearRect(0, 0, width, height)
+ ctx.fillStyle = "#000"
+
+ // Draw a simulated waveform
+ const barWidth = 3
+ const gap = 2
+ const bars = Math.floor(width / (barWidth + gap))
+
+ for (let i = 0; i < bars; i++) {
+ // Generate random heights for the waveform visualization
+ const barHeight = Math.random() * (height * 0.8) + height * 0.1
+ ctx.fillRect(i * (barWidth + gap), (height - barHeight) / 2, barWidth, barHeight)
+ }
+ }, [])
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ setAudioFile(e.target.files[0])
+ }
+ }
+
+ return (
+
+
+ Back to Studios
+
+
+
+
+
AUDIO STUDIO
+
+ {/* Audio upload section */}
+
+
+ {audioFile ? (
+
+
+
{audioFile.name}
+
{(audioFile.size / (1024 * 1024)).toFixed(2)} MB
+
+ ) : (
+ <>
+
+
Drop your audio file here or
+
document.getElementById("audio-upload")?.click()}
+ >
+ Select File
+
+
+ >
+ )}
+
+
+
+ {/* Waveform visualization */}
+
+
+
+
+
+ {/* Playback controls */}
+
+
+
+
+
setIsPlaying(!isPlaying)}
+ >
+ {isPlaying ? : }
+
+
+
+
+
+
+
+ {/* Mobile-only controls */}
+
+
+
+ Audio Controls
+
+
+
+
+
+
Volume
+
+ {volume === 0 ? : }
+ {volume}%
+
+
+
setVolume(value[0])}
+ />
+
+
+
+ Bass
+
+
+
+
+ Mid
+
+
+
+
+ Treble
+
+
+
+
+
+ Save
+
+
+ Export
+
+
+
+
+
+
+
+ {/* Desktop-only controls panel */}
+
+
+
+
Audio Controls
+
+
+
+
+
+ Mixer
+
+
+ Effects
+
+
+
+
+
+
+
Volume
+
+ {volume === 0 ? : }
+ {volume}%
+
+
+
setVolume(value[0])}
+ />
+
+
+
+ Bass
+
+
+
+
+ Mid
+
+
+
+
+ Treble
+
+
+
+
+
+
+ Reverb
+
+
+
+
+ Delay
+
+
+
+
+ Compression
+
+
+
+
+ Noise Reduction
+
+
+
+
+
+
+
+ Save Project
+
+
+ Export Audio
+
+
+
+
+
+
+
+ )
+}
diff --git a/components/studios/image-studio.tsx b/components/studios/image-studio.tsx
new file mode 100644
index 00000000..9d3c49ef
--- /dev/null
+++ b/components/studios/image-studio.tsx
@@ -0,0 +1,335 @@
+"use client"
+
+import type React from "react"
+
+import { useState, useRef, useEffect } from "react"
+import { Card } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+import { Slider } from "@/components/ui/slider"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Switch } from "@/components/ui/switch"
+import { Label } from "@/components/ui/label"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
+import {
+ Upload,
+ Save,
+ Download,
+ Crop,
+ Layers,
+ ImageIcon,
+ Paintbrush,
+ Eraser,
+ RotateCcw,
+ ChevronDown,
+} from "lucide-react"
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
+
+interface ImageStudioProps {
+ onBack: () => void
+}
+
+export default function ImageStudio({ onBack }: ImageStudioProps) {
+ const [imageFile, setImageFile] = useState(null)
+ const [imagePreview, setImagePreview] = useState(null)
+ const canvasRef = useRef(null)
+ const [activeFilter, setActiveFilter] = useState("none")
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ const file = e.target.files[0]
+ setImageFile(file)
+
+ // Create preview URL
+ const reader = new FileReader()
+ reader.onload = (e) => {
+ if (e.target?.result) {
+ setImagePreview(e.target.result as string)
+ }
+ }
+ reader.readAsDataURL(file)
+ }
+ }
+
+ // Apply filter effect to canvas when filter changes
+ useEffect(() => {
+ if (!imagePreview || !canvasRef.current) return
+
+ const canvas = canvasRef.current
+ const ctx = canvas.getContext("2d")
+ if (!ctx) return
+
+ const img = new Image()
+ img.crossOrigin = "anonymous"
+ img.onload = () => {
+ // Set canvas dimensions to match image
+ canvas.width = img.width
+ canvas.height = img.height
+
+ // Draw original image
+ ctx.drawImage(img, 0, 0)
+
+ // Apply filter based on selection
+ switch (activeFilter) {
+ case "grayscale":
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
+ const data = imageData.data
+ for (let i = 0; i < data.length; i += 4) {
+ const avg = (data[i] + data[i + 1] + data[i + 2]) / 3
+ data[i] = avg // red
+ data[i + 1] = avg // green
+ data[i + 2] = avg // blue
+ }
+ ctx.putImageData(imageData, 0, 0)
+ break
+
+ case "sepia":
+ const sepiaData = ctx.getImageData(0, 0, canvas.width, canvas.height)
+ const sepiaPixels = sepiaData.data
+ for (let i = 0; i < sepiaPixels.length; i += 4) {
+ const r = sepiaPixels[i]
+ const g = sepiaPixels[i + 1]
+ const b = sepiaPixels[i + 2]
+
+ sepiaPixels[i] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189)
+ sepiaPixels[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168)
+ sepiaPixels[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131)
+ }
+ ctx.putImageData(sepiaData, 0, 0)
+ break
+
+ case "invert":
+ const invertData = ctx.getImageData(0, 0, canvas.width, canvas.height)
+ const invertPixels = invertData.data
+ for (let i = 0; i < invertPixels.length; i += 4) {
+ invertPixels[i] = 255 - invertPixels[i] // red
+ invertPixels[i + 1] = 255 - invertPixels[i + 1] // green
+ invertPixels[i + 2] = 255 - invertPixels[i + 2] // blue
+ }
+ ctx.putImageData(invertData, 0, 0)
+ break
+
+ case "none":
+ default:
+ // No filter, already drawn original image
+ break
+ }
+ }
+ img.src = imagePreview
+ }, [imagePreview, activeFilter])
+
+ return (
+
+
+ Back to Studios
+
+
+
+
+
IMAGE STUDIO
+
+ {/* Image preview section */}
+
+ {imagePreview ? (
+
+
+
+ ) : (
+
+
+
Drop your image here or
+
document.getElementById("image-upload")?.click()}
+ >
+ Select Image
+
+
+
+ )}
+
+
+ {/* Quick tools */}
+ {imagePreview && (
+
+
+ Crop
+
+
+ Rotate
+
+
+ Draw
+
+
+ Erase
+
+
+ Layers
+
+
+ )}
+
+ {/* Mobile-only controls */}
+
+
+
+ Image Controls
+
+
+
+
+ Filter
+
+
+
+
+
+ None
+ Grayscale
+ Sepia
+ Invert
+
+
+
+
+
+ Brightness
+
+
+
+
+ Contrast
+
+
+
+
+
+ Save
+
+
+ Export
+
+
+
+
+
+
+
+ {/* Desktop-only controls panel */}
+
+
+
+
Image Controls
+
+
+
+
+
+ Adjust
+
+
+ Filters
+
+
+ Resize
+
+
+
+
+
+ Brightness
+
+
+
+
+ Contrast
+
+
+
+
+ Saturation
+
+
+
+
+ Sharpness
+
+
+
+
+
+
+ Filter
+
+
+
+
+
+ None
+ Grayscale
+ Sepia
+ Invert
+
+
+
+
+
+ Intensity
+
+
+
+
+ Vignette
+
+
+
+
+
+
+
+
+
+
+ Lock Aspect Ratio
+
+
+
+
+
+
+
+ Save Project
+
+
+ Export Image
+
+
+
+
+
+
+
+ )
+}
diff --git a/components/studios/video-studio.tsx b/components/studios/video-studio.tsx
new file mode 100644
index 00000000..e69bf4ca
--- /dev/null
+++ b/components/studios/video-studio.tsx
@@ -0,0 +1,272 @@
+"use client"
+
+import type React from "react"
+
+import { useState, useRef } from "react"
+import { Card } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+import { Slider } from "@/components/ui/slider"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Switch } from "@/components/ui/switch"
+import { Label } from "@/components/ui/label"
+import {
+ Play,
+ Pause,
+ SkipBack,
+ SkipForward,
+ Upload,
+ Save,
+ Download,
+ Scissors,
+ Layers,
+ Film,
+ Text,
+ ImageIcon,
+ ChevronDown,
+} from "lucide-react"
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
+
+interface VideoStudioProps {
+ onBack: () => void
+}
+
+export default function VideoStudio({ onBack }: VideoStudioProps) {
+ const [isPlaying, setIsPlaying] = useState(false)
+ const [videoFile, setVideoFile] = useState(null)
+ const videoRef = useRef(null)
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ setVideoFile(e.target.files[0])
+
+ // Create object URL for preview
+ const videoElement = videoRef.current
+ if (videoElement) {
+ videoElement.src = URL.createObjectURL(e.target.files[0])
+ }
+ }
+ }
+
+ const togglePlay = () => {
+ const videoElement = videoRef.current
+ if (!videoElement) return
+
+ if (isPlaying) {
+ videoElement.pause()
+ } else {
+ videoElement.play()
+ }
+
+ setIsPlaying(!isPlaying)
+ }
+
+ return (
+
+
+ Back to Studios
+
+
+
+
+
VIDEO STUDIO
+
+ {/* Video preview section */}
+
+ {videoFile ? (
+
+
+
+ ) : (
+
+
+
Drop your video file here or
+
document.getElementById("video-upload")?.click()}
+ >
+ Select Video
+
+
+
+ )}
+
+
+ {/* Timeline */}
+ {videoFile && (
+
+
+
+ {/* Timeline tracks would go here */}
+
+
+
+ {/* Playback controls */}
+
+
+
+
+
+ {isPlaying ? : }
+
+
+
+
+
+
+
+ )}
+
+ {/* Mobile-only controls */}
+
+
+
+ Video Controls
+
+
+
+
+ Split Clip
+
+
+
+ Add Layer
+
+
+
+ Playback Speed
+
+
+
+
+ Volume
+
+
+
+
+
+ Save
+
+
+ Export
+
+
+
+
+
+
+
+ {/* Desktop-only controls panel */}
+
+
+
+
Video Controls
+
+
+
+
+
+ Edit
+
+
+ Effects
+
+
+ Text
+
+
+
+
+
+ Split Clip
+
+
+
+ Add Layer
+
+
+
+ Playback Speed
+
+
+
+
+ Volume
+
+
+
+
+
+
+ Brightness
+
+
+
+
+ Contrast
+
+
+
+
+ Saturation
+
+
+
+
+ Stabilization
+
+
+
+
+
+
+ Add Title
+
+
+
+ Add Caption
+
+
+
+ Add Sticker
+
+
+
+
+
+
+ Save Project
+
+
+ Export Video
+
+
+
+
+
+
+
+ )
+}
diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx
new file mode 100644
index 00000000..55c2f6eb
--- /dev/null
+++ b/components/theme-provider.tsx
@@ -0,0 +1,11 @@
+'use client'
+
+import * as React from 'react'
+import {
+ ThemeProvider as NextThemesProvider,
+ type ThemeProviderProps,
+} from 'next-themes'
+
+export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
+ return {children}
+}
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
new file mode 100644
index 00000000..ee95f41a
--- /dev/null
+++ b/components/ui/button.tsx
@@ -0,0 +1,56 @@
+import * as React from 'react'
+import { Slot } from '@radix-ui/react-slot'
+import { cva, type VariantProps } from 'class-variance-authority'
+
+import { cn } from '@/lib/utils'
+
+const buttonVariants = cva(
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
+ {
+ variants: {
+ variant: {
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+ destructive:
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
+ outline:
+ 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
+ secondary:
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
+ link: 'text-primary underline-offset-4 hover:underline',
+ },
+ size: {
+ default: 'h-10 px-4 py-2',
+ sm: 'h-9 rounded-md px-3',
+ lg: 'h-11 rounded-md px-8',
+ icon: 'h-10 w-10',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ },
+)
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : 'button'
+ return (
+
+ )
+ },
+)
+Button.displayName = 'Button'
+
+export { Button, buttonVariants }
diff --git a/components/ui/card.tsx b/components/ui/card.tsx
new file mode 100644
index 00000000..1aab4ef4
--- /dev/null
+++ b/components/ui/card.tsx
@@ -0,0 +1,79 @@
+import * as React from 'react'
+
+import { cn } from '@/lib/utils'
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Card.displayName = 'Card'
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardHeader.displayName = 'CardHeader'
+
+const CardTitle = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardTitle.displayName = 'CardTitle'
+
+const CardDescription = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardDescription.displayName = 'CardDescription'
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardContent.displayName = 'CardContent'
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardFooter.displayName = 'CardFooter'
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/components/ui/collapsible.tsx b/components/ui/collapsible.tsx
new file mode 100644
index 00000000..1bbaed5a
--- /dev/null
+++ b/components/ui/collapsible.tsx
@@ -0,0 +1,11 @@
+'use client'
+
+import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'
+
+const Collapsible = CollapsiblePrimitive.Root
+
+const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
+
+const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
+
+export { Collapsible, CollapsibleTrigger, CollapsibleContent }
diff --git a/components/ui/label.tsx b/components/ui/label.tsx
new file mode 100644
index 00000000..fb5c5454
--- /dev/null
+++ b/components/ui/label.tsx
@@ -0,0 +1,26 @@
+'use client'
+
+import * as React from 'react'
+import * as LabelPrimitive from '@radix-ui/react-label'
+import { cva, type VariantProps } from 'class-variance-authority'
+
+import { cn } from '@/lib/utils'
+
+const labelVariants = cva(
+ 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
+)
+
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, ...props }, ref) => (
+
+))
+Label.displayName = LabelPrimitive.Root.displayName
+
+export { Label }
diff --git a/components/ui/select.tsx b/components/ui/select.tsx
new file mode 100644
index 00000000..266754e8
--- /dev/null
+++ b/components/ui/select.tsx
@@ -0,0 +1,160 @@
+'use client'
+
+import * as React from 'react'
+import * as SelectPrimitive from '@radix-ui/react-select'
+import { Check, ChevronDown, ChevronUp } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+
+const Select = SelectPrimitive.Root
+
+const SelectGroup = SelectPrimitive.Group
+
+const SelectValue = SelectPrimitive.Value
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1',
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+))
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = 'popper', ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+))
+SelectContent.displayName = SelectPrimitive.Content.displayName
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectLabel.displayName = SelectPrimitive.Label.displayName
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+
+ {children}
+
+))
+SelectItem.displayName = SelectPrimitive.Item.displayName
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+}
diff --git a/components/ui/sheet.tsx b/components/ui/sheet.tsx
new file mode 100644
index 00000000..f5fa7aa3
--- /dev/null
+++ b/components/ui/sheet.tsx
@@ -0,0 +1,140 @@
+'use client'
+
+import * as React from 'react'
+import * as SheetPrimitive from '@radix-ui/react-dialog'
+import { cva, type VariantProps } from 'class-variance-authority'
+import { X } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+
+const Sheet = SheetPrimitive.Root
+
+const SheetTrigger = SheetPrimitive.Trigger
+
+const SheetClose = SheetPrimitive.Close
+
+const SheetPortal = SheetPrimitive.Portal
+
+const SheetOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
+
+const sheetVariants = cva(
+ 'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
+ {
+ variants: {
+ side: {
+ top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
+ bottom:
+ 'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
+ left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
+ right:
+ 'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
+ },
+ },
+ defaultVariants: {
+ side: 'right',
+ },
+ },
+)
+
+interface SheetContentProps
+ extends React.ComponentPropsWithoutRef,
+ VariantProps {}
+
+const SheetContent = React.forwardRef<
+ React.ElementRef,
+ SheetContentProps
+>(({ side = 'right', className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+))
+SheetContent.displayName = SheetPrimitive.Content.displayName
+
+const SheetHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+SheetHeader.displayName = 'SheetHeader'
+
+const SheetFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+SheetFooter.displayName = 'SheetFooter'
+
+const SheetTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SheetTitle.displayName = SheetPrimitive.Title.displayName
+
+const SheetDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SheetDescription.displayName = SheetPrimitive.Description.displayName
+
+export {
+ Sheet,
+ SheetPortal,
+ SheetOverlay,
+ SheetTrigger,
+ SheetClose,
+ SheetContent,
+ SheetHeader,
+ SheetFooter,
+ SheetTitle,
+ SheetDescription,
+}
diff --git a/components/ui/slider.tsx b/components/ui/slider.tsx
new file mode 100644
index 00000000..8ee21327
--- /dev/null
+++ b/components/ui/slider.tsx
@@ -0,0 +1,28 @@
+'use client'
+
+import * as React from 'react'
+import * as SliderPrimitive from '@radix-ui/react-slider'
+
+import { cn } from '@/lib/utils'
+
+const Slider = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+
+))
+Slider.displayName = SliderPrimitive.Root.displayName
+
+export { Slider }
diff --git a/components/ui/switch.tsx b/components/ui/switch.tsx
new file mode 100644
index 00000000..c4591b93
--- /dev/null
+++ b/components/ui/switch.tsx
@@ -0,0 +1,29 @@
+'use client'
+
+import * as React from 'react'
+import * as SwitchPrimitives from '@radix-ui/react-switch'
+
+import { cn } from '@/lib/utils'
+
+const Switch = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+Switch.displayName = SwitchPrimitives.Root.displayName
+
+export { Switch }
diff --git a/components/ui/tabs.tsx b/components/ui/tabs.tsx
new file mode 100644
index 00000000..03caf144
--- /dev/null
+++ b/components/ui/tabs.tsx
@@ -0,0 +1,55 @@
+'use client'
+
+import * as React from 'react'
+import * as TabsPrimitive from '@radix-ui/react-tabs'
+
+import { cn } from '@/lib/utils'
+
+const Tabs = TabsPrimitive.Root
+
+const TabsList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+
+const TabsTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+
+const TabsContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/components/ui/textarea.tsx b/components/ui/textarea.tsx
new file mode 100644
index 00000000..09604631
--- /dev/null
+++ b/components/ui/textarea.tsx
@@ -0,0 +1,22 @@
+import * as React from 'react'
+
+import { cn } from '@/lib/utils'
+
+const Textarea = React.forwardRef<
+ HTMLTextAreaElement,
+ React.ComponentProps<'textarea'>
+>(({ className, ...props }, ref) => {
+ return (
+
+ )
+})
+Textarea.displayName = 'Textarea'
+
+export { Textarea }
diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile
deleted file mode 100644
index f224e288..00000000
--- a/gemfiles/rails_6.1.gemfile
+++ /dev/null
@@ -1,18 +0,0 @@
-source 'http://rubygems.org/'
-
-gem 'activerecord', '~> 6.1.0'
-gem 'rspec', '~> 3.9'
-gem 'rake', '~> 13.0'
-gem 'json'
-gem 'test-unit'
-gem 'concurrent-ruby', '< 1.3.5' # to avoid problem described in https://github.com/rails/rails/pull/54264
-
-platform :jruby do
- gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6'
-end
-
-platform :ruby do
- gem 'sqlite3', '~> 1.4', '< 2.0' # can allow 2.0 once Rails's sqlite adapter allows it
-end
-
-gemspec :path => '../'
diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile
deleted file mode 100644
index 804c699e..00000000
--- a/gemfiles/rails_7.0.gemfile
+++ /dev/null
@@ -1,18 +0,0 @@
-source 'http://rubygems.org/'
-
-gem 'activerecord', '~> 7.0.0'
-gem 'rspec', '~> 3.9'
-gem 'rake', '~> 13.0'
-gem 'json'
-gem 'test-unit'
-gem 'concurrent-ruby', '< 1.3.5' # to avoid problem described in https://github.com/rails/rails/pull/54264
-
-platform :jruby do
- gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6'
-end
-
-platform :ruby do
- gem 'sqlite3', '~> 1.4', '< 2.0' # can allow 2.0 once Rails's sqlite adapter allows it
-end
-
-gemspec :path => '../'
diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile
deleted file mode 100644
index d70255ef..00000000
--- a/gemfiles/rails_7.1.gemfile
+++ /dev/null
@@ -1,17 +0,0 @@
-source 'http://rubygems.org/'
-
-gem 'activerecord', '~> 7.1.0'
-gem 'rspec', '~> 3.9'
-gem 'rake', '~> 13.0'
-gem 'json'
-gem 'test-unit'
-
-platform :jruby do
- gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6'
-end
-
-platform :ruby do
- gem 'sqlite3', '~> 1.4', '< 2.0' # can allow 2.0 once Rails's sqlite adapter allows it
-end
-
-gemspec :path => '../'
diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile
deleted file mode 100644
index fd6478a8..00000000
--- a/gemfiles/rails_7.2.gemfile
+++ /dev/null
@@ -1,17 +0,0 @@
-source 'http://rubygems.org/'
-
-gem 'activerecord', '~> 7.2.0'
-gem 'rspec', '~> 3.9'
-gem 'rake', '~> 13.0'
-gem 'json'
-gem 'test-unit'
-
-platform :jruby do
- gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6'
-end
-
-platform :ruby do
- gem 'sqlite3', '~> 1.4', '< 2.0' # can allow 2.0 once Rails's sqlite adapter allows it
-end
-
-gemspec :path => '../'
diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile
deleted file mode 100644
index 2fca2ca9..00000000
--- a/gemfiles/rails_8.0.gemfile
+++ /dev/null
@@ -1,17 +0,0 @@
-source 'http://rubygems.org/'
-
-gem 'activerecord', '~> 8.0.0.beta1'
-gem 'rspec', '~> 3.13'
-gem 'rake', '~> 13.2'
-gem 'json'
-gem 'test-unit'
-
-platform :jruby do
- gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.6'
-end
-
-platform :ruby do
- gem 'sqlite3'
-end
-
-gemspec :path => '../'
diff --git a/lib/active_file/base.rb b/lib/active_file/base.rb
deleted file mode 100644
index 83b0b0b5..00000000
--- a/lib/active_file/base.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-module ActiveFile
-
- class Base < ActiveHash::Base
- extend ActiveFile::MultipleFiles
- @@instance_lock = Mutex.new
-
- class_attribute :filename, :root_path, :data_loaded, instance_reader: false, instance_writer: false
-
- class << self
-
- def delete_all
- self.data_loaded = true
- super
- end
-
- def reload(force = false)
- @@instance_lock.synchronize do
- return if !self.dirty && !force && self.data_loaded
- self.data = load_file
- mark_clean
- self.data_loaded = true
- end
- end
-
- def set_filename(name)
- self.filename = name
- end
-
- def set_root_path(path)
- self.root_path = path
- end
-
- def load_file
- raise "Override Me"
- end
-
- def full_path
- actual_filename = filename || name.tableize
- File.join(actual_root_path, "#{actual_filename}.#{extension}")
- end
-
- def extension
- raise "Override Me"
- end
- protected :extension
-
- def actual_root_path
- root_path || Dir.pwd
- end
- protected :actual_root_path
-
- [:find, :find_by_id, :all, :where, :method_missing, :find_each].each do |method|
- define_method(method) do |*args, &block|
- reload unless data_loaded
- return super(*args, &block)
- end
- end
-
- def all_in_process
- return super if data_loaded
- @records || []
- end
- end
- end
-
-end
diff --git a/lib/active_file/hash_and_array_files.rb b/lib/active_file/hash_and_array_files.rb
deleted file mode 100644
index c1d63cad..00000000
--- a/lib/active_file/hash_and_array_files.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-module ActiveFile
- module HashAndArrayFiles
- def raw_data
- if multiple_files?
- data_from_multiple_files
- else
- load_path(full_path)
- end
- end
-
- private
- def data_from_multiple_files
- loaded_files = full_paths.collect { |path| load_path(path) }
-
- if loaded_files.all?{ |file_data| file_data.is_a?(Array) }
- loaded_files.sum([])
- elsif loaded_files.all?{ |file_data| file_data.is_a?(Hash) }
- loaded_files.inject({}) { |hash, file_data| hash.merge(file_data) }
- else
- raise ActiveHash::FileTypeMismatchError.new("Choose between hash or array syntax")
- end
- end
- end
-end
diff --git a/lib/active_file/multiple_files.rb b/lib/active_file/multiple_files.rb
deleted file mode 100644
index 256a6fdc..00000000
--- a/lib/active_file/multiple_files.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-module ActiveFile
- module MultipleFiles
- def multiple_files?
- false
- end
-
- def use_multiple_files
- class_attribute :filenames, instance_reader: false, instance_writer: false
-
- def self.set_filenames(*filenames)
- self.filenames = filenames
- end
-
- def self.multiple_files?
- true
- end
-
- def self.full_paths
- if filenames.present?
- filenames.collect do |filename|
- File.join(actual_root_path, "#{filename}.#{extension}")
- end
- else
- [full_path]
- end
- end
- end
- end
-end
diff --git a/lib/active_hash.rb b/lib/active_hash.rb
deleted file mode 100644
index 44032b6e..00000000
--- a/lib/active_hash.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require 'active_support'
-
-begin
- require 'active_support/core_ext'
-rescue
-end
-
-begin
- require 'active_model'
- require 'active_model/naming'
-rescue LoadError
-end
-
-require 'active_hash/base'
-require 'active_hash/relation'
-require 'active_hash/condition'
-require 'active_hash/conditions'
-require 'active_file/multiple_files'
-require 'active_file/hash_and_array_files'
-require 'active_file/base'
-require 'active_yaml/base'
-require 'active_yaml/aliases'
-require 'active_json/base'
-require 'associations/associations'
-require 'enum/enum'
diff --git a/lib/active_hash/base.rb b/lib/active_hash/base.rb
deleted file mode 100644
index 4bffad29..00000000
--- a/lib/active_hash/base.rb
+++ /dev/null
@@ -1,532 +0,0 @@
-module ActiveHash
- class RecordNotFound < StandardError
- attr_reader :model, :primary_key, :id
-
- def initialize(message = nil, model = nil, primary_key = nil, id = nil)
- @primary_key = primary_key
- @model = model
- @id = id
-
- super(message)
- end
- end
-
- class ReservedFieldError < StandardError
- end
-
- class IdError < StandardError
- end
-
- class FileTypeMismatchError < StandardError
- end
-
- class Base
- class_attribute :_data, :dirty, :default_attributes, :scopes
-
- if Object.const_defined?(:ActiveModel)
- extend ActiveModel::Naming
- extend ActiveModel::Translation
- include ActiveModel::Conversion
- else
- def to_param
- id.present? ? id.to_s : nil
- end
- end
-
- class << self
-
- def cache_key
- if Object.const_defined?(:ActiveModel)
- model_name.cache_key
- else
- ActiveSupport::Inflector.tableize(self.name).downcase
- end
- end
-
- def primary_key
- "id"
- end
-
- def field_names
- @field_names ||= []
- end
-
- #
- # Useful for CSV integration needing column names as strings.
- #
- # @return [Array] An array of column names as strings.
- #
- # @example Usage
- # class Country < ActiveHash::Base
- # fields :name, :code
- # end
- #
- # Country.column_names
- # # => ["id", "name", "code"]
- #
- def column_names
- field_names.map(&:name)
- end
-
- def the_meta_class
- class << self
- self
- end
- end
-
- def compute_type(type_name)
- self
- end
-
- def pluralize_table_names
- true
- end
-
- def empty?
- false
- end
-
- def data
- _data
- end
-
- def data=(array_of_hashes)
- mark_dirty
- @records = nil
- reset_record_index
- self._data = array_of_hashes
- if array_of_hashes
- auto_assign_fields(array_of_hashes)
- array_of_hashes.each do |hash|
- insert new(hash)
- end
- end
- end
-
- def exists?(args = :none)
- if args.respond_to?(:id)
- record_index[args.id.to_s].present?
- elsif !args
- false
- elsif args == :none
- all.present?
- elsif args.is_a?(Hash)
- all.where(args).present?
- else
- all.where(id: args.to_i).present?
- end
- end
-
- def insert(record)
- @records ||= []
- record[:id] ||= next_id
- validate_unique_id(record) if dirty
- mark_dirty
-
- add_to_record_index({ record.id.to_s => @records.length })
- @records << record
- end
-
- def next_id
- max_record = all_in_process.max { |a, b| a.id <=> b.id }
- if max_record.nil?
- 1
- elsif max_record.id.is_a?(Numeric)
- max_record.id.succ
- end
- end
-
- def all_in_process
- all
- end
- private :all_in_process
-
- def record_index
- @record_index ||= {}
- end
-
- def has_query_constraints?
- false
- end
-
- private :record_index
-
- def reset_record_index
- record_index.clear
- end
-
- private :reset_record_index
-
- def add_to_record_index(entry)
- record_index.merge!(entry)
- end
-
- private :add_to_record_index
-
- def validate_unique_id(record)
- raise IdError.new("Duplicate ID found for record #{record.attributes.inspect}") if record_index.has_key?(record.id.to_s)
- end
-
- private :validate_unique_id
-
- def create(attributes = {})
- record = new(attributes)
- record.save
- mark_dirty
- record
- end
-
- alias_method :add, :create
-
- def create!(attributes = {})
- record = new(attributes)
- record.save!
- record
- end
-
- def all(options = {})
- relation = ActiveHash::Relation.new(self, @records || [])
- relation = relation.where!(options[:conditions]) if options[:conditions]
- relation
- end
-
- delegate :where, :find_each, :find, :find_by, :find_by!, :find_by_id, :count, :pluck, :ids, :pick, :first, :last, :order, to: :all
-
- def transaction
- yield
- rescue LocalJumpError => err
- raise err
- rescue StandardError => e
- unless Object.const_defined?(:ActiveRecord) && e.is_a?(ActiveRecord::Rollback)
- raise e
- end
- end
-
- def delete_all
- mark_dirty
- reset_record_index
- @records = []
- end
-
- def fields(*args)
- options = args.extract_options!
- args.each do |field|
- field(field, options)
- end
- end
-
- def field(field_name, options = {})
- field_name = field_name.to_sym
- validate_field(field_name)
-
- field_names << field_name
-
- add_default_value(field_name, options[:default]) if options.key?(:default)
- define_getter_method(field_name, options[:default])
- define_setter_method(field_name)
- define_interrogator_method(field_name)
- define_custom_find_method(field_name)
- define_custom_find_all_method(field_name)
- end
-
- def validate_field(field_name)
- field_name = field_name.to_sym
- if [:attributes].include?(field_name)
- raise ReservedFieldError.new("#{field_name} is a reserved field in ActiveHash. Please use another name.")
- end
- end
-
- private :validate_field
-
- def respond_to?(method_name, include_private=false)
- super ||
- begin
- config = configuration_for_custom_finder(method_name)
- config && config[:fields].all? do |field|
- field_names.include?(field.to_sym) || field.to_sym == :id
- end
- end
- end
-
- def method_missing(method_name, *args)
- return super unless respond_to? method_name
-
- config = configuration_for_custom_finder(method_name)
- attribute_pairs = config[:fields].zip(args)
- matches = all.select { |base| attribute_pairs.all? { |field, value| base.send(field).to_s == value.to_s } }
-
- if config[:all?]
- matches
- else
- result = matches.first
- if config[:bang?]
- result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attribute_pairs.collect { |pair| "#{pair[0]} = #{pair[1]}" }.join(', ')}")
- else
- result
- end
- end
- end
-
- def configuration_for_custom_finder(finder_name)
- if finder_name.to_s.match(/^find_(all_)?by_(.*?)(!)?$/) && !($1 && $3)
- {
- :all? => !!$1,
- :bang? => !!$3,
- :fields => $2.split('_and_')
- }
- end
- end
-
- private :configuration_for_custom_finder
-
- def add_default_value field_name, default_value
- self.default_attributes ||= {}
- self.default_attributes[field_name] = default_value
- end
-
- private :add_default_value
-
- def define_getter_method(field, default_value)
- unless instance_methods.include?(field)
- define_method(field) do
- attributes[field].nil? ? default_value : attributes[field]
- end
- end
- end
-
- private :define_getter_method
-
- def define_setter_method(field)
- method_name = :"#{field}="
- unless instance_methods.include?(method_name)
- define_method(method_name) do |new_val|
- @attributes[field] = new_val
- end
- end
- end
-
- private :define_setter_method
-
- def define_interrogator_method(field)
- method_name = :"#{field}?"
- unless instance_methods.include?(method_name)
- define_method(method_name) do
- send(field).present?
- end
- end
- end
-
- private :define_interrogator_method
-
- def define_custom_find_method(field_name)
- method_name = :"find_by_#{field_name}"
- unless singleton_methods.include?(method_name)
- the_meta_class.instance_eval do
- define_method(method_name) do |*args|
- args.extract_options!
- identifier = args[0]
- all.detect { |record| record.send(field_name) == identifier }
- end
- end
- end
- end
-
- private :define_custom_find_method
-
- def define_custom_find_all_method(field_name)
- method_name = :"find_all_by_#{field_name}"
- unless singleton_methods.include?(method_name)
- the_meta_class.instance_eval do
- unless singleton_methods.include?(method_name)
- define_method(method_name) do |*args|
- args.extract_options!
- identifier = args[0]
- all.select { |record| record.send(field_name) == identifier }
- end
- end
- end
- end
- end
-
- private :define_custom_find_all_method
-
- def auto_assign_fields(array_of_hashes)
- (array_of_hashes || []).inject([]) do |array, row|
- row.symbolize_keys!
- row.keys.each do |key|
- unless key.to_s == "id"
- array << key
- end
- end
- array
- end.uniq.each do |key|
- field key
- end
- end
-
- private :auto_assign_fields
-
- # Needed for ActiveRecord polymorphic associations
- def base_class
- ActiveHash::Base
- end
-
- # Needed for ActiveRecord polymorphic associations(rails/rails#32148)
- def polymorphic_name
- base_class.name
- end
-
- # Needed for ActiveRecord since rails/rails#47664
- def composite_primary_key?
- false
- end
-
- def reload
- reset_record_index
- self.data = _data
- mark_clean
- end
-
- private :reload
-
- def mark_dirty
- self.dirty = true
- end
-
- private :mark_dirty
-
- def mark_clean
- self.dirty = false
- end
-
- private :mark_clean
-
- def scope(name, body)
- raise ArgumentError, 'body needs to be callable' unless body.respond_to?(:call)
-
- self.scopes ||= {}
- self.scopes[name] = body
-
- the_meta_class.instance_eval do
- define_method(name) do |*args|
- instance_exec(*args, &body)
- end
- end
- end
-
- end
-
- def initialize(attributes = {})
- attributes.symbolize_keys!
- @attributes = attributes
- attributes.dup.each do |key, value|
- send "#{key}=", value
- end
- yield self if block_given?
- end
-
- def attributes
- if self.class.default_attributes
- (self.class.default_attributes.merge @attributes).freeze
- else
- @attributes
- end
- end
-
- def [](key)
- attributes[key]
- end
-
- def _read_attribute(key)
- attributes[key.to_sym]
- end
- alias_method :read_attribute, :_read_attribute
-
- def []=(key, val)
- @attributes[key] = val
- end
-
- def id
- attributes[:id] ? attributes[:id] : nil
- end
-
- def id=(id)
- @attributes[:id] = id
- end
-
- alias quoted_id id
-
- def new_record?
- !self.class.all.include?(self)
- end
-
- def destroyed?
- false
- end
-
- def persisted?
- self.class.all.map(&:id).include?(id)
- end
-
- def readonly?
- true
- end
-
- def eql?(other)
- other.instance_of?(self.class) and not id.nil? and (id == other.id)
- end
-
- alias == eql?
-
- def hash
- id.hash
- end
-
- def cache_key
- case
- when new_record?
- "#{self.class.cache_key}/new"
- when timestamp = self[:updated_at]
- if ActiveSupport::VERSION::MAJOR < 7
- "#{self.class.cache_key}/#{id}-#{timestamp.to_s(:number)}"
- else
- "#{self.class.cache_key}/#{id}-#{timestamp.to_fs(:number)}"
- end
- else
- "#{self.class.cache_key}/#{id}"
- end
- end
-
- def errors
- obj = Object.new
-
- def obj.[](key)
- []
- end
-
- def obj.full_messages()
- []
- end
-
- obj
- end
-
- def save(*args)
- unless self.class.exists?(self)
- self.class.insert(self)
- end
- true
- end
-
- alias save! save
-
- def valid?
- true
- end
-
- def marked_for_destruction?
- false
- end
-
- end
-end
diff --git a/lib/active_hash/condition.rb b/lib/active_hash/condition.rb
deleted file mode 100644
index 26dce78d..00000000
--- a/lib/active_hash/condition.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-class ActiveHash::Relation::Condition
- attr_reader :constraints, :inverted
-
- def initialize(constraints)
- @constraints = constraints
- @inverted = false
- end
-
- def invert!
- @inverted = !inverted
-
- self
- end
-
- def matches?(record)
- match = begin
- return true unless constraints
-
- expectation_method = inverted ? :any? : :all?
-
- constraints.send(expectation_method) do |attribute, expected|
- value = record.read_attribute(attribute)
-
- matches_value?(value, expected)
- end
- end
-
- inverted ? !match : match
- end
-
- private
-
- def matches_value?(value, comparison)
- return comparison.any? { |v| matches_value?(value, v) } if comparison.is_a?(Array)
- return comparison.cover?(value) if comparison.is_a?(Range)
- return comparison.match?(value) if comparison.is_a?(Regexp)
-
- normalize(value) == normalize(comparison)
- end
-
- def normalize(value)
- value.respond_to?(:to_s) ? value&.to_s : value
- end
-end
diff --git a/lib/active_hash/conditions.rb b/lib/active_hash/conditions.rb
deleted file mode 100644
index ebbd755c..00000000
--- a/lib/active_hash/conditions.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-class ActiveHash::Relation::Conditions
- attr_reader :conditions
-
- delegate :<<, :map, to: :conditions
-
- def initialize(conditions = [])
- @conditions = conditions
- end
-
- def matches?(record)
- conditions.all? do |condition|
- condition.matches?(record)
- end
- end
-
- def self.wrap(conditions)
- return conditions if conditions.is_a?(self)
-
- new(conditions)
- end
-end
\ No newline at end of file
diff --git a/lib/active_hash/relation.rb b/lib/active_hash/relation.rb
deleted file mode 100644
index 47b9afdb..00000000
--- a/lib/active_hash/relation.rb
+++ /dev/null
@@ -1,230 +0,0 @@
-module ActiveHash
- class Relation
- include Enumerable
-
- delegate :each, to: :records # Make Enumerable work
- delegate :equal?, :==, :===, :eql?, :sort!, to: :records
- delegate :empty?, :length, :first, :second, :third, :last, to: :records
- delegate :sample, to: :records
-
- alias find_each each
-
- attr_reader :conditions, :order_values, :klass, :all_records
-
- def initialize(klass, all_records, conditions = nil, order_values = nil)
- self.klass = klass
- self.all_records = all_records
- self.conditions = Conditions.wrap(conditions || [])
- self.order_values = order_values || []
- end
-
- def where(conditions_hash = :chain)
- return WhereChain.new(self) if conditions_hash == :chain
-
- spawn.where!(conditions_hash)
- end
-
- def pretty_print(pp)
- pp.pp(entries.to_ary)
- end
-
- class WhereChain
- attr_reader :relation
-
- def initialize(relation)
- @relation = relation
- end
-
- def not(conditions_hash)
- relation.conditions << Condition.new(conditions_hash).invert!
- relation
- end
- end
-
- def order(*options)
- spawn.order!(*options)
- end
-
- def reorder(*options)
- spawn.reorder!(*options)
- end
-
- def where!(conditions_hash, inverted = false)
- self.conditions << Condition.new(conditions_hash)
- self
- end
-
- def invert_where
- spawn.invert_where!
- end
-
- def invert_where!
- conditions.map(&:invert!)
- self
- end
-
- def spawn
- self.class.new(klass, all_records, conditions, order_values)
- end
-
- def order!(*options)
- check_if_method_has_arguments!(:order, options)
- self.order_values += preprocess_order_args(options)
- self
- end
-
- def reorder!(*options)
- check_if_method_has_arguments!(:order, options)
-
- self.order_values = preprocess_order_args(options)
- @records = apply_order_values(records, order_values)
-
- self
- end
-
- def records
- @records ||= begin
- filtered_records = apply_conditions(all_records, conditions)
- ordered_records = apply_order_values(filtered_records, order_values) # rubocop:disable Lint/UselessAssignment
- end
- end
-
- def reload
- @records = nil # Reset records
- self
- end
-
- def all(options = {})
- if options.key?(:conditions)
- where(options[:conditions])
- else
- where({})
- end
- end
-
- def find_by(options)
- where(options).first
- end
-
- def find_by!(options)
- find_by(options) || (raise RecordNotFound.new("Couldn't find #{klass.name}", klass.name))
- end
-
- def find(id = nil, *args, &block)
- case id
- when :all
- all
- when :first
- all(*args).first
- when Array
- id.map { |i| find(i) }
- when nil
- raise RecordNotFound.new("Couldn't find #{klass.name} without an ID", klass.name, "id") unless block_given?
- records.find(&block) # delegate to Enumerable#find if a block is given
- else
- find_by_id(id) || begin
- raise RecordNotFound.new("Couldn't find #{klass.name} with ID=#{id}", klass.name, "id", id)
- end
- end
- end
-
- def find_by_id(id)
- index = klass.send(:record_index)[id.to_s] # TODO: Make index in Base publicly readable instead of using send?
- return unless index
-
- record = all_records[index]
- record if conditions.matches?(record)
- end
-
- def count
- return super if block_given?
- length
- end
-
- def size
- length
- end
-
- def pluck(*column_names)
- if column_names.length == 1
- column_name = column_names.first
- all.map { |record| record.public_send(column_name) }
- else
- all.map { |record| column_names.map { |column_name| record.public_send(column_name) } }
- end
- end
-
- def ids
- pluck(:id)
- end
-
- def pick(*column_names)
- pluck(*column_names).first
- end
-
- def to_ary
- records.dup
- end
-
- def method_missing(method_name, *args)
- return super unless klass.scopes&.key?(method_name)
-
- instance_exec(*args, &klass.scopes[method_name])
- end
-
- def respond_to_missing?(method_name, include_private = false)
- klass.scopes&.key?(method_name) || super
- end
-
- private
-
- attr_writer :conditions, :order_values, :klass, :all_records
-
- def apply_conditions(records, conditions)
- return records if conditions.blank?
-
- records.select do |record|
- conditions.matches?(record)
- end
- end
-
- def check_if_method_has_arguments!(method_name, args)
- return unless args.blank?
-
- raise ArgumentError,
- "The method .#{method_name}() must contain arguments."
- end
-
- def preprocess_order_args(order_args)
- order_args.reject!(&:blank?)
- return order_args.reverse! unless order_args.first.is_a?(String)
-
- ary = order_args.first.split(', ')
- ary.map! { |e| e.split(/\W+/) }.reverse!
- end
-
- def apply_order_values(records, args)
- ordered_records = records.dup
-
- args.each do |arg|
- field, dir = if arg.is_a?(Hash)
- arg.to_a.flatten.map(&:to_sym)
- elsif arg.is_a?(Array)
- arg.map(&:to_sym)
- else
- arg.to_sym
- end
-
- ordered_records.sort! do |a, b|
- if dir.present? && dir.to_sym.upcase.equal?(:DESC)
- b[field] <=> a[field]
- else
- a[field] <=> b[field]
- end
- end
- end
-
- ordered_records
- end
- end
-end
diff --git a/lib/active_hash/version.rb b/lib/active_hash/version.rb
deleted file mode 100644
index f1ded36f..00000000
--- a/lib/active_hash/version.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module ActiveHash
- module Gem
- VERSION = "4.0.0"
- end
-end
diff --git a/lib/active_json/base.rb b/lib/active_json/base.rb
deleted file mode 100644
index 46ace109..00000000
--- a/lib/active_json/base.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module ActiveJSON
- class Base < ActiveFile::Base
- extend ActiveFile::HashAndArrayFiles
- class << self
- def load_file
- if (data = raw_data).is_a?(Array)
- data
- elsif data.respond_to?(:values)
- data.values
- end
- end
-
- def extension
- "json"
- end
-
- private
- def load_path(path)
- JSON.load(File.open(path, 'r:bom|utf-8'))
- end
-
- end
- end
-
-end
diff --git a/lib/active_yaml/aliases.rb b/lib/active_yaml/aliases.rb
deleted file mode 100644
index 05018628..00000000
--- a/lib/active_yaml/aliases.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-module ActiveYaml
-
- module Aliases
- def self.included(base)
- base.extend(ClassMethods)
- end
-
- ALIAS_KEY_REGEXP = /^\//.freeze
-
- module ClassMethods
-
- def insert(record)
- super if record.attributes.present?
- end
-
- def raw_data
- d = super
- if d.kind_of?(Array)
- d.reject do |h|
- h.keys.any? { |k| k.match(ALIAS_KEY_REGEXP) }
- end
- else
- d.reject do |k, v|
- v.kind_of?(Hash) && k.match(ALIAS_KEY_REGEXP)
- end
- end
- end
-
- end
- end
-
-end
diff --git a/lib/active_yaml/base.rb b/lib/active_yaml/base.rb
deleted file mode 100644
index 98f997f2..00000000
--- a/lib/active_yaml/base.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require 'yaml'
-
-module ActiveYaml
-
- class Base < ActiveFile::Base
- extend ActiveFile::HashAndArrayFiles
-
- cattr_accessor :process_erb, instance_accessor: false
- @@process_erb = true
-
- class << self
- def load_file
- if (data = raw_data).is_a?(Array)
- data
- elsif data.respond_to?(:values)
- data.map{ |key, value| {"key" => key}.merge(value) }
- end
- end
-
- def extension
- "yml"
- end
-
- private
-if Psych::VERSION >= "4.0.0"
- def load_path(path)
- result = File.read(path)
- result = ERB.new(result).result if process_erb
- YAML.unsafe_load(result)
- end
-else
- def load_path(path)
- result = File.read(path)
- result = ERB.new(result).result if process_erb
- YAML.load(result)
- end
-end
- end
- end
-end
diff --git a/lib/associations/associations.rb b/lib/associations/associations.rb
deleted file mode 100644
index 3ffd77d2..00000000
--- a/lib/associations/associations.rb
+++ /dev/null
@@ -1,179 +0,0 @@
-module ActiveHash
- module Associations
-
- module ActiveRecordExtensions
- def self.extended(base)
- require_relative 'reflection_extensions'
- end
-
- def has_many(association_id, scope = nil, **options, &extension)
- if options[:through]
- source_association_name = options[:source]&.to_s || association_id.to_s.singularize
-
- through_klass = reflect_on_association(options[:through])&.klass
- klass = through_klass&.reflect_on_association(source_association_name)&.klass
-
- if klass && klass < ActiveHash::Base
- define_method(association_id) do
- join_models = send(options[:through])
- join_models.flat_map do |join_model|
- join_model.send(source_association_name)
- end.uniq
- end
-
- return
- end
- end
-
- super
- end
-
- def belongs_to(name, scope = nil, **options)
- klass_name = options.key?(:class_name) ? options[:class_name] : name.to_s.camelize
- klass = klass_name.safe_constantize
-
- if klass && klass < ActiveHash::Base
- options = { class_name: klass_name }.merge(options)
- belongs_to_active_hash(name, options)
- else
- super
- end
- end
-
- def belongs_to_active_hash(association_id, options = {})
- options = {
- :class_name => association_id.to_s.camelize,
- :foreign_key => association_id.to_s.foreign_key,
- :shortcuts => []
- }.merge(options)
- # Define default primary_key with provided class_name if any
- options[:primary_key] ||= options[:class_name].safe_constantize.primary_key
- options[:shortcuts] = [options[:shortcuts]] unless options[:shortcuts].kind_of?(Array)
-
- define_method(association_id) do
- options[:class_name].safe_constantize.send("find_by_#{options[:primary_key]}", send(options[:foreign_key]))
- end
-
- define_method("#{association_id}=") do |new_value|
- send "#{options[:foreign_key]}=", new_value ? new_value.send(options[:primary_key]) : nil
- end
-
- options[:shortcuts].each do |shortcut|
- define_method("#{association_id}_#{shortcut}") do
- send(association_id).try(shortcut)
- end
-
- define_method("#{association_id}_#{shortcut}=") do |new_value|
- send "#{association_id}=", new_value ? options[:class_name].safe_constantize.send("find_by_#{shortcut}", new_value) : nil
- end
- end
-
- if ActiveRecord::Reflection.respond_to?(:create)
- if defined?(ActiveHash::Reflection::BelongsToReflection)
- reflection = ActiveHash::Reflection::BelongsToReflection.new(association_id.to_sym, nil, options, self)
- else
- reflection = ActiveRecord::Reflection.create(
- :belongs_to,
- association_id.to_sym,
- nil,
- options,
- self
- )
- end
-
- ActiveRecord::Reflection.add_reflection(
- self,
- association_id.to_sym,
- reflection
- )
- else
- method = ActiveRecord::Base.method(:create_reflection)
- if method.respond_to?(:parameters) && method.parameters.length == 5
- create_reflection(
- :belongs_to,
- association_id.to_sym,
- nil,
- options,
- self
- )
- else
- create_reflection(
- :belongs_to,
- association_id.to_sym,
- options,
- options[:class_name].safe_constantize
- )
- end
- end
- end
- end
-
- def self.included(base)
- base.extend Methods
- end
-
- module Methods
- def has_many(association_id, options = {})
- define_method(association_id) do
- options = {
- :class_name => association_id.to_s.classify,
- :foreign_key => self.class.to_s.foreign_key,
- :primary_key => self.class.primary_key
- }.merge(options)
-
- klass = options[:class_name].safe_constantize
- primary_key_value = send(options[:primary_key])
- foreign_key = options[:foreign_key].to_sym
-
- if Object.const_defined?(:ActiveRecord) && ActiveRecord.const_defined?(:Relation) && klass < ActiveRecord::Relation
- klass.where(foreign_key => primary_key_value)
- elsif klass.respond_to?(:scoped)
- klass.scoped(:conditions => {foreign_key => primary_key_value})
- else
- klass.where(foreign_key => primary_key_value)
- end
- end
-
- define_method("#{association_id.to_s.underscore.singularize}_ids") do
- public_send(association_id).map(&:id)
- end
- end
-
- def has_one(association_id, options = {})
- define_method(association_id) do
- options = {
- :class_name => association_id.to_s.classify,
- :foreign_key => self.class.to_s.foreign_key,
- :primary_key => self.class.primary_key
- }.merge(options)
-
- scope = options[:class_name].safe_constantize
-
- if scope.respond_to?(:scoped) && options[:conditions]
- scope = scope.scoped(:conditions => options[:conditions])
- end
- scope.send("find_by_#{options[:foreign_key]}", send(options[:primary_key]))
- end
- end
-
- def belongs_to(association_id, options = {})
- options = {
- :class_name => association_id.to_s.classify,
- :foreign_key => association_id.to_s.foreign_key,
- :primary_key => "id"
- }.merge(options)
-
- field options[:foreign_key].to_sym
-
- define_method(association_id) do
- options[:class_name].safe_constantize.send("find_by_#{options[:primary_key]}", send(options[:foreign_key]))
- end
-
- define_method("#{association_id}=") do |new_value|
- attributes[options[:foreign_key].to_sym] = new_value ? new_value.send(options[:primary_key]) : nil
- end
- end
- end
-
- end
-end
diff --git a/lib/associations/reflection_extensions.rb b/lib/associations/reflection_extensions.rb
deleted file mode 100644
index 0cd8587e..00000000
--- a/lib/associations/reflection_extensions.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module ActiveHash
- module Reflection
- class BelongsToReflection < ActiveRecord::Reflection::BelongsToReflection
- def compute_class(name)
- if polymorphic?
- raise ArgumentError, "Polymorphic associations do not support computing the class."
- end
-
- begin
- klass = active_record.send(:compute_type, name)
- rescue NameError => error
- if error.name.match?(/(?:\A|::)#{name}\z/)
- message = "Missing model class #{name} for the #{active_record}##{self.name} association."
- message += " You can specify a different model class with the :class_name option." unless options[:class_name]
- raise NameError.new(message, name)
- else
- raise
- end
- end
-
- klass
- end
- end
- end
-end
diff --git a/lib/enum/enum.rb b/lib/enum/enum.rb
deleted file mode 100644
index 3ba5a7c4..00000000
--- a/lib/enum/enum.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-module ActiveHash
- module Enum
-
- DuplicateEnumAccessor = Class.new(RuntimeError)
-
- def self.included(base)
- base.extend(Methods)
- end
-
- module Methods
-
- def enum_accessor(*field_names)
- @enum_accessors = field_names
- reload
- end
-
- def enum(columns)
- columns.each do |column, values|
- values = values.zip(values.map(&:to_s)).to_h if values.is_a?(Array)
- values.each do |method, value|
- class_eval <<~METHOD, __FILE__, __LINE__ + 1
- # frozen_string_literal: true
- def #{method}?
- #{column} == #{value.inspect}
- end
- METHOD
- end
- end
- end
-
- def insert(record)
- super
- set_constant(record) if defined?(@enum_accessors)
- end
-
- def delete_all
- if @enum_accessors.present?
- @records.each do |record|
- constant = constant_for(record, @enum_accessors)
- remove_const(constant) if const_defined?(constant, false)
- end
- end
- super
- end
-
- def set_constant(record)
- constant = constant_for(record, @enum_accessors)
- return nil if constant.blank?
-
- unless const_defined?(constant, false)
- const_set(constant, record)
- else
- raise DuplicateEnumAccessor, "#{constant} already defined for #{self.class}" unless const_get(constant, false) == record
- end
- end
-
- private :set_constant
-
- def constant_for(record, field_names)
- field_value = field_names.map { |name| record.attributes[name] }.join("_")
- if constant = !field_value.nil? && field_value.dup
- constant.gsub!(/\W+/, "_")
- constant.gsub!(/^_|_$/, '')
- constant.upcase!
- constant
- end
- end
-
- private :constant_for
- end
-
- end
-
-end
diff --git a/lib/utils.ts b/lib/utils.ts
new file mode 100644
index 00000000..fed2fe91
--- /dev/null
+++ b/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from 'clsx'
+import { twMerge } from 'tailwind-merge'
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/next.config.mjs b/next.config.mjs
new file mode 100644
index 00000000..1506af39
--- /dev/null
+++ b/next.config.mjs
@@ -0,0 +1,15 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ typescript: {
+ ignoreBuildErrors: true,
+ },
+ images: {
+ unoptimized: true,
+ },
+
+ eslint: {
+ ignoreDuringBuilds: true,
+ },
+}
+
+export default nextConfig
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..ddeba02a
--- /dev/null
+++ b/package.json
@@ -0,0 +1,71 @@
+{
+ "name": "my-v0-project",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "build": "next build",
+ "dev": "next dev",
+ "lint": "eslint .",
+ "start": "next start"
+ },
+ "dependencies": {
+ "@hookform/resolvers": "^3.10.0",
+ "@radix-ui/react-accordion": "1.2.2",
+ "@radix-ui/react-alert-dialog": "1.1.4",
+ "@radix-ui/react-aspect-ratio": "1.1.1",
+ "@radix-ui/react-avatar": "1.1.2",
+ "@radix-ui/react-checkbox": "1.1.3",
+ "@radix-ui/react-collapsible": "1.1.2",
+ "@radix-ui/react-context-menu": "2.2.4",
+ "@radix-ui/react-dialog": "1.1.4",
+ "@radix-ui/react-dropdown-menu": "2.1.4",
+ "@radix-ui/react-hover-card": "1.1.4",
+ "@radix-ui/react-label": "2.1.1",
+ "@radix-ui/react-menubar": "1.1.4",
+ "@radix-ui/react-navigation-menu": "1.2.3",
+ "@radix-ui/react-popover": "1.1.4",
+ "@radix-ui/react-progress": "1.1.1",
+ "@radix-ui/react-radio-group": "1.2.2",
+ "@radix-ui/react-scroll-area": "1.2.2",
+ "@radix-ui/react-select": "2.1.4",
+ "@radix-ui/react-separator": "1.1.1",
+ "@radix-ui/react-slider": "1.2.2",
+ "@radix-ui/react-slot": "1.1.1",
+ "@radix-ui/react-switch": "1.1.2",
+ "@radix-ui/react-tabs": "1.1.2",
+ "@radix-ui/react-toast": "1.2.4",
+ "@radix-ui/react-toggle": "1.1.1",
+ "@radix-ui/react-toggle-group": "1.1.1",
+ "@radix-ui/react-tooltip": "1.1.6",
+ "@vercel/analytics": "1.3.1",
+ "autoprefixer": "^10.4.20",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "cmdk": "1.0.4",
+ "date-fns": "4.1.0",
+ "embla-carousel-react": "8.5.1",
+ "input-otp": "1.4.1",
+ "lucide-react": "^0.454.0",
+ "next": "15.2.8",
+ "next-themes": "^0.4.6",
+ "react": "^19",
+ "react-day-picker": "9.8.0",
+ "react-dom": "^19",
+ "react-hook-form": "^7.60.0",
+ "react-resizable-panels": "^2.1.7",
+ "recharts": "2.15.4",
+ "sonner": "^1.7.4",
+ "tailwind-merge": "^2.5.5",
+ "tailwindcss-animate": "^1.0.7",
+ "vaul": "^1.1.2",
+ "zod": "3.25.76"
+ },
+ "devDependencies": {
+ "@types/node": "^22",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "postcss": "^8.5",
+ "tailwindcss": "^3.4.17",
+ "typescript": "^5"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 00000000..1943bca2
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,3369 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@hookform/resolvers':
+ specifier: ^3.10.0
+ version: 3.10.0(react-hook-form@7.60.0(react@19.2.0))
+ '@radix-ui/react-accordion':
+ specifier: 1.2.2
+ version: 1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-alert-dialog':
+ specifier: 1.1.4
+ version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-aspect-ratio':
+ specifier: 1.1.1
+ version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-avatar':
+ specifier: 1.1.2
+ version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-checkbox':
+ specifier: 1.1.3
+ version: 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-collapsible':
+ specifier: 1.1.2
+ version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-context-menu':
+ specifier: 2.2.4
+ version: 2.2.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-dialog':
+ specifier: 1.1.4
+ version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-dropdown-menu':
+ specifier: 2.1.4
+ version: 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-hover-card':
+ specifier: 1.1.4
+ version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-label':
+ specifier: 2.1.1
+ version: 2.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-menubar':
+ specifier: 1.1.4
+ version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-navigation-menu':
+ specifier: 1.2.3
+ version: 1.2.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-popover':
+ specifier: 1.1.4
+ version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-progress':
+ specifier: 1.1.1
+ version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-radio-group':
+ specifier: 1.2.2
+ version: 1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-scroll-area':
+ specifier: 1.2.2
+ version: 1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-select':
+ specifier: 2.1.4
+ version: 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-separator':
+ specifier: 1.1.1
+ version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slider':
+ specifier: 1.2.2
+ version: 1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot':
+ specifier: 1.1.1
+ version: 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-switch':
+ specifier: 1.1.2
+ version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-tabs':
+ specifier: 1.1.2
+ version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toast':
+ specifier: 1.2.4
+ version: 1.2.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toggle':
+ specifier: 1.1.1
+ version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toggle-group':
+ specifier: 1.1.1
+ version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-tooltip':
+ specifier: 1.1.6
+ version: 1.1.6(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@vercel/analytics':
+ specifier: 1.3.1
+ version: 1.3.1(next@15.2.8(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)
+ autoprefixer:
+ specifier: ^10.4.20
+ version: 10.4.20(postcss@8.5.0)
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ cmdk:
+ specifier: 1.0.4
+ version: 1.0.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ date-fns:
+ specifier: 4.1.0
+ version: 4.1.0
+ embla-carousel-react:
+ specifier: 8.5.1
+ version: 8.5.1(react@19.2.0)
+ input-otp:
+ specifier: 1.4.1
+ version: 1.4.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ lucide-react:
+ specifier: ^0.454.0
+ version: 0.454.0(react@19.2.0)
+ next:
+ specifier: 15.2.8
+ version: 15.2.8(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ next-themes:
+ specifier: ^0.4.6
+ version: 0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react:
+ specifier: ^19
+ version: 19.2.0
+ react-day-picker:
+ specifier: 9.8.0
+ version: 9.8.0(react@19.2.0)
+ react-dom:
+ specifier: ^19
+ version: 19.2.0(react@19.2.0)
+ react-hook-form:
+ specifier: ^7.60.0
+ version: 7.60.0(react@19.2.0)
+ react-resizable-panels:
+ specifier: ^2.1.7
+ version: 2.1.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ recharts:
+ specifier: 2.15.4
+ version: 2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ sonner:
+ specifier: ^1.7.4
+ version: 1.7.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ tailwind-merge:
+ specifier: ^2.5.5
+ version: 2.5.5
+ tailwindcss-animate:
+ specifier: ^1.0.7
+ version: 1.0.7(tailwindcss@3.4.17)
+ vaul:
+ specifier: ^1.1.2
+ version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ zod:
+ specifier: 3.25.76
+ version: 3.25.76
+ devDependencies:
+ '@types/node':
+ specifier: ^22
+ version: 22.0.0
+ '@types/react':
+ specifier: ^19
+ version: 19.0.0
+ '@types/react-dom':
+ specifier: ^19
+ version: 19.0.0
+ postcss:
+ specifier: ^8.5
+ version: 8.5.0
+ tailwindcss:
+ specifier: ^3.4.17
+ version: 3.4.17
+ typescript:
+ specifier: ^5
+ version: 5.0.2
+
+packages:
+
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ '@babel/runtime@7.28.4':
+ resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@date-fns/tz@1.2.0':
+ resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==}
+
+ '@emnapi/runtime@1.7.1':
+ resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==}
+
+ '@floating-ui/core@1.7.3':
+ resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
+
+ '@floating-ui/dom@1.7.4':
+ resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==}
+
+ '@floating-ui/react-dom@2.1.6':
+ resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@floating-ui/utils@0.2.10':
+ resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
+
+ '@hookform/resolvers@3.10.0':
+ resolution: {integrity: sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==}
+ peerDependencies:
+ react-hook-form: ^7.0.0
+
+ '@img/sharp-darwin-arm64@0.33.5':
+ resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.33.5':
+ resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-arm64@1.0.4':
+ resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.0.4':
+ resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.0.4':
+ resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-arm@1.0.5':
+ resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-s390x@1.0.4':
+ resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-x64@1.0.4':
+ resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
+ resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.0.4':
+ resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linux-arm64@0.33.5':
+ resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linux-arm@0.33.5':
+ resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-linux-s390x@0.33.5':
+ resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-linux-x64@0.33.5':
+ resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-arm64@0.33.5':
+ resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-x64@0.33.5':
+ resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-wasm32@0.33.5':
+ resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-ia32@0.33.5':
+ resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.33.5':
+ resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@next/env@15.2.8':
+ resolution: {integrity: sha512-TaEsAki14R7BlgywA05t2PFYfwZiNlGUHyIQHVyloXX3y+Dm0HUITe5YwTkjtuOQuDhuuLotNEad4VtnmE11Uw==}
+
+ '@next/swc-darwin-arm64@15.2.5':
+ resolution: {integrity: sha512-4OimvVlFTbgzPdA0kh8A1ih6FN9pQkL4nPXGqemEYgk+e7eQhsst/p35siNNqA49eQA6bvKZ1ASsDtu9gtXuog==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@15.2.5':
+ resolution: {integrity: sha512-ohzRaE9YbGt1ctE0um+UGYIDkkOxHV44kEcHzLqQigoRLaiMtZzGrA11AJh2Lu0lv51XeiY1ZkUvkThjkVNBMA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@15.2.5':
+ resolution: {integrity: sha512-FMSdxSUt5bVXqqOoZCc/Seg4LQep9w/fXTazr/EkpXW2Eu4IFI9FD7zBDlID8TJIybmvKk7mhd9s+2XWxz4flA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-arm64-musl@15.2.5':
+ resolution: {integrity: sha512-4ZNKmuEiW5hRKkGp2HWwZ+JrvK4DQLgf8YDaqtZyn7NYdl0cHfatvlnLFSWUayx9yFAUagIgRGRk8pFxS8Qniw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-x64-gnu@15.2.5':
+ resolution: {integrity: sha512-bE6lHQ9GXIf3gCDE53u2pTl99RPZW5V1GLHSRMJ5l/oB/MT+cohu9uwnCK7QUph2xIOu2a6+27kL0REa/kqwZw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-linux-x64-musl@15.2.5':
+ resolution: {integrity: sha512-y7EeQuSkQbTAkCEQnJXm1asRUuGSWAchGJ3c+Qtxh8LVjXleZast8Mn/rL7tZOm7o35QeIpIcid6ufG7EVTTcA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-win32-arm64-msvc@15.2.5':
+ resolution: {integrity: sha512-gQMz0yA8/dskZM2Xyiq2FRShxSrsJNha40Ob/M2n2+JGRrZ0JwTVjLdvtN6vCxuq4ByhOd4a9qEf60hApNR2gQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@15.2.5':
+ resolution: {integrity: sha512-tBDNVUcI7U03+3oMvJ11zrtVin5p0NctiuKmTGyaTIEAVj9Q77xukLXGXRnWxKRIIdFG4OTA2rUVGZDYOwgmAA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@radix-ui/number@1.1.0':
+ resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
+
+ '@radix-ui/primitive@1.1.1':
+ resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==}
+
+ '@radix-ui/react-accordion@1.2.2':
+ resolution: {integrity: sha512-b1oh54x4DMCdGsB4/7ahiSrViXxaBwRPotiZNnYXjLha9vfuURSAZErki6qjDoSIV0eXx5v57XnTGVtGwnfp2g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-alert-dialog@1.1.4':
+ resolution: {integrity: sha512-A6Kh23qZDLy3PSU4bh2UJZznOrUdHImIXqF8YtUa6CN73f8EOO9XlXSCd9IHyPvIquTaa/kwaSWzZTtUvgXVGw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-arrow@1.1.1':
+ resolution: {integrity: sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-aspect-ratio@1.1.1':
+ resolution: {integrity: sha512-kNU4FIpcFMBLkOUcgeIteH06/8JLBcYY6Le1iKenDGCYNYFX3TQqCZjzkOsz37h7r94/99GTb7YhEr98ZBJibw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-avatar@1.1.2':
+ resolution: {integrity: sha512-GaC7bXQZ5VgZvVvsJ5mu/AEbjYLnhhkoidOboC50Z6FFlLA03wG2ianUoH+zgDQ31/9gCF59bE4+2bBgTyMiig==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-checkbox@1.1.3':
+ resolution: {integrity: sha512-HD7/ocp8f1B3e6OHygH0n7ZKjONkhciy1Nh0yuBgObqThc3oyx+vuMfFHKAknXRHHWVE9XvXStxJFyjUmB8PIw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-collapsible@1.1.2':
+ resolution: {integrity: sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-collection@1.1.1':
+ resolution: {integrity: sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-compose-refs@1.1.1':
+ resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-compose-refs@1.1.2':
+ resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-context-menu@2.2.4':
+ resolution: {integrity: sha512-ap4wdGwK52rJxGkwukU1NrnEodsUFQIooANKu+ey7d6raQ2biTcEf8za1zr0mgFHieevRTB2nK4dJeN8pTAZGQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-context@1.1.1':
+ resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-dialog@1.1.4':
+ resolution: {integrity: sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-direction@1.1.0':
+ resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-dismissable-layer@1.1.3':
+ resolution: {integrity: sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-dropdown-menu@2.1.4':
+ resolution: {integrity: sha512-iXU1Ab5ecM+yEepGAWK8ZhMyKX4ubFdCNtol4sT9D0OVErG9PNElfx3TQhjw7n7BC5nFVz68/5//clWy+8TXzA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-focus-guards@1.1.1':
+ resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-focus-scope@1.1.1':
+ resolution: {integrity: sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-hover-card@1.1.4':
+ resolution: {integrity: sha512-QSUUnRA3PQ2UhvoCv3eYvMnCAgGQW+sTu86QPuNb+ZMi+ZENd6UWpiXbcWDQ4AEaKF9KKpCHBeaJz9Rw6lRlaQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-id@1.1.0':
+ resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-id@1.1.1':
+ resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-label@2.1.1':
+ resolution: {integrity: sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-menu@2.1.4':
+ resolution: {integrity: sha512-BnOgVoL6YYdHAG6DtXONaR29Eq4nvbi8rutrV/xlr3RQCMMb3yqP85Qiw/3NReozrSW+4dfLkK+rc1hb4wPU/A==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-menubar@1.1.4':
+ resolution: {integrity: sha512-+KMpi7VAZuB46+1LD7a30zb5IxyzLgC8m8j42gk3N4TUCcViNQdX8FhoH1HDvYiA8quuqcek4R4bYpPn/SY1GA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-navigation-menu@1.2.3':
+ resolution: {integrity: sha512-IQWAsQ7dsLIYDrn0WqPU+cdM7MONTv9nqrLVYoie3BPiabSfUVDe6Fr+oEt0Cofsr9ONDcDe9xhmJbL1Uq1yKg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-popover@1.1.4':
+ resolution: {integrity: sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-popper@1.2.1':
+ resolution: {integrity: sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-portal@1.1.3':
+ resolution: {integrity: sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-presence@1.1.2':
+ resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-primitive@2.0.1':
+ resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-primitive@2.1.4':
+ resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-progress@1.1.1':
+ resolution: {integrity: sha512-6diOawA84f/eMxFHcWut0aE1C2kyE9dOyCTQOMRR2C/qPiXz/X0SaiA/RLbapQaXUCmy0/hLMf9meSccD1N0pA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-radio-group@1.2.2':
+ resolution: {integrity: sha512-E0MLLGfOP0l8P/NxgVzfXJ8w3Ch8cdO6UDzJfDChu4EJDy+/WdO5LqpdY8PYnCErkmZH3gZhDL1K7kQ41fAHuQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-roving-focus@1.1.1':
+ resolution: {integrity: sha512-QE1RoxPGJ/Nm8Qmk0PxP8ojmoaS67i0s7hVssS7KuI2FQoc/uzVlZsqKfQvxPE6D8hICCPHJ4D88zNhT3OOmkw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-scroll-area@1.2.2':
+ resolution: {integrity: sha512-EFI1N/S3YxZEW/lJ/H1jY3njlvTd8tBmgKEn4GHi51+aMm94i6NmAJstsm5cu3yJwYqYc93gpCPm21FeAbFk6g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-select@2.1.4':
+ resolution: {integrity: sha512-pOkb2u8KgO47j/h7AylCj7dJsm69BXcjkrvTqMptFqsE2i0p8lHkfgneXKjAgPzBMivnoMyt8o4KiV4wYzDdyQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-separator@1.1.1':
+ resolution: {integrity: sha512-RRiNRSrD8iUiXriq/Y5n4/3iE8HzqgLHsusUSg5jVpU2+3tqcUFPJXHDymwEypunc2sWxDUS3UC+rkZRlHedsw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slider@1.2.2':
+ resolution: {integrity: sha512-sNlU06ii1/ZcbHf8I9En54ZPW0Vil/yPVg4vQMcFNjrIx51jsHbFl1HYHQvCIWJSr1q0ZmA+iIs/ZTv8h7HHSA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slot@1.1.1':
+ resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-slot@1.2.4':
+ resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-switch@1.1.2':
+ resolution: {integrity: sha512-zGukiWHjEdBCRyXvKR6iXAQG6qXm2esuAD6kDOi9Cn+1X6ev3ASo4+CsYaD6Fov9r/AQFekqnD/7+V0Cs6/98g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-tabs@1.1.2':
+ resolution: {integrity: sha512-9u/tQJMcC2aGq7KXpGivMm1mgq7oRJKXphDwdypPd/j21j/2znamPU8WkXgnhUaTrSFNIt8XhOyCAupg8/GbwQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toast@1.2.4':
+ resolution: {integrity: sha512-Sch9idFJHJTMH9YNpxxESqABcAFweJG4tKv+0zo0m5XBvUSL8FM5xKcJLFLXononpePs8IclyX1KieL5SDUNgA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toggle-group@1.1.1':
+ resolution: {integrity: sha512-OgDLZEA30Ylyz8YSXvnGqIHtERqnUt1KUYTKdw/y8u7Ci6zGiJfXc02jahmcSNK3YcErqioj/9flWC9S1ihfwg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-toggle@1.1.1':
+ resolution: {integrity: sha512-i77tcgObYr743IonC1hrsnnPmszDRn8p+EGUsUt+5a/JFn28fxaM88Py6V2mc8J5kELMWishI0rLnuGLFD/nnQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-tooltip@1.1.6':
+ resolution: {integrity: sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-use-callback-ref@1.1.0':
+ resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-controllable-state@1.1.0':
+ resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-escape-keydown@1.1.0':
+ resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-layout-effect@1.1.0':
+ resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-layout-effect@1.1.1':
+ resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-previous@1.1.0':
+ resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-rect@1.1.0':
+ resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-size@1.1.0':
+ resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-visually-hidden@1.1.1':
+ resolution: {integrity: sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/rect@1.1.0':
+ resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
+
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
+ '@swc/helpers@0.5.15':
+ resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+
+ '@types/d3-array@3.2.2':
+ resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
+
+ '@types/d3-color@3.1.3':
+ resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+ '@types/d3-ease@3.0.2':
+ resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+ '@types/d3-interpolate@3.0.4':
+ resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+ '@types/d3-path@3.1.1':
+ resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+ '@types/d3-scale@4.0.9':
+ resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
+ '@types/d3-shape@3.1.7':
+ resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==}
+
+ '@types/d3-time@3.0.4':
+ resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+ '@types/d3-timer@3.0.2':
+ resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
+ '@types/node@22.0.0':
+ resolution: {integrity: sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==}
+
+ '@types/react-dom@19.0.0':
+ resolution: {integrity: sha512-1KfiQKsH1o00p9m5ag12axHQSb3FOU9H20UTrujVSkNhuCrRHiQWFqgEnTNK5ZNfnzZv8UWrnXVqCmCF9fgY3w==}
+
+ '@types/react@19.0.0':
+ resolution: {integrity: sha512-MY3oPudxvMYyesqs/kW1Bh8y9VqSmf+tzqw3ae8a9DZW68pUe3zAdHeI1jc6iAysuRdACnVknHP8AhwD4/dxtg==}
+
+ '@vercel/analytics@1.3.1':
+ resolution: {integrity: sha512-xhSlYgAuJ6Q4WQGkzYTLmXwhYl39sWjoMA3nHxfkvG+WdBT25c563a7QhwwKivEOZtPJXifYHR1m2ihoisbWyA==}
+ peerDependencies:
+ next: '>= 13'
+ react: ^18 || ^19
+ peerDependenciesMeta:
+ next:
+ optional: true
+ react:
+ optional: true
+
+ any-promise@1.3.0:
+ resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ arg@5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+ aria-hidden@1.2.6:
+ resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
+ engines: {node: '>=10'}
+
+ autoprefixer@10.4.20:
+ resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
+ engines: {node: ^10 || ^12 || >=14}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.1.0
+
+ baseline-browser-mapping@2.9.11:
+ resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==}
+ hasBin: true
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.28.1:
+ resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ busboy@1.6.0:
+ resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
+ engines: {node: '>=10.16.0'}
+
+ camelcase-css@2.0.1:
+ resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+ engines: {node: '>= 6'}
+
+ caniuse-lite@1.0.30001761:
+ resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ class-variance-authority@0.7.1:
+ resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
+
+ client-only@0.0.1:
+ resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
+ cmdk@1.0.4:
+ resolution: {integrity: sha512-AnsjfHyHpQ/EFeAnG216WY7A5LiYCoZzCSygiLvfXC3H3LFGCprErteUcszaVluGOhuOTbJS3jWHrSDYPBBygg==}
+ peerDependencies:
+ react: ^18 || ^19 || ^19.0.0-rc
+ react-dom: ^18 || ^19 || ^19.0.0-rc
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
+ color@4.2.3:
+ resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
+ engines: {node: '>=12.5.0'}
+
+ commander@4.1.1:
+ resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+ engines: {node: '>= 6'}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-format@3.1.0:
+ resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+
+ d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+
+ d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+
+ d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+
+ d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ engines: {node: '>=12'}
+
+ d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+
+ date-fns-jalali@4.1.0-0:
+ resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==}
+
+ date-fns@4.1.0:
+ resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
+
+ decimal.js-light@2.5.1:
+ resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ detect-node-es@1.1.0:
+ resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
+
+ didyoumean@1.2.2:
+ resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+
+ dlv@1.1.3:
+ resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+
+ dom-helpers@5.2.1:
+ resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
+
+ electron-to-chromium@1.5.267:
+ resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==}
+
+ embla-carousel-react@8.5.1:
+ resolution: {integrity: sha512-z9Y0K84BJvhChXgqn2CFYbfEi6AwEr+FFVVKm/MqbTQ2zIzO1VQri6w67LcfpVF0AjbhwVMywDZqY4alYkjW5w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
+ embla-carousel-reactive-utils@8.5.1:
+ resolution: {integrity: sha512-n7VSoGIiiDIc4MfXF3ZRTO59KDp820QDuyBDGlt5/65+lumPHxX2JLz0EZ23hZ4eg4vZGUXwMkYv02fw2JVo/A==}
+ peerDependencies:
+ embla-carousel: 8.5.1
+
+ embla-carousel@8.5.1:
+ resolution: {integrity: sha512-JUb5+FOHobSiWQ2EJNaueCNT/cQU9L6XWBbWmorWPQT9bkbk+fhsuLr8wWrzXKagO3oWszBO7MSx+GfaRk4E6A==}
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ eventemitter3@4.0.7:
+ resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+
+ fast-equals@5.4.0:
+ resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==}
+ engines: {node: '>=6.0.0'}
+
+ fast-glob@3.3.3:
+ resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+ engines: {node: '>=8.6.0'}
+
+ fastq@1.20.1:
+ resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ fraction.js@4.3.7:
+ resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ get-nonce@1.0.1:
+ resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+ engines: {node: '>=6'}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ input-otp@1.4.1:
+ resolution: {integrity: sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+
+ internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+
+ is-arrayish@0.3.4:
+ resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-core-module@2.16.1:
+ resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+ engines: {node: '>= 0.4'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ jiti@1.21.7:
+ resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ lilconfig@3.1.3:
+ resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
+ engines: {node: '>=14'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+
+ lucide-react@0.454.0:
+ resolution: {integrity: sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mz@2.7.0:
+ resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ next-themes@0.4.6:
+ resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
+ peerDependencies:
+ react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
+
+ next@15.2.8:
+ resolution: {integrity: sha512-pe2trLKZTdaCuvNER0S9Wp+SP2APf7SfFmyUP9/w1SFA2UqmW0u+IsxCKkiky3n6um7mryaQIlgiDnKrf1ZwIw==}
+ engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.41.2
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@playwright/test':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
+
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ normalize-range@0.1.2:
+ resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+ engines: {node: '>=0.10.0'}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-hash@3.0.0:
+ resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+ engines: {node: '>= 6'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ pify@2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+
+ pirates@4.0.7:
+ resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+ engines: {node: '>= 6'}
+
+ postcss-import@15.1.0:
+ resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+
+ postcss-js@4.1.0:
+ resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==}
+ engines: {node: ^12 || ^14 || >= 16}
+ peerDependencies:
+ postcss: ^8.4.21
+
+ postcss-load-config@4.0.2:
+ resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
+ engines: {node: '>= 14'}
+ peerDependencies:
+ postcss: '>=8.0.9'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+ ts-node:
+ optional: true
+
+ postcss-nested@6.2.0:
+ resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.2.14
+
+ postcss-selector-parser@6.1.2:
+ resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+ engines: {node: '>=4'}
+
+ postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ postcss@8.5.0:
+ resolution: {integrity: sha512-27VKOqrYfPncKA2NrFOVhP5MGAfHKLYn/Q0mz9cNQyRAKYi3VNHwYU2qKKqPCqgBmeeJ0uAFB56NumXZ5ZReXg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prop-types@15.8.1:
+ resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ react-day-picker@9.8.0:
+ resolution: {integrity: sha512-E0yhhg7R+pdgbl/2toTb0xBhsEAtmAx1l7qjIWYfcxOy8w4rTSVfbtBoSzVVhPwKP/5E9iL38LivzoE3AQDhCQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: '>=16.8.0'
+
+ react-dom@19.2.0:
+ resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==}
+ peerDependencies:
+ react: ^19.2.0
+
+ react-hook-form@7.60.0:
+ resolution: {integrity: sha512-SBrYOvMbDB7cV8ZfNpaiLcgjH/a1c7aK0lK+aNigpf4xWLO8q+o4tcvVurv3c4EOyzn/3dCsYt4GKD42VvJ/+A==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18 || ^19
+
+ react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+ react-is@18.3.1:
+ resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+
+ react-remove-scroll-bar@2.3.8:
+ resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-remove-scroll@2.7.2:
+ resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-resizable-panels@2.1.7:
+ resolution: {integrity: sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==}
+ peerDependencies:
+ react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
+ react-smooth@4.0.4:
+ resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ react-style-singleton@2.2.3:
+ resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ react-transition-group@4.4.5:
+ resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
+ peerDependencies:
+ react: '>=16.6.0'
+ react-dom: '>=16.6.0'
+
+ react@19.2.0:
+ resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==}
+ engines: {node: '>=0.10.0'}
+
+ read-cache@1.0.0:
+ resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ recharts-scale@0.4.5:
+ resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==}
+
+ recharts@2.15.4:
+ resolution: {integrity: sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ resolve@1.22.11:
+ resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ semver@7.7.3:
+ resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ server-only@0.0.1:
+ resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
+
+ sharp@0.33.5:
+ resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
+ simple-swizzle@0.2.4:
+ resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
+
+ sonner@1.7.4:
+ resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==}
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ streamsearch@1.1.0:
+ resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
+ engines: {node: '>=10.0.0'}
+
+ styled-jsx@5.1.6:
+ resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ babel-plugin-macros:
+ optional: true
+
+ sucrase@3.35.1:
+ resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ tailwind-merge@2.5.5:
+ resolution: {integrity: sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==}
+
+ tailwindcss-animate@1.0.7:
+ resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders'
+
+ tailwindcss@3.4.17:
+ resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ thenify-all@1.6.0:
+ resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+ engines: {node: '>=0.8'}
+
+ thenify@3.3.1:
+ resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+ tiny-invariant@1.3.3:
+ resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ ts-interface-checker@0.1.13:
+ resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ typescript@5.0.2:
+ resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==}
+ engines: {node: '>=12.20'}
+ hasBin: true
+
+ undici-types@6.11.1:
+ resolution: {integrity: sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==}
+
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ use-callback-ref@1.3.3:
+ resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ use-sidecar@1.1.3:
+ resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ use-sync-external-store@1.6.0:
+ resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ vaul@1.1.2:
+ resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
+
+ victory-vendor@36.9.2:
+ resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
+
+ yaml@2.8.2:
+ resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ zod@3.25.76:
+ resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
+
+snapshots:
+
+ '@alloc/quick-lru@5.2.0': {}
+
+ '@babel/runtime@7.28.4': {}
+
+ '@date-fns/tz@1.2.0': {}
+
+ '@emnapi/runtime@1.7.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@floating-ui/core@1.7.3':
+ dependencies:
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/dom@1.7.4':
+ dependencies:
+ '@floating-ui/core': 1.7.3
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@floating-ui/dom': 1.7.4
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ '@floating-ui/utils@0.2.10': {}
+
+ '@hookform/resolvers@3.10.0(react-hook-form@7.60.0(react@19.2.0))':
+ dependencies:
+ react-hook-form: 7.60.0(react@19.2.0)
+
+ '@img/sharp-darwin-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-darwin-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.0.5':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-linux-arm@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.0.5
+ optional: true
+
+ '@img/sharp-linux-s390x@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.0.4
+ optional: true
+
+ '@img/sharp-linux-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-wasm32@0.33.5':
+ dependencies:
+ '@emnapi/runtime': 1.7.1
+ optional: true
+
+ '@img/sharp-win32-ia32@0.33.5':
+ optional: true
+
+ '@img/sharp-win32-x64@0.33.5':
+ optional: true
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@next/env@15.2.8': {}
+
+ '@next/swc-darwin-arm64@15.2.5':
+ optional: true
+
+ '@next/swc-darwin-x64@15.2.5':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@15.2.5':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@15.2.5':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@15.2.5':
+ optional: true
+
+ '@next/swc-linux-x64-musl@15.2.5':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@15.2.5':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@15.2.5':
+ optional: true
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.20.1
+
+ '@radix-ui/number@1.1.0': {}
+
+ '@radix-ui/primitive@1.1.1': {}
+
+ '@radix-ui/react-accordion@1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-collapsible': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-alert-dialog@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-arrow@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-aspect-ratio@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-avatar@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-checkbox@1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-collapsible@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-collection@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-compose-refs@1.1.2(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-context-menu@2.2.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-context@1.1.1(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-dialog@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ aria-hidden: 1.2.6
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-remove-scroll: 2.7.2(@types/react@19.0.0)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-direction@1.1.0(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-dropdown-menu@2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-hover-card@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-id@1.1.0(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-id@1.1.1(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-label@2.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-menu@2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ aria-hidden: 1.2.6
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-remove-scroll: 2.7.2(@types/react@19.0.0)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-menubar@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-navigation-menu@1.2.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-popover@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ aria-hidden: 1.2.6
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-remove-scroll: 2.7.2(@types/react@19.0.0)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-popper@1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-arrow': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/rect': 1.1.0
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-portal@1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-slot': 1.2.4(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-progress@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-radio-group@1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-roving-focus@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-scroll-area@1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/number': 1.1.0
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-select@2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/number': 1.1.0
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ aria-hidden: 1.2.6
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-remove-scroll: 2.7.2(@types/react@19.0.0)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-separator@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-slider@1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/number': 1.1.0
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-slot@1.1.1(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-slot@1.2.4(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-switch@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-tabs@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-toast@1.2.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-toggle-group@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-toggle': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-toggle@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-tooltip@1.1.6(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.1
+ '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-use-previous@1.1.0(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/rect': 1.1.0
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-use-size@1.1.0(@types/react@19.0.0)(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.2.0)
+ react: 19.2.0
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+ '@types/react-dom': 19.0.0
+
+ '@radix-ui/rect@1.1.0': {}
+
+ '@swc/counter@0.1.3': {}
+
+ '@swc/helpers@0.5.15':
+ dependencies:
+ tslib: 2.8.1
+
+ '@types/d3-array@3.2.2': {}
+
+ '@types/d3-color@3.1.3': {}
+
+ '@types/d3-ease@3.0.2': {}
+
+ '@types/d3-interpolate@3.0.4':
+ dependencies:
+ '@types/d3-color': 3.1.3
+
+ '@types/d3-path@3.1.1': {}
+
+ '@types/d3-scale@4.0.9':
+ dependencies:
+ '@types/d3-time': 3.0.4
+
+ '@types/d3-shape@3.1.7':
+ dependencies:
+ '@types/d3-path': 3.1.1
+
+ '@types/d3-time@3.0.4': {}
+
+ '@types/d3-timer@3.0.2': {}
+
+ '@types/node@22.0.0':
+ dependencies:
+ undici-types: 6.11.1
+
+ '@types/react-dom@19.0.0':
+ dependencies:
+ '@types/react': 19.0.0
+
+ '@types/react@19.0.0':
+ dependencies:
+ csstype: 3.2.3
+
+ '@vercel/analytics@1.3.1(next@15.2.8(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ server-only: 0.0.1
+ optionalDependencies:
+ next: 15.2.8(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+
+ any-promise@1.3.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ arg@5.0.2: {}
+
+ aria-hidden@1.2.6:
+ dependencies:
+ tslib: 2.8.1
+
+ autoprefixer@10.4.20(postcss@8.5.0):
+ dependencies:
+ browserslist: 4.28.1
+ caniuse-lite: 1.0.30001761
+ fraction.js: 4.3.7
+ normalize-range: 0.1.2
+ picocolors: 1.1.1
+ postcss: 8.5.0
+ postcss-value-parser: 4.2.0
+
+ baseline-browser-mapping@2.9.11: {}
+
+ binary-extensions@2.3.0: {}
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.28.1:
+ dependencies:
+ baseline-browser-mapping: 2.9.11
+ caniuse-lite: 1.0.30001761
+ electron-to-chromium: 1.5.267
+ node-releases: 2.0.27
+ update-browserslist-db: 1.2.3(browserslist@4.28.1)
+
+ busboy@1.6.0:
+ dependencies:
+ streamsearch: 1.1.0
+
+ camelcase-css@2.0.1: {}
+
+ caniuse-lite@1.0.30001761: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ class-variance-authority@0.7.1:
+ dependencies:
+ clsx: 2.1.1
+
+ client-only@0.0.1: {}
+
+ clsx@2.1.1: {}
+
+ cmdk@1.0.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.0.0)(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ use-sync-external-store: 1.6.0(react@19.2.0)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+ optional: true
+
+ color-name@1.1.4:
+ optional: true
+
+ color-string@1.9.1:
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.4
+ optional: true
+
+ color@4.2.3:
+ dependencies:
+ color-convert: 2.0.1
+ color-string: 1.9.1
+ optional: true
+
+ commander@4.1.1: {}
+
+ cssesc@3.0.0: {}
+
+ csstype@3.2.3: {}
+
+ d3-array@3.2.4:
+ dependencies:
+ internmap: 2.0.3
+
+ d3-color@3.1.0: {}
+
+ d3-ease@3.0.1: {}
+
+ d3-format@3.1.0: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-path@3.1.0: {}
+
+ d3-scale@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.0
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+
+ d3-shape@3.2.0:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-time-format@4.1.0:
+ dependencies:
+ d3-time: 3.1.0
+
+ d3-time@3.1.0:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-timer@3.0.1: {}
+
+ date-fns-jalali@4.1.0-0: {}
+
+ date-fns@4.1.0: {}
+
+ decimal.js-light@2.5.1: {}
+
+ detect-libc@2.1.2:
+ optional: true
+
+ detect-node-es@1.1.0: {}
+
+ didyoumean@1.2.2: {}
+
+ dlv@1.1.3: {}
+
+ dom-helpers@5.2.1:
+ dependencies:
+ '@babel/runtime': 7.28.4
+ csstype: 3.2.3
+
+ electron-to-chromium@1.5.267: {}
+
+ embla-carousel-react@8.5.1(react@19.2.0):
+ dependencies:
+ embla-carousel: 8.5.1
+ embla-carousel-reactive-utils: 8.5.1(embla-carousel@8.5.1)
+ react: 19.2.0
+
+ embla-carousel-reactive-utils@8.5.1(embla-carousel@8.5.1):
+ dependencies:
+ embla-carousel: 8.5.1
+
+ embla-carousel@8.5.1: {}
+
+ escalade@3.2.0: {}
+
+ eventemitter3@4.0.7: {}
+
+ fast-equals@5.4.0: {}
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fastq@1.20.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ fraction.js@4.3.7: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ get-nonce@1.0.1: {}
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ input-otp@1.4.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ internmap@2.0.3: {}
+
+ is-arrayish@0.3.4:
+ optional: true
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-core-module@2.16.1:
+ dependencies:
+ hasown: 2.0.2
+
+ is-extglob@2.1.1: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-number@7.0.0: {}
+
+ jiti@1.21.7: {}
+
+ js-tokens@4.0.0: {}
+
+ lilconfig@3.1.3: {}
+
+ lines-and-columns@1.2.4: {}
+
+ lodash@4.17.21: {}
+
+ loose-envify@1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+
+ lucide-react@0.454.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mz@2.7.0:
+ dependencies:
+ any-promise: 1.3.0
+ object-assign: 4.1.1
+ thenify-all: 1.6.0
+
+ nanoid@3.3.11: {}
+
+ next-themes@0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ next@15.2.8(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ '@next/env': 15.2.8
+ '@swc/counter': 0.1.3
+ '@swc/helpers': 0.5.15
+ busboy: 1.6.0
+ caniuse-lite: 1.0.30001761
+ postcss: 8.4.31
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ styled-jsx: 5.1.6(react@19.2.0)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 15.2.5
+ '@next/swc-darwin-x64': 15.2.5
+ '@next/swc-linux-arm64-gnu': 15.2.5
+ '@next/swc-linux-arm64-musl': 15.2.5
+ '@next/swc-linux-x64-gnu': 15.2.5
+ '@next/swc-linux-x64-musl': 15.2.5
+ '@next/swc-win32-arm64-msvc': 15.2.5
+ '@next/swc-win32-x64-msvc': 15.2.5
+ sharp: 0.33.5
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
+ node-releases@2.0.27: {}
+
+ normalize-path@3.0.0: {}
+
+ normalize-range@0.1.2: {}
+
+ object-assign@4.1.1: {}
+
+ object-hash@3.0.0: {}
+
+ path-parse@1.0.7: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.3: {}
+
+ pify@2.3.0: {}
+
+ pirates@4.0.7: {}
+
+ postcss-import@15.1.0(postcss@8.5.0):
+ dependencies:
+ postcss: 8.5.0
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.11
+
+ postcss-js@4.1.0(postcss@8.5.0):
+ dependencies:
+ camelcase-css: 2.0.1
+ postcss: 8.5.0
+
+ postcss-load-config@4.0.2(postcss@8.5.0):
+ dependencies:
+ lilconfig: 3.1.3
+ yaml: 2.8.2
+ optionalDependencies:
+ postcss: 8.5.0
+
+ postcss-nested@6.2.0(postcss@8.5.0):
+ dependencies:
+ postcss: 8.5.0
+ postcss-selector-parser: 6.1.2
+
+ postcss-selector-parser@6.1.2:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.4.31:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ postcss@8.5.0:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prop-types@15.8.1:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react-is: 16.13.1
+
+ queue-microtask@1.2.3: {}
+
+ react-day-picker@9.8.0(react@19.2.0):
+ dependencies:
+ '@date-fns/tz': 1.2.0
+ date-fns: 4.1.0
+ date-fns-jalali: 4.1.0-0
+ react: 19.2.0
+
+ react-dom@19.2.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ scheduler: 0.27.0
+
+ react-hook-form@7.60.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
+ react-is@16.13.1: {}
+
+ react-is@18.3.1: {}
+
+ react-remove-scroll-bar@2.3.8(@types/react@19.0.0)(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-style-singleton: 2.2.3(@types/react@19.0.0)(react@19.2.0)
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ react-remove-scroll@2.7.2(@types/react@19.0.0)(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-remove-scroll-bar: 2.3.8(@types/react@19.0.0)(react@19.2.0)
+ react-style-singleton: 2.2.3(@types/react@19.0.0)(react@19.2.0)
+ tslib: 2.8.1
+ use-callback-ref: 1.3.3(@types/react@19.0.0)(react@19.2.0)
+ use-sidecar: 1.1.3(@types/react@19.0.0)(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ react-resizable-panels@2.1.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ react-smooth@4.0.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ fast-equals: 5.4.0
+ prop-types: 15.8.1
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+
+ react-style-singleton@2.2.3(@types/react@19.0.0)(react@19.2.0):
+ dependencies:
+ get-nonce: 1.0.1
+ react: 19.2.0
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ '@babel/runtime': 7.28.4
+ dom-helpers: 5.2.1
+ loose-envify: 1.4.0
+ prop-types: 15.8.1
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ react@19.2.0: {}
+
+ read-cache@1.0.0:
+ dependencies:
+ pify: 2.3.0
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ recharts-scale@0.4.5:
+ dependencies:
+ decimal.js-light: 2.5.1
+
+ recharts@2.15.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ clsx: 2.1.1
+ eventemitter3: 4.0.7
+ lodash: 4.17.21
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ react-is: 18.3.1
+ react-smooth: 4.0.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ recharts-scale: 0.4.5
+ tiny-invariant: 1.3.3
+ victory-vendor: 36.9.2
+
+ resolve@1.22.11:
+ dependencies:
+ is-core-module: 2.16.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ reusify@1.1.0: {}
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ scheduler@0.27.0: {}
+
+ semver@7.7.3:
+ optional: true
+
+ server-only@0.0.1: {}
+
+ sharp@0.33.5:
+ dependencies:
+ color: 4.2.3
+ detect-libc: 2.1.2
+ semver: 7.7.3
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.33.5
+ '@img/sharp-darwin-x64': 0.33.5
+ '@img/sharp-libvips-darwin-arm64': 1.0.4
+ '@img/sharp-libvips-darwin-x64': 1.0.4
+ '@img/sharp-libvips-linux-arm': 1.0.5
+ '@img/sharp-libvips-linux-arm64': 1.0.4
+ '@img/sharp-libvips-linux-s390x': 1.0.4
+ '@img/sharp-libvips-linux-x64': 1.0.4
+ '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
+ '@img/sharp-libvips-linuxmusl-x64': 1.0.4
+ '@img/sharp-linux-arm': 0.33.5
+ '@img/sharp-linux-arm64': 0.33.5
+ '@img/sharp-linux-s390x': 0.33.5
+ '@img/sharp-linux-x64': 0.33.5
+ '@img/sharp-linuxmusl-arm64': 0.33.5
+ '@img/sharp-linuxmusl-x64': 0.33.5
+ '@img/sharp-wasm32': 0.33.5
+ '@img/sharp-win32-ia32': 0.33.5
+ '@img/sharp-win32-x64': 0.33.5
+ optional: true
+
+ simple-swizzle@0.2.4:
+ dependencies:
+ is-arrayish: 0.3.4
+ optional: true
+
+ sonner@1.7.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+
+ source-map-js@1.2.1: {}
+
+ streamsearch@1.1.0: {}
+
+ styled-jsx@5.1.6(react@19.2.0):
+ dependencies:
+ client-only: 0.0.1
+ react: 19.2.0
+
+ sucrase@3.35.1:
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ commander: 4.1.1
+ lines-and-columns: 1.2.4
+ mz: 2.7.0
+ pirates: 4.0.7
+ tinyglobby: 0.2.15
+ ts-interface-checker: 0.1.13
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ tailwind-merge@2.5.5: {}
+
+ tailwindcss-animate@1.0.7(tailwindcss@3.4.17):
+ dependencies:
+ tailwindcss: 3.4.17
+
+ tailwindcss@3.4.17:
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ arg: 5.0.2
+ chokidar: 3.6.0
+ didyoumean: 1.2.2
+ dlv: 1.1.3
+ fast-glob: 3.3.3
+ glob-parent: 6.0.2
+ is-glob: 4.0.3
+ jiti: 1.21.7
+ lilconfig: 3.1.3
+ micromatch: 4.0.8
+ normalize-path: 3.0.0
+ object-hash: 3.0.0
+ picocolors: 1.1.1
+ postcss: 8.5.0
+ postcss-import: 15.1.0(postcss@8.5.0)
+ postcss-js: 4.1.0(postcss@8.5.0)
+ postcss-load-config: 4.0.2(postcss@8.5.0)
+ postcss-nested: 6.2.0(postcss@8.5.0)
+ postcss-selector-parser: 6.1.2
+ resolve: 1.22.11
+ sucrase: 3.35.1
+ transitivePeerDependencies:
+ - ts-node
+
+ thenify-all@1.6.0:
+ dependencies:
+ thenify: 3.3.1
+
+ thenify@3.3.1:
+ dependencies:
+ any-promise: 1.3.0
+
+ tiny-invariant@1.3.3: {}
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ ts-interface-checker@0.1.13: {}
+
+ tslib@2.8.1: {}
+
+ typescript@5.0.2: {}
+
+ undici-types@6.11.1: {}
+
+ update-browserslist-db@1.2.3(browserslist@4.28.1):
+ dependencies:
+ browserslist: 4.28.1
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ use-callback-ref@1.3.3(@types/react@19.0.0)(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ use-sidecar@1.1.3(@types/react@19.0.0)(react@19.2.0):
+ dependencies:
+ detect-node-es: 1.1.0
+ react: 19.2.0
+ tslib: 2.8.1
+ optionalDependencies:
+ '@types/react': 19.0.0
+
+ use-sync-external-store@1.6.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
+ util-deprecate@1.0.2: {}
+
+ vaul@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
+ dependencies:
+ '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+
+ victory-vendor@36.9.2:
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/d3-ease': 3.0.2
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-scale': 4.0.9
+ '@types/d3-shape': 3.1.7
+ '@types/d3-time': 3.0.4
+ '@types/d3-timer': 3.0.2
+ d3-array: 3.2.4
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-scale: 4.0.2
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-timer: 3.0.1
+
+ yaml@2.8.2: {}
+
+ zod@3.25.76: {}
diff --git a/postcss.config.mjs b/postcss.config.mjs
new file mode 100644
index 00000000..0dc456ad
--- /dev/null
+++ b/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('postcss-load-config').Config} */
+const config = {
+ plugins: {
+ tailwindcss: {},
+ },
+}
+
+export default config
diff --git a/public/apple-icon.png b/public/apple-icon.png
new file mode 100644
index 00000000..f9418b40
Binary files /dev/null and b/public/apple-icon.png differ
diff --git a/public/icon-dark-32x32.png b/public/icon-dark-32x32.png
new file mode 100644
index 00000000..12c825a1
Binary files /dev/null and b/public/icon-dark-32x32.png differ
diff --git a/public/icon-light-32x32.png b/public/icon-light-32x32.png
new file mode 100644
index 00000000..a3462ccd
Binary files /dev/null and b/public/icon-light-32x32.png differ
diff --git a/public/icon.svg b/public/icon.svg
new file mode 100644
index 00000000..5c11e6c5
--- /dev/null
+++ b/public/icon.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/placeholder-logo.png b/public/placeholder-logo.png
new file mode 100644
index 00000000..8a792ac2
Binary files /dev/null and b/public/placeholder-logo.png differ
diff --git a/public/placeholder-logo.svg b/public/placeholder-logo.svg
new file mode 100644
index 00000000..b1695aaf
--- /dev/null
+++ b/public/placeholder-logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/placeholder-user.jpg b/public/placeholder-user.jpg
new file mode 100644
index 00000000..6fa7543d
Binary files /dev/null and b/public/placeholder-user.jpg differ
diff --git a/public/placeholder.jpg b/public/placeholder.jpg
new file mode 100644
index 00000000..6bfe9633
Binary files /dev/null and b/public/placeholder.jpg differ
diff --git a/public/placeholder.svg b/public/placeholder.svg
new file mode 100644
index 00000000..e763910b
--- /dev/null
+++ b/public/placeholder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/spec/active_file/base_spec.rb b/spec/active_file/base_spec.rb
deleted file mode 100644
index 1daff629..00000000
--- a/spec/active_file/base_spec.rb
+++ /dev/null
@@ -1,158 +0,0 @@
-require 'spec_helper'
-
-describe ActiveFile::Base do
- before do
- class Country < ActiveFile::Base
- end
- end
-
- after do
- Object.send :remove_const, :Country
- end
-
- describe ".multiple_files?" do
- it "is false" do
- expect(Country.multiple_files?).to be_falsey
- end
- end
-
- describe ".filename=" do
- before do
- Country.filename = "foo-izzle"
-
- class Bar < ActiveFile::Base
- self.filename = "bar-izzle"
- end
- end
- after { Object.send :remove_const, :Bar }
-
- it "sets the filename on a per-subclass basis" do
- expect(Country.filename).to eq("foo-izzle")
- expect(Bar.filename).to eq("bar-izzle")
- end
- end
-
- describe ".find_each" do
- before do
- class Country
- def self.load_file()
- [{"name"=>"Niger", "id"=>1}, {"name"=>"Peru", "id"=>2}]
- end
- end
- end
-
- it "iterates over the data" do
- first_letters = ""
- expect do
- Country.find_each { |country| first_letters += country.name[0] }
- end.to change { first_letters }.to("NP")
- end
- end
-
- describe ".set_filename" do
- before do
- Country.set_filename "foo-izzle"
-
- class Bar < ActiveFile::Base
- set_filename "bar-izzle"
- end
- end
- after { Object.send :remove_const, :Bar }
-
- it "sets the filename on a per-subclass basis" do
- expect(Country.filename).to eq("foo-izzle")
- expect(Bar.filename).to eq("bar-izzle")
- end
- end
-
- describe ".root_path=" do
- before do
- Country.root_path = "foo-izzle"
-
- class Bar < ActiveFile::Base
- self.root_path = "bar-izzle"
- end
- end
- after { Object.send :remove_const, :Bar }
-
- it "sets the root_path on a per-subclass basis" do
- expect(Country.root_path).to eq("foo-izzle")
- expect(Bar.root_path).to eq("bar-izzle")
- end
- end
-
- describe ".set_root_path" do
- before do
- Country.set_root_path "foo-izzle"
-
- class Bar < ActiveFile::Base
- set_root_path "bar-izzle"
- end
- end
- after { Object.send :remove_const, :Bar }
-
- it "sets the root_path on a per-subclass basis" do
- expect(Country.root_path).to eq("foo-izzle")
- expect(Bar.root_path).to eq("bar-izzle")
- end
- end
-
- describe ".full_path" do
- it "defaults to the directory of the calling file" do
- class Country
- def self.extension() "foo" end
- end
-
- expect(Country.full_path).to eq("#{Dir.pwd}/countries.foo")
- end
- end
-
- describe ".reload" do
- before do
- class Country
- def self.load_file()
- {"new_york"=>{"name"=>"New York", "id"=>1}}.values
- end
- end
- Country.reload # initial load
- end
-
- context "when nothing has been modified" do
- it "does not reload anything" do
- class Country
- def self.load_file()
- raise "should not have been called"
- end
- end
- expect(Country.dirty).to be_falsey
- Country.reload
- expect(Country.dirty).to be_falsey
- end
- end
-
- context "when forced" do
- it "reloads the data" do
- class Country
- def self.load_file()
- {"new_york"=>{"name"=>"New York", "id"=>2}}.values
- end
- end
- expect(Country.dirty).to be_falsey
- expect(Country.find_by_id(2)).to be_nil
- Country.reload(true)
- expect(Country.dirty).to be_falsey
- expect(Country.find(2).name).to eq("New York")
- end
- end
-
- context "when the data has been modified" do
- it "reloads the data" do
- Country.create!
- expect(Country.dirty).to be_truthy
- Country.reload
- expect(Country.dirty).to be_falsey
- end
- end
- end
-
-end
diff --git a/spec/active_file/multiple_files_spec.rb b/spec/active_file/multiple_files_spec.rb
deleted file mode 100644
index ed81e55e..00000000
--- a/spec/active_file/multiple_files_spec.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-require 'spec_helper'
-
-describe ActiveFile::MultipleFiles do
- before do
- class Country < ActiveFile::Base
- use_multiple_files
- end
- end
-
- after do
- Object.send :remove_const, :Country
- end
-
- describe ".filenames=" do
- before do
- Country.filenames = ["country-file"]
-
- class Bar < ActiveFile::Base
- use_multiple_files
- self.filenames = ["bar-file"]
- end
- end
- after { Object.send :remove_const, :Bar }
-
- it "sets the filenames on a per-subclass basis" do
- expect(Country.filenames).to eq(["country-file"])
- expect(Bar.filenames).to eq(["bar-file"])
- end
- end
-
- describe "set_filenames" do
- before do
- Country.set_filenames "country-file"
-
- class Bar < ActiveFile::Base
- use_multiple_files
- set_filenames "bar-file", "baz-file"
- end
- end
- after { Object.send :remove_const, :Bar }
-
- it "sets the filenames on a per-subclass basis" do
- expect(Country.filenames).to eq(["country-file"])
- expect(Bar.filenames).to eq(["bar-file", "baz-file"])
- end
- end
-
- describe ".multiple_files?" do
- it "is true" do
- expect(Country.multiple_files?).to be_truthy
- end
-
- context "on a per class basis" do
- before do
- class Bar < ActiveFile::Base
- end
- end
- after { Object.send :remove_const, :Bar }
-
- it "is true for classes with filenames" do
- expect(Country.multiple_files?).to be_truthy
- expect(Bar.multiple_files?).to be_falsey
- end
- end
- end
-
- describe ".full_paths" do
- it "defaults to the directory of the calling file" do
- class Country
- def self.extension() "foo" end
- end
-
- expect(Country.full_paths).to eq(["#{Dir.pwd}/countries.foo"])
- end
-
- context "given multiple files do" do
- it "is good" do
- class Country
- def self.extension() "foo" end
- self.filenames = ["fizz", "bazz"]
- end
-
- expect(Country.full_paths).to eq(["#{Dir.pwd}/fizz.foo", "#{Dir.pwd}/bazz.foo"])
- end
- end
- end
-end
diff --git a/spec/active_hash/base_spec.rb b/spec/active_hash/base_spec.rb
deleted file mode 100644
index c2688150..00000000
--- a/spec/active_hash/base_spec.rb
+++ /dev/null
@@ -1,1905 +0,0 @@
-require 'spec_helper'
-
-describe ActiveHash, "Base" do
-
- before do
- class Country < ActiveHash::Base
- end
- end
-
- after do
- Object.send :remove_const, :Country
- end
-
- it "passes LocalJumpError through in .transaction when no block is given" do
- expect { Country.transaction }.to raise_error(LocalJumpError)
- end
-
- describe ".new" do
- it "yields a block" do
- expect { |b| Country.new(&b) }.to yield_with_args(Country)
- end
-
- context "initializing with a block" do
- subject do
- Country.fields :name
- Country.new do |country|
- country.name = 'Germany'
- end
- end
-
- it "sets assigns the fields" do
- expect(subject.name).to eq('Germany')
- end
- end
- end
-
- describe ".fields" do
- before do
- Country.fields :name, :iso_name
- end
-
- it "defines a reader for each field" do
- expect(Country.new).to respond_to(:name)
- expect(Country.new).to respond_to(:iso_name)
- end
-
- it "defines interrogator methods for each field" do
- expect(Country.new).to respond_to(:name?)
- expect(Country.new).to respond_to(:iso_name?)
- end
-
- it "defines single finder methods for each field" do
- expect(Country).to respond_to(:find_by_name)
- expect(Country).to respond_to(:find_by_iso_name)
- end
-
- it "defines banged single finder methods for each field" do
- expect(Country).to respond_to(:find_by_name!)
- expect(Country).to respond_to(:find_by_iso_name!)
- end
-
- it "defines array finder methods for each field" do
- expect(Country).to respond_to(:find_all_by_name)
- expect(Country).to respond_to(:find_all_by_iso_name)
- end
-
- it "does not define banged array finder methods for each field" do
- expect(Country).not_to respond_to(:find_all_by_name!)
- expect(Country).not_to respond_to(:find_all_by_iso_name!)
- end
-
- it "defines single finder methods for all combinations of fields" do
- expect(Country).to respond_to(:find_by_name_and_iso_name)
- expect(Country).to respond_to(:find_by_iso_name_and_name)
- end
-
- it "defines banged single finder methods for all combinations of fields" do
- expect(Country).to respond_to(:find_by_name_and_iso_name!)
- expect(Country).to respond_to(:find_by_iso_name_and_name!)
- end
-
- it "defines array finder methods for all combinations of fields" do
- expect(Country).to respond_to(:find_all_by_name_and_iso_name)
- expect(Country).to respond_to(:find_all_by_iso_name_and_name)
- end
-
- it "does not define banged array finder methods for all combinations of fields" do
- expect(Country).not_to respond_to(:find_all_by_name_and_iso_name!)
- expect(Country).not_to respond_to(:find_all_by_iso_name_and_name!)
- end
-
- it "allows you to pass options to the built-in find_by_* methods (but ignores the hash for now)" do
- expect(Country.find_by_name("Canada", :select => nil)).to be_nil
- expect(Country.find_all_by_name("Canada", :select => nil)).to eq([])
- end
-
- it "allows you to pass options to the custom find_by_* methods (but ignores the hash for now)" do
- expect(Country.find_by_name_and_iso_name("Canada", "CA", :select => nil)).to be_nil
- expect(Country.find_all_by_name_and_iso_name("Canada", "CA", :select => nil)).to eq([])
- end
-
- it "blows up if you try to overwrite :attributes" do
- expect do
- Country.field :attributes
- end.to raise_error(ActiveHash::ReservedFieldError)
- end
- end
-
- describe ".field_names" do
- before do
- Country.fields :name, :iso_name, "size"
- end
-
- it "returns an array of field names" do
- expect(Country.field_names).to eq([:name, :iso_name, :size])
- end
- end
-
- describe ".column_names" do
- before do
- Country.fields :name, :iso_name, "size"
- end
-
- it "returns an array of column names" do
- skip "Not supported in Ruby 3.0.0" if RUBY_VERSION < "3.0.0"
- expect(Country.column_names).to eq(["name", "iso_name", "size"])
- end
- end
-
- describe ".data=" do
- before do
- class Region < ActiveHash::Base
- field :description
- end
- end
-
- it "populates the object with data and auto-assigns keys" do
- Country.data = [{:name => "US"}, {:name => "Canada"}]
- expect(Country.data).to eq([{:name => "US", :id => 1}, {:name => "Canada", :id => 2}])
- end
-
- it "allows each of it's subclasses to have it's own data" do
- Country.data = [{:name => "US"}, {:name => "Canada"}]
- Region.data = [{:description => "A big region"}, {:description => "A remote region"}]
-
- expect(Country.data).to eq([{:name => "US", :id => 1}, {:name => "Canada", :id => 2}])
- expect(Region.data).to eq([{:description => "A big region", :id => 1}, {:description => "A remote region", :id => 2}])
- end
-
- it "marks the class as dirty" do
- expect(Country.dirty).to be_falsey
- Country.data = []
- expect(Country.dirty).to be_truthy
- end
- end
-
- describe ".add" do
- before do
- Country.fields :name
- end
-
- it "adds a record" do
- expect {
- Country.add :name => "Russia"
- }.to change { Country.count }
- end
-
- it "marks the class as dirty" do
- expect(Country.dirty).to be_falsey
- Country.add :name => "Russia"
- expect(Country.dirty).to be_truthy
- end
-
- it "returns the record" do
- record = Country.add :name => "Russia"
- expect(record.name).to eq("Russia")
- end
-
- it "should populate the id" do
- record = Country.add :name => "Russia"
- expect(record.id).not_to be_nil
- end
- end
-
- describe ".all" do
- before do
- Country.field :name
- Country.data = [
- {:id => 1, :name => "US"},
- {:id => 2, :name => "Canada"}
- ]
- end
-
- it "returns an empty array if data is nil" do
- Country.data = nil
- expect(Country.all).to be_empty
- end
-
- it "returns all data as inflated objects" do
- Country.all.all? { |country| expect(country).to be_kind_of(Country) }
- end
-
- it "populates the data correctly" do
- records = Country.all
- expect(records.first.id).to eq(1)
- expect(records.first.name).to eq("US")
- expect(records.last.id).to eq(2)
- expect(records.last.name).to eq("Canada")
- end
-
- it "re-populates the records after data= is called" do
- Country.data = [
- {:id => 45, :name => "Canada"}
- ]
- records = Country.all
- expect(records.first.id).to eq(45)
- expect(records.first.name).to eq("Canada")
- expect(records.length).to eq(1)
- end
-
- it "filters the records from a AR-like conditions hash" do
- record = Country.all(:conditions => {:name => 'US'})
- expect(record.count).to eq(1)
- expect(record.first.id).to eq(1)
- expect(record.first.name).to eq('US')
- end
- end
-
- describe ".reload" do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", :language => 'English'},
- {:id => 2, :name => "Canada", :language => 'English'},
- {:id => 3, :name => "Mexico", :language => 'Spanish'}
- ]
- end
-
- it "it reloads cached records" do
- countries = Country.where(language: 'Spanish')
- expect(countries.count).to eq(1)
-
- Country.create(id: 4, name: 'Spain', language: 'Spanish')
-
- expect(countries.count).to eq(1)
- countries.reload
- expect(countries.count).to eq(2)
- end
- end
-
- describe ".where" do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", :language => 'English'},
- {:id => 2, :name => "Canada", :language => 'English'},
- {:id => 3, :name => "Mexico", :language => 'Spanish'}
- ]
- end
-
- it 'returns a Relation class if conditions are provided' do
- expect(Country.where(language: 'English').class).to eq(ActiveHash::Relation)
- end
-
- it "returns WhereChain class if no conditions are provided" do
- expect(Country.where.class).to eq(ActiveHash::Relation::WhereChain)
- end
-
- it "returns all records when passed nil" do
- expect(Country.where(nil)).to eq(Country.all)
- end
-
- it "returns all records when an empty hash" do
- expect(Country.where({})).to eq(Country.all)
- end
-
- it "returns all data as inflated objects" do
- Country.where(:language => 'English').all? { |country| expect(country).to be_kind_of(Country) }
- end
-
- it "populates the data correctly" do
- records = Country.where(:language => 'English')
- expect(records.first.id).to eq(1)
- expect(records.first.name).to eq("US")
- expect(records.last.id).to eq(2)
- expect(records.last.name).to eq("Canada")
- end
-
- it "re-populates the records after data= is called" do
- Country.data = [
- {:id => 45, :name => "Canada"}
- ]
- records = Country.where(:name => 'Canada')
- expect(records.first.id).to eq(45)
- expect(records.first.name).to eq("Canada")
- expect(records.length).to eq(1)
- end
-
- it "filters the records from a AR-like conditions hash" do
- record = Country.where(:name => 'US')
- expect(record.count).to eq(1)
- expect(record.first.id).to eq(1)
- expect(record.first.name).to eq('US')
- end
-
- it "filters records when passed a hash with string keys" do
- record = Country.where('name' => 'US')
- expect(record.count).to eq(1)
- expect(record.first.id).to eq(1)
- expect(record.first.name).to eq('US')
- end
-
- it "raises an error if ids aren't unique" do
- expect do
- Country.data = [
- {:id => 1, :name => "US", :language => 'English'},
- {:id => 2, :name => "Canada", :language => 'English'},
- {:id => 2, :name => "Mexico", :language => 'Spanish'}
- ]
- end.to raise_error(ActiveHash::IdError)
- end
-
- it "returns a record for specified id" do
- record = Country.where(id: 1)
- expect(record.first.id).to eq(1)
- expect(record.first.name).to eq('US')
- end
-
- it "returns empty array" do
- expect(Country.where(id: nil)).to eq []
- end
-
- it "returns multiple records for multiple ids" do
- expect(Country.where(:id => %w(1 2)).map(&:id)).to match_array([1,2])
- end
-
- it "returns multiple records for range argument" do
- expect(Country.where(:id => 1..2).map(&:id)).to match_array([1,2])
- end
-
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
- it "returns multiple records for infinite range argument" do
- expect(Country.where(:id => eval("2..")).map(&:id)).to match_array([2,3])
- end
- end
-
- it "filters records for multiple values" do
- expect(Country.where(:name => %w(US Canada)).map(&:name)).to match_array(%w(US Canada))
- end
-
- it "filters records by a RegEx" do
- expect(Country.where(:language => /Eng/).map(&:name)).to match_array(%w(US Canada))
- end
-
- it "filters records for multiple symbol values" do
- expect(Country.where(:name => [:US, :Canada]).map(&:name)).to match_array(%w(US Canada))
- end
-
- it 'is chainable' do
- where_relation = Country.where(language: 'English')
-
- expect(where_relation.length).to eq 2
- expect(where_relation.map(&:id)).to eq([1, 2])
- chained_where_relation = where_relation.where(name: 'US')
- expect(chained_where_relation.length).to eq 1
- expect(chained_where_relation.map(&:id)).to eq([1])
- end
- end
-
- describe ".find_each" do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", :language => 'English'},
- {:id => 2, :name => "Canada", :language => 'English'},
- {:id => 3, :name => "Mexico", :language => 'Spanish'}
- ]
- end
-
- it "iterates over data" do
- logs = []
- Country.where(language: 'English').find_each do |country|
- logs << "visited #{country.name}"
- end
- expect(logs).to eq(["visited US", "visited Canada"])
- end
- end
-
- describe ".invert_where" do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", :language => 'English'},
- {:id => 2, :name => "Canada", :language => 'English'},
- {:id => 3, :name => "Mexico", :language => 'Spanish'}
- ]
- end
-
- it "inverts all conditions" do
- expect(Country.where(id: 1).where.not(id: 3).invert_where.map(&:name)).to match_array(%w(Mexico))
- end
- end
-
- describe ".where.not" do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", :language => 'English'},
- {:id => 2, :name => "Canada", :language => 'English'},
- {:id => 3, :name => "Mexico", :language => 'Spanish'}
- ]
- end
-
- it "raises ArgumentError if no conditions are provided" do
- expect{
- Country.where.not
- }.to raise_error(ArgumentError)
- end
-
- it 'returns a chainable Relation when conditions are passed' do
- expect(Country.where.not(language: 'Spanish').class).to eq(ActiveHash::Relation)
- end
-
- it "returns all records when passed nil" do
- expect(Country.where.not(nil)).to eq(Country.all)
- end
-
- it "returns all records when an empty hash" do
- expect(Country.where.not({})).to eq(Country.all)
- end
-
- it "returns all records as inflated objects" do
- Country.where.not(:language => 'English').all? { |country| expect(country).to be_kind_of(Country) }
- end
-
- it "populates the records correctly" do
- records = Country.where.not(:language => 'Spanish')
- expect(records.first.id).to eq(1)
- expect(records.first.name).to eq("US")
- expect(records.last.id).to eq(2)
- expect(records.last.name).to eq("Canada")
- expect(records.length).to eq(2)
- end
-
- it "re-populates the records after data= is called" do
- Country.data = [
- {:id => 45, :name => "Canada"}
- ]
- records = Country.where.not(:name => "US")
- expect(records.first.id).to eq(45)
- expect(records.first.name).to eq("Canada")
- expect(records.length).to eq(1)
- end
-
- it "filters the records from a AR-like conditions hash" do
- record = Country.where.not(:name => 'US')
- expect(record.first.id).to eq(2)
- expect(record.first.name).to eq('Canada')
- expect(record.last.id).to eq(3)
- expect(record.last.name).to eq('Mexico')
- expect(record.length).to eq(2)
- end
-
- it "returns the records for NOT specified id" do
- record = Country.where.not(id: 1)
- expect(record.first.id).to eq(2)
- expect(record.first.name).to eq('Canada')
- expect(record.last.id).to eq(3)
- expect(record.last.name).to eq('Mexico')
- end
-
- it "returns a chainable relation even if id is given" do
- expect(Country.where.not(id: 1).class).to eq(ActiveHash::Relation)
- end
-
- it "returns all records when id is nil" do
- expect(Country.where.not(:id => nil)).to eq Country.all
- end
-
- it "filters records for multiple ids" do
- expect(Country.where.not(:id => [1, 2]).pluck(:id)).to match_array([3])
- end
-
- it "filters records for multiple values" do
- expect(Country.where.not(:name => %w[US Canada]).pluck(:name)).to match_array(%w[Mexico])
- end
-
- it "filters records for multiple symbol values" do
- expect(Country.where.not(:name => %i[US Canada]).pluck(:name)).to match_array(%w[Mexico])
- end
-
- it "filters records for multiple conditions" do
- expect(Country.where.not(:id => 1, :name => 'Mexico')).to match_array([Country.find(2)])
- end
- end
-
- describe ".find_by" do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", :language => 'English'},
- {:id => 2, :name => "Canada", :language => 'English'},
- {:id => 3, :name => "Mexico", :language => 'Spanish'},
- {:id => 5, :name => "Any", :language => nil}
- ]
- end
-
- it "raises ArgumentError if no conditions are provided" do
- expect{
- Country.find_by
- }.to raise_error(ArgumentError)
- end
-
- it "returns first record when passed nil" do
- expect(Country.find_by(nil)).to eq(Country.first)
- end
-
- it "returns all data as inflated objects" do
- expect(Country.find_by(:language => 'English')).to be_kind_of(Country)
- end
-
- it "populates the data correctly" do
- record = Country.find_by(:language => 'English')
- expect(record.id).to eq(1)
- expect(record.name).to eq("US")
- end
-
- it "re-populates the records after data= is called" do
- Country.data = [
- {:id => 45, :name => "Canada"}
- ]
- record = Country.find_by(:name => 'Canada')
- expect(record.id).to eq(45)
- expect(record.name).to eq("Canada")
- end
-
- it "filters the records from a AR-like conditions hash" do
- record = Country.find_by(:name => 'US')
- expect(record.id).to eq(1)
- expect(record.name).to eq('US')
- end
-
- it "finds the record with the specified id as a string" do
- record = Country.find_by(:id => '1')
- expect(record.name).to eq('US')
- end
-
- it "returns the record that matches options" do
- expect(Country.find_by(:name => "US").id).to eq(1)
- end
-
- it "returns the record that matches options with symbol value" do
- expect(Country.find_by(:name => :US).id).to eq(1)
- end
-
- it "returns nil when not matched in candidates" do
- expect(Country.find_by(:name => "UK")).to be_nil
- end
-
- it "returns nil when passed a wrong id" do
- expect(Country.find_by(:id => 4)).to be_nil
- end
-
- it "finds record by nil value" do
- expect(Country.find_by(:language => nil).id).to eq(5)
- end
-
- it "doesn't finds nil records when searching for ''" do
- expect(Country.find_by(:language => '')).to be_nil
- end
- end
-
- describe ".find_by!" do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", :language => 'English'}
- ]
- end
-
- subject { Country.find_by!(name: word) }
-
- context 'when data exists' do
- let(:word) { 'US' }
- it { expect(subject.id).to eq 1 }
- end
-
- context 'when data not found' do
- let(:word) { 'UK' }
- it { expect{ subject }.to raise_error ActiveHash::RecordNotFound }
- it "raises 'RecordNotFound' when passed a wrong id" do
- expect { Country.find_by!(id: 2) }.
- to raise_error ActiveHash::RecordNotFound
- end
-
- it "raises 'RecordNotFound' when passed wrong id and options" do
- expect { Country.find_by!(id: 2, name: "FR") }.
- to raise_error ActiveHash::RecordNotFound
- end
- end
- end
-
- describe ".count" do
- before do
- Country.data = [
- {:id => 1, :name => "US"},
- {:id => 2, :name => "Canada"}
- ]
- end
-
- it "returns the number of elements in the array" do
- expect(Country.count).to eq(2)
- end
- end
-
- describe ".pluck" do
- before do
- Country.data = [
- {:id => 1, :name => "US", :language => "English"},
- {:id => 2, :name => "Canada", :language => "English"},
- {:id => 3, :name => "Mexico", :language => "Spanish"}
- ]
- end
-
- it "returns an two dimensional Array of 3 attributes values" do
- expect(Country.pluck(:id, :name, :language)).to match_array([[1, "US", "English"], [2, "Canada", "English"], [3, "Mexico", "Spanish"]])
- end
-
- it "returns an two dimensional Array of 2 attributes values" do
- expect(Country.pluck(:id, :name)).to match_array([[1, "US"], [2, "Canada"], [3, "Mexico"]])
- end
-
- it "returns an Array of attribute values" do
- expect(Country.pluck(:id)).to match_array([1, 2, 3])
- end
-
- context 'with the same field name and method name' do
- before do
- class CountryWithContinent < ActiveHash::Base
- self.data = [
- {:id => 1, :name => "US", continent: 1},
- {:id => 2, :name => "Canada", continent: 1},
- {:id => 3, :name => "Mexico", continent: 1},
- {:id => 4, :name => "Brazil", continent: 2}
- ]
-
- # behave like ActiveRecord Enum
- CONTINENTS = { north_america: 1, south_america: 2, europe: 3, asia: 4, africa: 5, oceania: 6 }
-
- def continent
- CONTINENTS.key(self[:continent]).to_sym
- end
- end
- end
-
- it "returns the value of the method when the field name is the same as the method name" do
- expect(CountryWithContinent.pluck(:id, :name, :continent)).to match_array([[1, "US", :north_america], [2, "Canada", :north_america], [3, "Mexico", :north_america], [4, "Brazil", :south_america]])
- end
- end
- end
-
- describe '.ids' do
- before do
- Country.data = [
- {:id => 1, :name => "US"},
- {:id => 2, :name => "Canada"}
- ]
- end
-
- it "returns an Array of id attributes" do
- expect(Country.ids).to match_array([1,2])
- end
- end
-
- describe ".pick" do
- before do
- Country.data = [
- {:id => 1, :name => "US"},
- {:id => 2, :name => "Canada"}
- ]
- end
-
- it "returns a dimensional Array of attributes values" do
- expect(Country.pick(:id, :name)).to match_array([1,"US"])
- end
-
- it "returns an attribute value" do
- expect(Country.pick(:id)).to eq 1
- end
- end
-
- describe ".first" do
- before do
- Country.data = [
- {:id => 1, :name => "US"},
- {:id => 2, :name => "Canada"}
- ]
- end
-
- it "returns the first object" do
- expect(Country.first).to eq(Country.new(:id => 1))
- end
- end
-
- describe ".last" do
- before do
- Country.data = [
- {:id => 1, :name => "US"},
- {:id => 2, :name => "Canada"}
- ]
- end
-
- it "returns the last object" do
- expect(Country.last).to eq(Country.new(:id => 2))
- end
- end
-
- describe ".find" do
- before do
- Country.data = [
- {:id => 1, :name => "US"},
- {:id => 2, :name => "Canada"}
- ]
- end
-
- context "with an id" do
- it "finds the record with the specified id" do
- expect(Country.find(2).id).to eq(2)
- end
-
- it "finds the record with the specified id as a string" do
- expect(Country.find("2").id).to eq(2)
- end
-
- it "raises ActiveHash::RecordNotFound when id not found" do
- expect {
- Country.find(0)
- }.to raise_error(an_instance_of(ActiveHash::RecordNotFound)
- .and having_attributes(
- message: "Couldn't find Country with ID=0",
- primary_key: 'id',
- id: 0
- )
- )
- end
- end
-
- context "with :all" do
- it "returns all records" do
- expect(Country.find(:all)).to eq([Country.new(:id => 1), Country.new(:id => 2)])
- end
- end
-
- context "with :first" do
- it "returns the first record" do
- expect(Country.find(:first)).to eq(Country.new(:id => 1))
- end
-
- it "returns the first record that matches the search criteria" do
- expect(Country.find(:first, :conditions => {:id => 2})).to eq(Country.new(:id => 2))
- end
-
- it "returns nil if none matches the search criteria" do
- expect(Country.find(:first, :conditions => {:id => 3})).to eq(nil)
- end
- end
-
- context "with 2 arguments" do
- it "returns the record with the given id and ignores the conditions" do
- expect(Country.find(1, :conditions => "foo=bar")).to eq(Country.new(:id => 1))
- expect(Country.find(:all, :conditions => "foo=bar").length).to eq(2)
- end
- end
-
- context "with an array of ids" do
- before do
- Country.data = [
- {:id => 1},
- {:id => 2},
- {:id => 3}
- ]
- end
-
- it "returns all matching ids" do
- expect(Country.find([1, 3])).to eq([Country.new(:id => 1), Country.new(:id => 3)])
- end
-
- it "raises ActiveHash::RecordNotFound when id not found" do
- expect do
- Country.find([0, 3])
- end.to raise_error(ActiveHash::RecordNotFound, /Couldn't find Country with ID=0/)
- end
- end
-
- context "with nil" do
- context 'and no block' do
- it "raises ActiveHash::RecordNotFound when id is nil" do
- expect do
- Country.find(nil)
- end.to raise_error(ActiveHash::RecordNotFound, /Couldn't find Country without an ID/)
- end
- end
-
- context 'and a block' do
- it 'finds the record by evaluating the block' do
- country = Country.find { |c| c.id == 1 }
-
- expect(country).to be_a(Country)
- expect(country.name).to eq('US')
- end
- end
- end
- end
-
- describe ".find_by_id" do
- before do
- Country.data = [
- {:id => 1, :name => "US"},
- {:id => 2, :name => "Canada"}
- ]
- end
-
- context "with an id" do
- it "finds the record with the specified id" do
- expect(Country.find_by_id(2).id).to eq(2)
- end
-
- it "finds the record with the specified id as a string" do
- expect(Country.find_by_id("2").id).to eq(2)
- end
-
- it "finds the record with a chained filter" do
- expect(Country.where(name: "Canada").find_by_id("2").id).to eq(2)
- end
-
- it "filters ecord with a chained filter" do
- expect(Country.where(name: "Canada").find_by_id("1")).to be_nil
- end
- end
-
- context "with string ids" do
- before do
- Country.data = [
- {:id => "abc", :name => "US"},
- {:id => "def", :name => "Canada"}
- ]
- end
-
- it "finds the record with the specified id" do
- expect(Country.find_by_id("abc").id).to eq("abc")
- end
- end
-
- context "with nil" do
- it "returns nil" do
- expect(Country.find_by_id(nil)).to be_nil
- end
- end
-
- context "with an id not present" do
- it "returns nil" do
- expect(Country.find_by_id(4567)).to be_nil
- end
- end
- end
-
- describe "custom finders" do
- before do
- Country.fields :name, :monarch, :language
-
- # Start ids above 4 lest we get nil and think it's an AH::Base model with id=4.
- Country.data = [
- {:id => 11, :name => nil, :monarch => nil, :language => "Latin"},
- {:id => 12, :name => "US", :monarch => nil, :language => "English"},
- {:id => 13, :name => "Canada", :monarch => "The Crown of England", :language => "English"},
- {:id => 14, :name => "UK", :monarch => "The Crown of England", :language => "English"}
- ]
- end
-
- describe "find_by_" do
- describe "with a match" do
- context "for a non-nil argument" do
- it "returns the first matching record" do
- expect(Country.find_by_name("US").id).to eq(12)
- end
- end
-
- context "for a nil argument" do
- it "returns the first matching record" do
- expect(Country.find_by_name(nil).id).to eq(11)
- end
- end
- end
-
- describe "without a match" do
- before do
- Country.data = []
- end
-
- context "for a non-nil argument" do
- it "returns nil" do
- expect(Country.find_by_name("Mexico")).to be_nil
- end
- end
-
- context "for a nil argument" do
- it "returns nil" do
- expect(Country.find_by_name(nil)).to be_nil
- end
- end
- end
- end
-
- describe "find_by_!" do
- describe "with a match" do
- context "for a non-nil argument" do
- it "returns the first matching record" do
- expect(Country.find_by_name!("US").id).to eq(12)
- end
- end
-
- context "for a nil argument" do
- it "returns the first matching record" do
- expect(Country.find_by_name!(nil).id).to eq(11)
- end
- end
- end
-
- describe "without a match" do
- before do
- Country.data = []
- end
-
- context "for a non-nil argument" do
- it "raises ActiveHash::RecordNotFound" do
- expect { Country.find_by_name!("Mexico") }.to raise_error(ActiveHash::RecordNotFound, /Couldn't find Country with name = Mexico/)
- end
- end
-
- context "for a nil argument" do
- it "raises ActiveHash::RecordNotFound" do
- expect { Country.find_by_name!(nil) }.to raise_error(ActiveHash::RecordNotFound, /Couldn't find Country with name = /)
- end
- end
- end
- end
-
- describe "find_all_by_" do
- describe "with matches" do
- it "returns all matching records" do
- countries = Country.find_all_by_monarch("The Crown of England")
- expect(countries.length).to eq(2)
- expect(countries.first.name).to eq("Canada")
- expect(countries.last.name).to eq("UK")
- end
- end
-
- describe "without matches" do
- it "returns an empty array" do
- expect(Country.find_all_by_name("Mexico")).to be_empty
- end
- end
- end
-
- describe "find_by__and_" do
- describe "with a match" do
- it "returns the first matching record" do
- expect(Country.find_by_name_and_monarch("Canada", "The Crown of England").id).to eq(13)
- expect(Country.find_by_monarch_and_name("The Crown of England", "Canada").id).to eq(13)
- end
- end
-
- describe "with a match based on to_s" do
- it "returns the first matching record" do
- expect(Country.find_by_name_and_id("Canada", "13").id).to eq(13)
- end
- end
-
- describe "without a match" do
- it "returns nil" do
- expect(Country.find_by_name_and_monarch("US", "The Crown of England")).to be_nil
- end
- end
-
- describe "for fields the class doesn't have" do
- it "raises a NoMethodError" do
- expect {
- Country.find_by_name_and_shoe_size("US", 10)
- }.to raise_error(NoMethodError, /undefined method [`']find_by_name_and_shoe_size' (?:for|on) (class )?Country/)
- end
- end
- end
-
- describe "find_by__and_!" do
- describe "with a match" do
- it "returns the first matching record" do
- expect(Country.find_by_name_and_monarch!("Canada", "The Crown of England").id).to eq(13)
- expect(Country.find_by_monarch_and_name!("The Crown of England", "Canada").id).to eq(13)
- end
- end
-
- describe "with a match based on to_s" do
- it "returns the first matching record" do
- expect(Country.find_by_name_and_id!("Canada", "13").id).to eq(13)
- end
- end
-
- describe "without a match" do
- it "raises ActiveHash::RecordNotFound" do
- expect { Country.find_by_name_and_monarch!("US", "The Crown of England") }.to raise_error(ActiveHash::RecordNotFound, /Couldn't find Country with name = US, monarch = The Crown of England/)
- end
- end
-
- describe "for fields the class doesn't have" do
- it "raises a NoMethodError" do
- expect {
- Country.find_by_name_and_shoe_size!("US", 10)
- }.to raise_error(NoMethodError, /undefined method [`']find_by_name_and_shoe_size!' (?:for|on) (class )?Country/)
- end
- end
- end
-
- describe "find_all_by__and_" do
- describe "with matches" do
- it "returns all matching records" do
- countries = Country.find_all_by_monarch_and_language("The Crown of England", "English")
- expect(countries.length).to eq(2)
- expect(countries.first.name).to eq("Canada")
- expect(countries.last.name).to eq("UK")
- end
- end
-
- describe "without matches" do
- it "returns an empty array" do
- expect(Country.find_all_by_monarch_and_language("Shaka Zulu", "Zulu")).to be_empty
- end
- end
- end
- end
-
- describe ".order" do
- before do
- Country.field :name
- Country.field :language
- Country.field :code
- Country.data = [
- { id: 1, name: "US", language: "English", code: 1 },
- { id: 2, name: "Canada", language: "English", code: 1 },
- { id: 3, name: "Mexico", language: "Spanish", code: 52 }
- ]
- end
-
- it "raises ArgumentError if no args are provieded" do
- expect { Country.order() }.to raise_error(ArgumentError, 'The method .order() must contain arguments.')
- end
-
- it "returns all records when passed nil" do
- expect(Country.order(nil)).to eq Country.all
- end
-
- it "returns all records when an empty hash" do
- expect(Country.order({})).to eq Country.all
- end
-
- it "returns all records ordered by name attribute in ASC order when ':name' is provieded" do
- countries = Country.order(:name)
- expect(countries.first).to eq Country.find_by(name: "Canada")
- expect(countries.second).to eq Country.find_by(name: "Mexico")
- expect(countries.third).to eq Country.find_by(name: "US")
- end
-
- it "returns all records ordered by name attribute in DESC order when 'name: :desc' is provieded" do
- countries = Country.order(name: :desc)
- expect(countries.first).to eq Country.find_by(name: "US")
- expect(countries.second).to eq Country.find_by(name: "Mexico")
- expect(countries.third).to eq Country.find_by(name: "Canada")
- end
-
- it "returns all records ordered by code attribute, followed by id attribute in DESC order when ':code, id: :desc' is provieded" do
- countries = Country.order(:code, id: :desc)
- expect(countries.first).to eq Country.find_by(name: "Canada")
- expect(countries.second).to eq Country.find_by(name: "US")
- expect(countries.third).to eq Country.find_by(name: "Mexico")
- end
-
- it "returns all records ordered by name attribute in ASC order when 'name' is provieded" do
- countries = Country.order("name")
- expect(countries.first).to eq Country.find_by(name: "Canada")
- expect(countries.second).to eq Country.find_by(name: "Mexico")
- expect(countries.third).to eq Country.find_by(name: "US")
- end
-
- it "returns all records ordered by name attribute in DESC order when 'name: :desc' is provieded" do
- countries = Country.order("name DESC")
- expect(countries.first).to eq Country.find_by(name: "US")
- expect(countries.second).to eq Country.find_by(name: "Mexico")
- expect(countries.third).to eq Country.find_by(name: "Canada")
- end
-
- it "returns all records ordered by code attributes, followed by id attribute in DESC order when ':code, id: :desc' is provieded" do
- countries = Country.order("code, id DESC")
- expect(countries.first).to eq Country.find_by(name: "Canada")
- expect(countries.second).to eq Country.find_by(name: "US")
- expect(countries.third).to eq Country.find_by(name: "Mexico")
- end
-
- it "populates the data correctly in the order provided" do
- countries = Country.where(language: 'English').order(id: :desc)
-
- expect(countries.count).to eq 2
- expect(countries.first).to eq Country.find_by(name: "Canada")
- expect(countries.second).to eq Country.find_by(name: "US")
- end
-
- it "can be chained" do
- countries = Country.order(language: :asc)
- expect(countries.first).to eq Country.find_by(name: "US")
-
- countries = countries.order(name: :asc)
- expect(countries.first).to eq Country.find_by(name: "Canada")
- end
-
- it "doesn't change the order of original records" do
- countries = Country.order(id: :desc)
-
- expect(countries.first).to eq Country.find_by(name: "Mexico")
- expect(countries.second).to eq Country.find_by(name: "Canada")
- expect(countries.third).to eq Country.find_by(name: "US")
-
- expect(countries.find(1)).to eq Country.find_by(name: "US")
-
- expect(Country.all.first).to eq Country.find_by(name: "US")
- expect(Country.all.second).to eq Country.find_by(name: "Canada")
- expect(Country.all.third).to eq Country.find_by(name: "Mexico")
- end
- end
-
- describe ".reorder" do
- it "re-orders records" do
- countries = Country.order(language: :asc)
- expect(countries.first).to eq Country.find_by(name: "US")
-
- countries = countries.reorder(id: :desc)
- expect(countries.first).to eq Country.find_by(name: "Mexico")
- end
- end
-
- describe ".exists?" do
- before do
- Country.field :name
- Country.field :language
- Country.field :code
- Country.data = [
- { id: 1, name: "US", language: "English", code: 1 },
- { id: 2, name: "Canada", language: "English", code: 1 },
- { id: 3, name: "Mexico", language: "Spanish", code: 52 }
- ]
- end
-
- context "when data are exists and no arguments is passed" do
- it "return true" do
- expect(Country.exists?).to be_truthy
- end
- end
-
- context "when no data are exists and no arguments is passed" do
- before do
- Country.field :name
- Country.field :language
- Country.field :code
- Country.data = []
- end
-
- it "return false" do
- expect(Country.exists?).to be_falsy
- end
- end
-
- context "when false is passed" do
- it "return false" do
- expect(Country.exists?(false)).to be_falsy
- end
- end
-
- context "when nil is passed" do
- it "return nil" do
- expect(Country.exists?(nil)).to be_falsy
- end
- end
-
- describe "with matches" do
- context 'for a record argument' do
- it "return true" do
- expect(Country.exists?(Country.new({ id: 1, name: "US", language: "English", code: 1 }))).to be_truthy
- end
- end
-
- context "for an integer argument" do
- it "return true" do
- expect(Country.exists?(1)).to be_truthy
- end
- end
-
- context "for a string argument" do
- it "return true" do
- expect(Country.exists?("1")).to be_truthy
- end
- end
-
- context "for a hash argument" do
- it "return true" do
- expect(Country.exists?(name: "US", language: "English")).to be_truthy
- end
- end
- end
-
- describe "without matches" do
- context 'for a record argument' do
- it "return false" do
- expect(Country.exists?(Country.new({ id: 4, name: "Franch", language: "French", code: 16 }))).to be_falsy
- end
- end
-
- context "for an integer argument" do
- it "return false" do
- expect(Country.exists?(4)).to be_falsy
- end
- end
-
- context "for a string argument" do
- it "return false" do
- expect(Country.exists?("4")).to be_falsy
- end
- end
-
- context "for a hash argument" do
- it "return false" do
- expect(Country.exists?(name: "US", language: "Spanish")).to be_falsy
- end
- end
- end
- end
-
- describe "#method_missing" do
- it "doesn't blow up if you call a missing dynamic finder when fields haven't been set" do
- expect do
- Country.find_by_name("Foo")
- end.to raise_error(NoMethodError, /undefined method [`']find_by_name' (?:for|on) (class )?Country/)
- end
- end
-
- describe "#attributes" do
- it "returns the hash passed in the initializer" do
- Country.field :foo
- country = Country.new(:foo => :bar)
- expect(country.attributes).to eq({:foo => :bar})
- end
-
- it "symbolizes keys" do
- Country.field :foo
- country = Country.new("foo" => :bar)
- expect(country.attributes).to eq({:foo => :bar})
- end
-
- it "works with #[]" do
- Country.field :foo
- country = Country.new(:foo => :bar)
- expect(country[:foo]).to eq(:bar)
- end
-
- it "works with _read_attribute" do
- Country.field :foo
- country = Country.new(:foo => :bar)
- expect(country._read_attribute(:foo)).to eq(:bar)
- end
-
- it "works when string key passed to _read_attribute" do
- Country.field :foo
- country = Country.new(:foo => :bar)
- expect(country._read_attribute('foo')).to eq(:bar)
- end
-
- it "works with read_attribute" do
- Country.field :foo
- country = Country.new(:foo => :bar)
- expect(country.read_attribute(:foo)).to eq(:bar)
- end
-
- it "works when string key passed to read_attribute" do
- Country.field :foo
- country = Country.new(:foo => :bar)
- expect(country.read_attribute('foo')).to eq(:bar)
- end
-
- it "works with #[]=" do
- Country.field :foo
- country = Country.new
- country[:foo] = :bar
- expect(country.foo).to eq(:bar)
- end
- end
-
- describe "reader methods" do
- context "for regular fields" do
- before do
- Country.fields :name, :iso_name
- end
-
- it "returns the given attribute when present" do
- country = Country.new(:name => "Spain")
- expect(country.name).to eq("Spain")
- end
-
- it "returns nil when not present" do
- country = Country.new
- expect(country.name).to be_nil
- end
- end
-
- context "for fields with default values" do
- before do
- Country.field :name, :default => "foobar"
- end
-
- it "returns the given attribute when present" do
- country = Country.new(:name => "Spain")
- expect(country.name).to eq("Spain")
- end
-
- it "returns the default value when not present" do
- country = Country.new
- expect(country.name).to eq("foobar")
- end
-
- context "#attributes" do
- it "returns the default value when not present" do
- country = Country.new
- expect(country.attributes[:name]).to eq("foobar")
- end
-
- context "when the default value is false" do
- before do
- Country.field :active, :default => false
- end
-
- it "returns the default value when not present" do
- country = Country.new
- expect(country.attributes[:active]).to eq(false)
- end
- end
- end
- end
- end
-
- describe "interrogator methods" do
- before do
- Country.fields :name, :iso_name
- end
-
- it "returns true if the given attribute is non-blank" do
- country = Country.new(:name => "Spain")
- expect(country).to be_name
- end
-
- it "returns false if the given attribute is blank" do
- country = Country.new(:name => " ")
- expect(country.name?).to eq(false)
- end
-
- it "returns false if the given attribute was not passed" do
- country = Country.new
- expect(country).not_to be_name
- end
- end
-
- describe "#id" do
- context "when not passed an id" do
- it "returns nil" do
- country = Country.new
- expect(country.id).to be_nil
- end
- end
- end
-
- describe "#quoted_id" do
- it "should return id" do
- expect(Country.new(:id => 2).quoted_id).to eq(2)
- end
- end
-
- describe "#to_param" do
- it "should return id as a string" do
- expect(Country.create(:id => 2).to_param).to eq("2")
- end
- end
-
- describe "#persisted" do
- it "should return true if the object has been saved" do
- expect(Country.create(:id => 2)).to be_persisted
- end
-
- it "should return false if the object has not been saved" do
- expect(Country.new(:id => 2)).not_to be_persisted
- end
- end
-
- describe "#persisted" do
- it "should return true if the object has been saved" do
- expect(Country.create(:id => 2)).to be_persisted
- end
-
- it "should return false if the object has not been saved" do
- expect(Country.new(:id => 2)).not_to be_persisted
- end
- end
-
- describe "#eql?" do
- before do
- class Region < ActiveHash::Base
- end
- end
-
- it "should return true with the same class and id" do
- expect(Country.new(:id => 23).eql?(Country.new(:id => 23))).to be_truthy
- end
-
- it "should return false with the same class and different ids" do
- expect(Country.new(:id => 24).eql?(Country.new(:id => 23))).to be_falsey
- end
-
- it "should return false with the different classes and the same id" do
- expect(Country.new(:id => 23).eql?(Region.new(:id => 23))).to be_falsey
- end
-
- it "returns false when id is nil" do
- expect(Country.new.eql?(Country.new)).to be_falsey
- end
- end
-
- describe "#==" do
- before do
- class Region < ActiveHash::Base
- end
- end
-
- it "should return true with the same class and id" do
- expect(Country.new(:id => 23)).to eq(Country.new(:id => 23))
- end
-
- it "should return false with the same class and different ids" do
- expect(Country.new(:id => 24)).not_to eq(Country.new(:id => 23))
- end
-
- it "should return false with the different classes and the same id" do
- expect(Country.new(:id => 23)).not_to eq(Region.new(:id => 23))
- end
-
- it "returns false when id is nil" do
- expect(Country.new).not_to eq(Country.new)
- end
- end
-
- describe "#hash" do
- it "returns id for hash" do
- expect(Country.new(:id => 45).hash).to eq(45.hash)
- expect(Country.new.hash).to eq(nil.hash)
- end
-
- it "is hashable" do
- expect({Country.new(:id => 4) => "bar"}).to eq({Country.new(:id => 4) => "bar"})
- expect({Country.new(:id => 3) => "bar"}).not_to eq({Country.new(:id => 4) => "bar"})
- end
- end
-
- describe "#readonly?" do
- it "returns true" do
- expect(Country.new).to be_readonly
- end
- end
-
- describe "auto-discovery of fields" do
- it "dynamically creates fields for all keys in the hash" do
- Country.data = [
- {:field1 => "foo"},
- {:field2 => "bar"},
- {:field3 => "biz"}
- ]
-
- [:field1, :field2, :field3].each do |field|
- expect(Country).to respond_to("find_by_#{field}")
- expect(Country).to respond_to("find_all_by_#{field}")
- expect(Country.new).to respond_to(field)
- expect(Country.new).to respond_to("#{field}?")
- end
- end
-
- it "doesn't override methods already defined" do
- Country.class_eval do
- class << self
- def find_by_name(name)
- "find_by_name defined manually"
- end
-
- def find_all_by_name(name)
- "find_all_by_name defined manually"
- end
- end
-
- def name
- "name defined manually"
- end
-
- def name?
- "name? defined manually"
- end
- end
-
- expect(Country.find_by_name("foo")).to eq("find_by_name defined manually")
- expect(Country.find_all_by_name("foo")).to eq("find_all_by_name defined manually")
- expect(Country.new.name).to eq("name defined manually")
- expect(Country.new.name?).to eq("name? defined manually")
-
- Country.data = [
- {:name => "foo"}
- ]
-
- Country.all
- expect(Country.find_by_name("foo")).to eq("find_by_name defined manually")
- expect(Country.find_all_by_name("foo")).to eq("find_all_by_name defined manually")
- expect(Country.new.name).to eq("name defined manually")
- expect(Country.new.name?).to eq("name? defined manually")
- end
- end
-
- describe "using with belongs_to in ActiveRecord", :unless => SKIP_ACTIVE_RECORD do
- before do
- Country.data = [
- {:id => 1, :name => "foo"}
- ]
-
- class Book < ActiveRecord::Base
- establish_connection :adapter => "sqlite3", :database => ":memory:"
- connection.create_table(:books, :force => true) do |t|
- t.text :subject_type
- t.integer :subject_id
- t.integer :country_id
- end
-
- extend ActiveHash::Associations::ActiveRecordExtensions
-
- belongs_to :subject, :polymorphic => true
- belongs_to :country
- end
- end
-
- after do
- Object.send :remove_const, :Book
- end
-
- it "should be possible to use it as a parent" do
- book = Book.new
- book.country = Country.first
- expect(book.country).to eq(Country.first)
- end
-
- it "should be possible to use it as a polymorphic parent" do
- book = Book.new
- book.subject = Country.first
- expect(book.subject).to eq(Country.first)
- end
-
- end
-
- describe "#cache_key" do
- it 'should use the class\'s cache_key and id' do
- Country.data = [
- {:id => 1, :name => "foo"}
- ]
-
- expect(Country.first.cache_key).to eq('countries/1')
- end
-
- it 'should use the record\'s updated_at if present' do
- timestamp = Time.now
-
- Country.data = [
- {:id => 1, :name => "foo", :updated_at => timestamp}
- ]
-
- if ActiveSupport::VERSION::MAJOR < 7
- expect(Country.first.cache_key).to eq("countries/1-#{timestamp.to_s(:number)}")
- else
- expect(Country.first.cache_key).to eq("countries/1-#{timestamp.to_fs(:number)}")
- end
- end
-
- it 'should use "new" instead of the id for a new record' do
- expect(Country.new(:id => 1).cache_key).to eq('countries/new')
- end
- end
-
- describe "#save" do
-
- before do
- Country.field :name
- end
-
- it "adds the new object to the data collection" do
- expect(Country.all).to be_empty
- country = Country.new :id => 1, :name => "foo"
- expect(country.save).to be_truthy
- expect(Country.all).to eq([country])
- end
-
- it "adds the new object to the data collection" do
- expect(Country.all).to be_empty
- country = Country.new :id => 1, :name => "foo"
- expect(country.save!).to be_truthy
- expect(Country.all).to eq([country])
- end
-
- it "marks the class as dirty" do
- expect(Country.dirty).to be_falsey
- Country.new(:id => 1, :name => "foo").save
- expect(Country.dirty).to be_truthy
- end
-
- it "it is a no-op if the object has already been added to the collection" do
- expect(Country.all).to be_empty
- country = Country.new :id => 1, :name => "foo"
- country.save
- country.name = "bar"
- country.save
- country.save!
- expect(Country.all).to eq([country])
- end
-
- end
-
- describe ".create" do
-
- before do
- Country.field :name
- end
-
- it "works with no args" do
- expect(Country.all).to be_empty
- country = Country.create
- expect(country.id).to eq(1)
- end
-
- it "adds the new object to the data collection" do
- expect(Country.all).to be_empty
- country = Country.create :id => 1, :name => "foo"
- expect(country.id).to eq(1)
- expect(country.name).to eq("foo")
- expect(Country.all).to eq([country])
- end
-
- it "adds an auto-incrementing id if the id is nil" do
- country1 = Country.new :name => "foo"
- country1.save
- expect(country1.id).to eq(1)
-
- country2 = Country.new :name => "bar"
- country2.save
- expect(country2.id).to eq(2)
- end
-
- it "does not add auto-incrementing id if the id is present" do
- country1 = Country.new :id => 456, :name => "foo"
- country1.save
- expect(country1.id).to eq(456)
- end
-
- it "does not blow up with strings" do
- country1 = Country.new :id => "foo", :name => "foo"
- country1.save
- expect(country1.id).to eq("foo")
-
- country2 = Country.new :name => "foo"
- country2.save
- expect(country2.id).to be_nil
- end
-
- it "adds the new object to the data collection" do
- expect(Country.all).to be_empty
- country = Country.create! :id => 1, :name => "foo"
- expect(country.id).to eq(1)
- expect(country.name).to eq("foo")
- expect(Country.all).to eq([country])
- end
-
- it "marks the class as dirty" do
- expect(Country.dirty).to be_falsey
- Country.create! :id => 1, :name => "foo"
- expect(Country.dirty).to be_truthy
- end
-
- end
-
- describe "#valid?" do
-
- it "should return true" do
- expect(Country.new).to be_valid
- end
-
- end
-
- describe "#new_record?" do
- before do
- Country.field :name
- Country.data = [
- :id => 1, :name => "foo"
- ]
- end
-
- it "returns false when the object is already part of the collection" do
- expect(Country.new(:id => 1)).not_to be_new_record
- end
-
- it "returns true when the object is not part of the collection" do
- expect(Country.new(:id => 2)).to be_new_record
- end
-
- end
-
- describe "#model_name" do
- # This test reproduces the issue reported in https://github.com/active-hash/active_hash/pull/335,
- # regarding whether ActiveHash (and its derived models) expose `#model_name`.
- # Some serializers (e.g., okuramasafumi/alba) use the `#model_name` method internally,
- # so this test ensures that a NoMethodError is not raised.
- it "exposes model_name correctly" do
- instance = Country.new(:id => 1)
- expect { instance.model_name }.not_to raise_error
- end
- end
-
- describe ".transaction" do
-
- it "execute the block given to it" do
- foo = Object.new
- expect(foo).to receive(:bar)
- Country.transaction do
- foo.bar
- end
- end
-
- it "swallows ActiveRecord::Rollback errors", :unless => SKIP_ACTIVE_RECORD do
- expect do
- Country.transaction do
- raise ActiveRecord::Rollback
- end
- end.not_to raise_error
- end
-
- it "passes other errors through" do
- expect do
- Country.transaction do
- raise "hell"
- end
- end.to raise_error("hell")
- end
-
- end
-
- describe ".delete_all" do
-
- it "clears out all record" do
- country1 = Country.create
- country2 = Country.create
- expect(Country.all).to eq([country1, country2])
- Country.delete_all
- expect(Country.all).to be_empty
- end
-
- it "marks the class as dirty" do
- expect(Country.dirty).to be_falsey
- Country.delete_all
- expect(Country.dirty).to be_truthy
- end
-
- end
-
- describe '.scope' do
- context 'for query without argument' do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", continent: 'North America', :language => 'English'},
- {:id => 2, :name => "Canada" , continent: 'North America', :language => 'English'},
- {:id => 3, :name => "Mexico" , continent: 'North America', :language => 'Spanish'},
- {:id => 4, :name => "Brazil" , continent: 'South America', :language => 'Portuguese'}
- ]
- Country.scope :english_language, -> { where(language: 'English') }
- Country.scope :portuguese_language, -> { where(language: 'Portuguese') }
- Country.scope :south_america, -> { where(continent: 'South America') }
- end
-
- it 'should define a scope method' do
- expect(Country.respond_to?(:english_language)).to be_truthy
- end
-
- it 'should return the query used to define the scope' do
- expect(Country.english_language).to eq Country.where(language: 'English')
- end
-
- it 'should behave like the query used to define the scope' do
- expect(Country.english_language.count).to eq 2
- expect(Country.english_language.first.id).to eq 1
- expect(Country.english_language.second.id).to eq 2
- end
-
- it 'should be chainable' do
- expect(Country.south_america.portuguese_language).to(
- eq(Country.where(continent: 'South America').where(language: 'Portuguese'))
- )
- end
- end
-
- context 'for query with argument' do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", continent: 'North America', :language => 'English'},
- {:id => 2, :name => "Canada", continent: 'North America', :language => 'English'},
- {:id => 3, :name => "Mexico", continent: 'North America', :language => 'Spanish'},
- {:id => 4, :name => "Brazil" , continent: 'South America', :language => 'Portuguese'}
- ]
- Country.scope :with_continent, ->(continent) { where(continent: continent) }
- Country.scope :with_language, ->(language) { where(language: language) }
- end
-
- it 'should define a scope method' do
- expect(Country.respond_to?(:with_language)).to be_truthy
- end
-
- it 'should return the query used to define the scope' do
- expect(Country.with_language('English')).to eq Country.where(language: 'English')
- end
-
- it 'should behave like the query used to define the scope' do
- expect(Country.with_language('English').count).to eq 2
- expect(Country.with_language('English').first.id).to eq 1
- expect(Country.with_language('English').second.id).to eq 2
- end
-
- it 'should be chainable' do
- expect(Country.with_continent('South America').with_language('Portuguese')).to(
- eq(Country.where(continent: 'South America').where(language: 'Portuguese'))
- )
- end
- end
-
- context 'when scope body is not a lambda' do
- before do
- Country.field :name
- Country.field :language
- Country.data = [
- {:id => 1, :name => "US", :language => 'English'},
- {:id => 2, :name => "Canada", :language => 'English'},
- {:id => 3, :name => "Mexico", :language => 'Spanish'}
- ]
- end
-
- it 'should raise an error' do
- expect { Country.scope :invalid_scope, :not_a_callable }.to raise_error(ArgumentError, 'body needs to be callable')
- end
- end
- end
-
- describe 'ActiveModel::Translation' do
- around(:example) do |example|
- if Object.const_defined?(:ActiveModel)
- example.run
- else
- skip
- end
- end
-
- context 'if the locale is set to :ja' do
- around(:example) do |example|
- current_locale = I18n.locale
- I18n.locale = :ja
-
- example.run
-
- I18n.locale = current_locale
- end
-
- subject { Country.model_name.human }
-
- it { is_expected.to eq('国') }
- end
- end
-end
diff --git a/spec/active_hash/relation_spec.rb b/spec/active_hash/relation_spec.rb
deleted file mode 100644
index bf892695..00000000
--- a/spec/active_hash/relation_spec.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe ActiveHash::Relation do
- let(:model_class) do
- Class.new(ActiveHash::Base) do
- self.data = [
- {:id => 1, :name => "US"},
- {:id => 2, :name => "Canada"}
- ]
- end
- end
-
- subject { model_class.all }
-
- describe '#sample' do
- it 'delegate `sample` to Array' do
- expect(subject).to respond_to(:sample)
- end
-
- it 'return a random element or n random elements' do
- records = subject
-
- expect(records.sample).to be_present
- expect(records.sample(2).count).to eq(2)
- end
- end
-
- describe '#to_ary' do
- it 'returns an array' do
- expect(subject.to_ary).to be_an(Array)
- end
-
- it 'contains the same items as the relation' do
- array = subject.to_ary
-
- expect(array.length).to eq(subject.count)
- expect(array.first.id).to eq(1)
- expect(array.second.id).to eq(2)
- end
- end
-
- describe '#count' do
- it 'supports a block arg' do
- expect(subject.count { |s| s.name == "US" }).to eq(1)
- end
-
- it 'returns the correct number of items of the relation' do
- expect(subject.count).to eq(2)
- end
- end
-
- describe '#size' do
- it 'returns an Integer' do
- expect(subject.size).to be_an(Integer)
- end
-
- it 'returns the correct number of items of the relation' do
- array = subject.to_ary
-
- expect(array.size).to eq(2)
- end
- end
-
- describe "colliding methods https://github.com/active-hash/active_hash/issues/280" do
- it "should handle attributes named after existing methods" do
- klass = Class.new(ActiveHash::Base) do
- self.data = [
- {
- id: 1,
- name: "Aaa",
- display: true,
- },
- {
- id: 2,
- name: "Bbb",
- display: false,
- },
- ]
- end
-
- expect(klass.where(display: true).length).to eq(1)
- end
- end
-
- describe "#pretty_print" do
- it "prints the records" do
- out = StringIO.new
- PP.pp(subject, out)
-
- expect(out.string.scan(/\bid\b/).length).to eq(2)
- expect(out.string).to match(/\bCanada\b/)
- expect(out.string).to match(/\bUS\b/)
- expect(out.string).to_not match(/ActiveHash::Relation/)
- end
- end
-end
diff --git a/spec/active_json/base_spec.rb b/spec/active_json/base_spec.rb
deleted file mode 100644
index 26d991b4..00000000
--- a/spec/active_json/base_spec.rb
+++ /dev/null
@@ -1,180 +0,0 @@
-require 'spec_helper'
-
-describe ActiveJSON::Base do
-
- before do
- ActiveJSON::Base.set_root_path File.expand_path(File.dirname(__FILE__) + "/../fixtures")
-
- class ArrayRow < ActiveJSON::Base; end
- class City < ActiveJSON::Base; end
- class State < ActiveJSON::Base; end
- class Empty < ActiveJSON::Base ; end # Empty JSON
- end
-
- after do
- Object.send :remove_const, :ArrayRow
- Object.send :remove_const, :City
- Object.send :remove_const, :State
- Object.send :remove_const, :Empty
- end
-
- describe ".load_path" do
- it 'can execute embedded ruby' do
- expect(State.first.name).to match(/^New York/)
- end
-
- it 'can load empty yaml' do
- expect(Empty.first).to be_nil
- end
- end
-
- describe ".all" do
- context "before the file is loaded" do
- it "reads from the file" do
- expect(State.all).not_to be_empty
- expect(State.count).to be > 0
- end
- end
- end
-
- describe ".where" do
- context "before the file is loaded" do
- it "reads from the file and filters by where statement" do
- expect(State.where(:name => 'Oregon')).not_to be_empty
- expect(State.count).to be > 0
- end
- end
- end
-
- describe ".delete_all" do
- context "when called before .all" do
- it "causes all to not load data" do
- State.delete_all
- expect(State.all).to be_empty
- end
- end
-
- context "when called after .all" do
- it "clears out the data" do
- expect(State.all).not_to be_empty
- State.delete_all
- expect(State.all).to be_empty
- end
- end
- end
-
- describe ".raw_data" do
- it "returns the raw array data loaded from yaml array-formatted files" do
- expect(ArrayRow.raw_data).to be_kind_of(Array)
- end
- end
-
- describe ".load_file" do
- describe "with array data" do
- it "returns an array of hashes" do
- expect(ArrayRow.load_file).to be_kind_of(Array)
- expect(ArrayRow.load_file).to include({"name" => "Row 1", "id" => 1})
- end
- end
-
- describe "with hash data" do
- it "returns an array of hashes" do
- expect(City.load_file).to be_kind_of(Array)
- expect(City.load_file).to include({"state" => "New York", "name" => "Albany", "id" => 1})
- City.reload
- expect(City.all).to include(City.new(:id => 1))
- end
- end
- end
-
- describe 'ID finders without reliance on a call to all, even with fields specified' do
-
- before do
- class City < ActiveJSON::Base
- fields :id, :state, :name
- end
- end
-
- it 'returns a single city based on #find' do
- expect(City.find(1).name).to eq('Albany')
- end
-
- it 'returns a single city based on #find with a block' do
- expect(City.find { |c| c.id == 1 }.name).to eq('Albany')
- end
-
- it 'returns a single city based on find_by_id' do
- expect(City.find_by_id(1).name).to eq('Albany')
- end
-
- end
-
- describe 'meta programmed finders and properties for fields that exist in the JSON file' do
-
- it 'should have a finder method for each property' do
- expect(City.find_by_state('Oregon')).not_to be_nil
- end
-
- it 'should have a find all method for each property' do
- expect(City.find_all_by_state('Oregon')).not_to be_nil
- end
-
- end
-
- describe "multiple files" do
- context "given array files" do
- before do
- class Country < ActiveJSON::Base
- use_multiple_files
- set_filenames 'countries', 'commonwealths'
- end
- end
- after { Object.send :remove_const, :Country }
-
- it "loads data from both files" do
- # countries.yml
- expect(Country.find_by_name("Canada")).not_to be_nil
-
- # commonwealths.yml
- expect(Country.find_by_name("Puerto Rico")).not_to be_nil
- end
- end
-
- context "given hash files" do
- before do
- class MultiState < ActiveJSON::Base
- use_multiple_files
- set_filenames 'states', 'provinces'
- end
- end
-
- after do
- Object.send(:remove_const, :MultiState)
- end
-
- it "loads data from both files" do
- # states.yml
- expect(MultiState.find_by_name("Oregon")).not_to be_nil
-
- # provinces.yml
- expect(MultiState.find_by_name("British Colombia")).not_to be_nil
- end
- end
-
- context "given a hash and an array file" do
- before do
- class Municipality < ActiveJSON::Base
- use_multiple_files
- set_filenames 'states', 'countries'
- end
- end
- after { Object.send(:remove_const, :Municipality) }
-
- it "raises an exception" do
- expect do
- Municipality.find_by_name("Oregon")
- end.to raise_error(ActiveHash::FileTypeMismatchError)
- end
- end
- end
-end
diff --git a/spec/active_yaml/aliases_spec.rb b/spec/active_yaml/aliases_spec.rb
deleted file mode 100644
index 9adf9fa2..00000000
--- a/spec/active_yaml/aliases_spec.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-require 'spec_helper'
-
-describe ActiveYaml::Aliases do
-
- before do
- ActiveYaml::Base.set_root_path File.expand_path(File.dirname(__FILE__) + "/../fixtures")
-
- class ArrayProduct < ActiveYaml::Base
- include ActiveYaml::Aliases
- end
-
- class KeyProduct < ActiveYaml::Base
- include ActiveYaml::Aliases
- end
- end
-
- after do
- Object.send :remove_const, :ArrayProduct
- Object.send :remove_const, :KeyProduct
- end
-
- context 'using yaml arrays' do
- let(:model) { ArrayProduct }
-
- describe '.all' do
- specify { expect(model.all.length).to eq 4 }
- end
-
- describe 'aliased attributes' do
- subject { model.where(:name => 'Coke').first.attributes }
-
- it('sets strings correctly') { expect(subject[:flavor]).to eq('sweet') }
- it('sets floats correctly') { expect(subject[:price]).to eq(1.0) }
- end
-
- describe 'keys starting with "/"' do
- it 'excludes them from records' do
- models_including_aliases = model.all.select { |p| p.attributes.keys.include? :'/aliases' }
- expect(models_including_aliases).to be_empty
- end
-
- it 'excludes them from fields' do
- model.all
- expect(model.field_names).to match_array [:name, :flavor, :price]
- end
-
- it 'excludes them from column_names' do
- skip "Not supported in Ruby 3.0.0" if RUBY_VERSION < "3.0.0"
- model.all
- expect(model.column_names).to match_array ["name", "flavor", "price"]
- end
- end
- end
-
- context 'with YAML hashes' do
- let(:model) { KeyProduct }
-
- describe '.all' do
- specify { expect(model.all.length).to eq 4 }
- end
-
- describe 'aliased attributes' do
- subject { model.where(:name => 'Coke').first.attributes }
-
- it('sets strings correctly') { expect(subject[:flavor]).to eq('sweet') }
- it('sets floats correctly') { expect(subject[:price]).to eq(1.0) }
- end
-
- describe 'keys starting with "/"' do
- it 'excludes them from records' do
- models_including_aliases = model.all.select { |p| p.attributes.keys.include? :'/aliases' }
- expect(models_including_aliases).to be_empty
- end
-
- it 'excludes them from fields' do
- model.all
- expect(model.field_names).to match_array [:name, :flavor, :price, :slogan, :key]
- end
-
- it 'excludes them from column_names' do
- skip "Not supported in Ruby 3.0.0" if RUBY_VERSION < "3.0.0"
- model.all
- expect(model.column_names).to match_array ["name", "flavor", "price", "slogan", "key"]
- end
- end
- end
-
- describe 'Loading multiple files' do
- let(:model) { MultipleFiles }
- let(:coke) { model.where(:name => 'Coke').first }
- let(:schweppes) { model.where(:name => 'Schweppes').first }
-
- before do
- class MultipleFiles < ActiveYaml::Base
- include ActiveYaml::Aliases
- use_multiple_files
- set_filenames 'array_products', 'array_products_2'
- end
- end
-
- after do
- Object.send :remove_const, :MultipleFiles
- end
-
- it 'returns correct data from both files' do
- expect(coke.flavor).to eq 'sweet'
- expect(schweppes.flavor).to eq 'bitter'
- end
- end
-end
diff --git a/spec/active_yaml/base_spec.rb b/spec/active_yaml/base_spec.rb
deleted file mode 100644
index 281b6fd8..00000000
--- a/spec/active_yaml/base_spec.rb
+++ /dev/null
@@ -1,226 +0,0 @@
-require 'spec_helper'
-
-describe ActiveYaml::Base do
-
- before do
- ActiveYaml::Base.set_root_path File.expand_path(File.dirname(__FILE__) + "/../fixtures")
-
- ENV['USER_PASSWORD'] = 'secret'
-
- class ArrayRow < ActiveYaml::Base ; end
- class City < ActiveYaml::Base ; end
- class State < ActiveYaml::Base ; end
- class ArrayProduct < ActiveYaml::Base ; end # Contain YAML aliases
- class KeyProduct < ActiveYaml::Base ; end # Contain YAML aliases
- class User < ActiveYaml::Base ; end # Contain ERB (embedded ruby)
- class Empty < ActiveYaml::Base ; end # Empty YAML
- end
-
- after do
- Object.send :remove_const, :ArrayRow
- Object.send :remove_const, :City
- Object.send :remove_const, :State
- Object.send :remove_const, :User
- Object.send :remove_const, :Empty
- end
-
- describe ".load_path" do
- context 'default' do
- it 'can execute embedded ruby' do
- expect(User.first.email).to match /^user[0-9]*@email.com$/
- expect(User.first.password).to eq 'secret'
- end
- end
-
- context 'erb disabled' do
- before { ActiveYaml::Base.process_erb = false }
- after { ActiveYaml::Base.process_erb = true }
-
- it 'can execute embedded ruby' do
- expect(User.first.email).to eq '<%= "user#{rand(100)}@email.com" %>'
- expect(User.first.password).to eq "<%= ENV['USER_PASSWORD'] %>"
- end
- end
-
- it 'can load empty yaml' do
- expect(Empty.first).to be_nil
- end
-
- it 'is thread-safe' do
- (1..5).map do
- Thread.new { expect(City.count).to eq(3) }
- end.each(&:join)
- end
- end
-
- describe ".all" do
- context "before the file is loaded" do
- it "reads from the file" do
- expect(State.all).not_to be_empty
- expect(State.count).to be > 0
- end
- end
- end
-
- describe ".where" do
- context "before the file is loaded" do
- it "reads from the file and filters by where statement" do
- expect(State.where(:name => 'Oregon')).not_to be_empty
- expect(State.count).to be > 0
- end
- end
- end
-
- describe ".delete_all" do
- context "when called before .all" do
- it "causes all to not load data" do
- State.delete_all
- expect(State.all).to be_empty
- end
- end
-
- context "when called after .all" do
- it "clears out the data" do
- expect(State.all).not_to be_empty
- State.delete_all
- expect(State.all).to be_empty
- end
- end
- end
-
- describe ".raw_data" do
-
- it "returns the raw hash data loaded from yaml hash-formatted files" do
- expect(City.raw_data).to be_kind_of(Hash)
- expect(City.raw_data.keys).to include("albany", "portland")
- end
-
- it "returns the raw array data loaded from yaml array-formatted files" do
- expect(ArrayRow.raw_data).to be_kind_of(Array)
- end
-
- end
-
- describe ".load_file" do
-
- describe "with array data" do
- it "returns an array of hashes" do
- expect(ArrayRow.load_file).to be_kind_of(Array)
- expect(ArrayRow.load_file).to include({"name" => "Row 1", "id" => 1})
- end
- end
-
- describe "with hash data" do
- it "returns an array of hashes" do
- expect(City.load_file).to be_kind_of(Array)
- expect(City.load_file).to include({"state" => :new_york, "name" => "Albany", "id" => 1, "key" => "albany"})
- City.reload
- expect(City.all).to include(City.new(:id => 1))
- end
-
- it "automatically adds the key attribute" do
- expect(City.load_file.first.keys).to include("key")
- end
-
- it "doesn't overwrite the key attribute when specifically listed in the yml file" do
- expect(City.load_file.last["key"]).to eql("livable")
- end
-
- it "uses the root key of the hash if no key attribute is specified" do
- expect(City.load_file.first["key"]).to eql("albany")
- end
- end
-
- end
-
- describe 'ID finders without reliance on a call to all, even with fields specified' do
-
- before do
- class City < ActiveYaml::Base
- fields :id, :state, :name
- end
- end
-
- it 'returns a single city based on #find' do
- expect(City.find(1).name).to eq('Albany')
- end
-
- it 'returns a single city based on #find with a block' do
- expect(City.find { |c| c.id == 1 }.name).to eq('Albany')
- end
-
- it 'returns a single city based on find_by_id' do
- expect(City.find_by_id(1).name).to eq('Albany')
- end
-
- end
-
- describe 'meta programmed finders and properties for fields that exist in the YAML' do
-
- it 'should have a finder method for each property' do
- expect(City.find_by_state('Oregon')).not_to be_nil
- end
-
- it 'should have a find all method for each property' do
- expect(City.find_all_by_state('Oregon')).not_to be_nil
- end
-
- end
-
- describe "multiple files" do
- context "given array files" do
- before do
- class Country < ActiveYaml::Base
- use_multiple_files
- set_filenames 'countries', 'commonwealths'
- end
- end
- after { Object.send :remove_const, :Country }
-
- it "loads data from both files" do
- # countries.yml
- expect(Country.find_by_name("Canada")).not_to be_nil
-
- # commonwealths.yml
- expect(Country.find_by_name("Puerto Rico")).not_to be_nil
- end
- end
-
- context "given hash files" do
- before do
- class MultiState < ActiveYaml::Base
- use_multiple_files
- set_filenames 'states', 'provinces'
- end
- end
-
- after do
- Object.send(:remove_const, :MultiState)
- end
-
- it "loads data from both files" do
- # states.yml
- expect(MultiState.find_by_name("Oregon")).not_to be_nil
-
- # provinces.yml
- expect(MultiState.find_by_name("British Colombia")).not_to be_nil
- end
- end
-
- context "given a hash and an array file" do
- before do
- class Municipality < ActiveYaml::Base
- use_multiple_files
- set_filenames 'states', 'countries'
- end
- end
- after { Object.send :remove_const, :Municipality }
-
- it "raises an exception" do
- expect do
- Municipality.find_by_name("Oregon")
- end.to raise_error(ActiveHash::FileTypeMismatchError)
- end
- end
- end
-end
diff --git a/spec/associations/active_record_extensions_spec.rb b/spec/associations/active_record_extensions_spec.rb
deleted file mode 100644
index 50b6fb9f..00000000
--- a/spec/associations/active_record_extensions_spec.rb
+++ /dev/null
@@ -1,485 +0,0 @@
-require 'spec_helper'
-
-unless SKIP_ACTIVE_RECORD
- describe ActiveHash::Base, "active record extensions" do
-
- def define_ephemeral_class(name, superclass, &block)
- klass = Class.new(superclass)
- Object.const_set(name, klass)
- klass.class_eval(&block) if block_given?
- @ephemeral_classes << name
- end
-
- def define_book_classes
- define_ephemeral_class(:Author, ActiveHash::Base) do
- include ActiveHash::Associations
- end
-
- define_ephemeral_class(:Book, ActiveRecord::Base) do
- establish_connection :adapter => "sqlite3", :database => ":memory:"
- connection.create_table(:books, :force => true) do |t|
- t.integer :author_id
- t.integer :author_code
- t.boolean :published
- end
-
- if Object.const_defined?(:ActiveModel)
- scope(:published, proc { where(:published => true) })
- else
- named_scope :published, {:conditions => {:published => true}}
- end
- end
- end
-
- def define_person_classes
- define_ephemeral_class(:Country, ActiveHash::Base) do
- self.data = [
- {:id => 1, :name => "Japan"}
- ]
- end
-
- define_ephemeral_class(:Person, ActiveRecord::Base) do
- establish_connection :adapter => "sqlite3", :database => ":memory:"
- connection.create_table(:people, :force => true) do |t|
- end
-
- extend ActiveHash::Associations::ActiveRecordExtensions
- end
-
- define_ephemeral_class(:Post, ActiveRecord::Base) do
- establish_connection :adapter => "sqlite3", :database => ":memory:"
- connection.create_table(:posts, :force => true) do |t|
- t.integer :person_id
- t.datetime :created_at
- end
-
- belongs_to :person
- end
- end
-
- def define_school_classes
- define_ephemeral_class(:Country, ActiveRecord::Base) do
- establish_connection :adapter => "sqlite3", :database => ":memory:"
- connection.create_table(:countries, :force => true) do |t|
- t.string :name
- end
- extend ActiveHash::Associations::ActiveRecordExtensions
- end
-
- define_ephemeral_class(:School, ActiveRecord::Base) do
- establish_connection :adapter => "sqlite3", :database => ":memory:"
- connection.create_table(:schools, :force => true) do |t|
- t.integer :country_id
- t.string :locateable_type
- t.integer :locateable_id
- t.integer :city_id
- end
-
- extend ActiveHash::Associations::ActiveRecordExtensions
- end
-
- define_ephemeral_class(:City, ActiveHash::Base) do
- include ActiveHash::Associations
- end
-
- define_ephemeral_class(:SchoolStatus, ActiveHash::Base)
- end
-
- def define_doctor_classes
- define_ephemeral_class(:Physician, ActiveHash::Base) do
- include ActiveHash::Associations
-
- has_many :appointments
- has_many :patients, through: :appointments
-
- self.data = [
- {:id => 1, :name => "ikeda"},
- {:id => 2, :name => "sato"}
- ]
- end
-
- define_ephemeral_class(:Appointment, ActiveRecord::Base) do
- establish_connection :adapter => "sqlite3", :database => ":memory:"
- connection.create_table :appointments, force: true do |t|
- t.references :physician
- t.references :patient
- end
-
- extend ActiveHash::Associations::ActiveRecordExtensions
-
- belongs_to :physician
- belongs_to :patient
- end
-
- define_ephemeral_class(:Patient, ActiveRecord::Base) do
- establish_connection :adapter => "sqlite3", :database => ":memory:"
- connection.create_table :patients, force: true do |t|
- end
-
- extend ActiveHash::Associations::ActiveRecordExtensions
-
- has_many :appointments
- has_many :physicians, through: :appointments
- end
-
- end
-
- before do
- @ephemeral_classes = []
- end
-
- after do
- @ephemeral_classes.each do |klass_name|
- Object.send :remove_const, klass_name
- end
- end
-
- describe "#has_many" do
- context "with ActiveRecord children" do
- before { define_book_classes }
-
- context "with default options" do
- before do
- @book_1 = Book.create! :author_id => 1, :published => true
- @book_2 = Book.create! :author_id => 1, :published => false
- @book_3 = Book.create! :author_id => 2, :published => true
- Author.has_many :books
- end
-
- it "find the correct records" do
- author = Author.create :id => 1
- expect(author.books).to eq([@book_1, @book_2])
- end
-
- it "should find the correct record ids" do
- author = Author.create :id => 1
- expect(author.book_ids).to eq([@book_1.id, @book_2.id])
- end
-
- it "return a scope so that we can apply further scopes" do
- author = Author.create :id => 1
- expect(author.books.published).to eq([@book_1])
- end
- end
-
- context "with a primary_key option" do
- before do
- @book_1 = Book.create! :author_id => 1, :published => true
- @book_2 = Book.create! :author_id => 2, :published => false
- @book_3 = Book.create! :author_id => 2, :published => true
- Author.field :book_identifier
- Author.has_many :books, :primary_key => :book_identifier
- end
-
- it "should find the correct records" do
- author = Author.create :id => 1, :book_identifier => 2
- expect(author.books).to eq([@book_2, @book_3])
- end
-
- it "should find the correct record ids" do
- author = Author.create :id => 1, :book_identifier => 2
- expect(author.book_ids).to eq([@book_2.id, @book_3.id])
- end
-
- it "return a scope so that we can apply further scopes" do
- author = Author.create :id => 1, :book_identifier => 2
- expect(author.books.published).to eq([@book_3])
- end
- end
-
- context "with a foreign_key option" do
- before do
- @book_1 = Book.create! :author_code => 1, :published => true
- @book_2 = Book.create! :author_code => 1, :published => false
- @book_3 = Book.create! :author_code => 2, :published => true
- Author.has_many :books, :foreign_key => :author_code
- end
-
- it "should find the correct records" do
- author = Author.create :id => 1
- expect(author.books).to eq([@book_1, @book_2])
- end
-
- it "should find the correct record ids" do
- author = Author.create :id => 1
- expect(author.book_ids).to eq([@book_1.id, @book_2.id])
- end
-
- it "return a scope so that we can apply further scopes" do
- author = Author.create :id => 1
- expect(author.books.published).to eq([@book_1])
- end
- end
-
- it "only uses 1 query" do
- Author.has_many :books
- author = Author.create :id => 1
- expect(Book).to receive(:where).with(author_id: 1).once.and_call_original
- author.books.to_a
- end
- end
-
- describe ":through" do
- before { define_doctor_classes }
-
- it "finds ActiveHash records through the join model" do
- patient = Patient.create!
-
- physician1 = Physician.first
- Appointment.create!(physician: physician1, patient: patient)
- Appointment.create!(physician: physician1, patient: patient)
-
- physician2 = Physician.last
- Appointment.create!(physician: physician2, patient: patient)
-
- expect(patient.physicians).to contain_exactly(physician1, physician2)
- end
-
- describe "with the :source option" do
- before do
- # NOTE: Removing the Patient#physicians association and adding Patient#doctors
- Patient._reflections.delete('physicians')
- Patient.class_eval do
- define_method(:physicians) { raise NoMethodError, "The #physicians association is removed in this spec, use #doctors" }
- define_method(:physicians=) { |_| raise NoMethodError, "The #physicians association is removed in this spec, use #doctors" }
- end
- Patient.has_many :doctors, through: :appointments, source: :physician
- end
-
- it "finds ActiveHash records through the join model" do
- patient = Patient.create!
-
- physician = Physician.last
- Appointment.create!(physician: physician, patient: patient)
-
- expect(patient.doctors).to contain_exactly(physician)
- end
- end
-
- describe ":through when the join model uses an aliased association" do
- before do
- # NOTE: Removing the Appointment#physician association and adding Appointment#doctor
- Appointment._reflections.delete('physician')
- Appointment.class_eval do
- define_method(:physician) { raise NoMethodError, "The #physician association is removed in this spec, use #doctor" }
- define_method(:physician=) { |_| raise NoMethodError, "The #physician association is removed in this spec, use #doctor" }
- end
- Appointment.belongs_to :doctor, class_name: 'Physician', foreign_key: :physician_id
-
- # NOTE: Removing the Patient#physicians association and adding Patient#doctors
- Patient._reflections.delete('physicians')
- Patient.class_eval do
- define_method(:physicians) { raise NoMethodError, "The #physicians association is removed in this spec, use #doctors" }
- define_method(:physicians=) { |_| raise NoMethodError, "The #physicians association is removed in this spec, use #doctors" }
- end
- Patient.has_many :doctors, through: :appointments
- end
-
- it "finds ActiveHash records through the join model" do
- patient = Patient.create!
-
- physician = Physician.last
- Appointment.create!(doctor: physician, patient: patient)
-
- expect(patient.doctors).to contain_exactly(physician)
- end
- end
- end
-
- describe "with a lambda" do
- before do
- define_person_classes
- now = Time.now
- @post_1 = Post.create! :person_id => 1, :created_at => now
- @post_2 = Post.create! :person_id => 1, :created_at => 1.day.ago
- Post.create! :person_id => 2, :created_at => now
- Person.has_many :posts, lambda { order(created_at: :asc) }
- end
-
- it "should find the correct records" do
- person = Person.create :id => 1
- expect(person.posts).to eq([@post_2, @post_1])
- end
- end
- end
-
- describe ActiveHash::Associations::ActiveRecordExtensions do
- describe "#belongs_to" do
- before { define_school_classes }
-
- it "doesn't interfere with AR's procs in belongs_to methods" do
- School.belongs_to :country, lambda { where(name: 'Japan') }
- school = School.new
- country = Country.create!(id: 1, name: 'Japan')
- school.country = country
- expect(school.country).to eq(country)
- expect(school.country_id).to eq(country.id)
- expect(school.country).to eq(country)
- school.save!
- school.reload
- expect(school.country_id).to eq(country.id)
- expect(school.country).to eq(country)
-
- country.update!(name: 'JAPAN')
- school.reload
- expect(school.country_id).to eq(country.id)
- expect(school.country).to eq(nil)
- end
-
- it "doesn't interfere with AR's belongs_to arguments" do
- allow(ActiveRecord::Base).to receive(:belongs_to).with(:country, nil)
- allow(ActiveRecord::Base).to receive(:belongs_to).with(:country, nil, {})
-
- School.belongs_to :country
- end
-
- it "doesn't interfere w/ ActiveRecord's polymorphism" do
- School.belongs_to :locateable, :polymorphic => true
- school = School.new
- country = Country.create!
- school.locateable = country
- expect(school.locateable).to eq(country)
- school.save!
- expect(school.reload.locateable_id).to eq(country.id)
- end
-
- it "sets up an ActiveRecord association for non-ActiveHash objects" do
- School.belongs_to :country
- school = School.new
- country = Country.create!
- school.country = country
- expect(school.country).to eq(country)
- expect(school.country_id).to eq(country.id)
- school.save!
- school.reload
- expect(school.reload.country_id).to eq(country.id)
- end
-
- it "calls through to belongs_to_active_hash if it's an ActiveHash object" do
- School.belongs_to :city
- city = City.create
- school = School.create :city_id => city.id
- expect(school.city).to eq(city)
- end
-
- it "doesn't raise any exception when the belongs_to association class can't be autoloaded" do
- # Simulate autoloader
- allow_any_instance_of(String).to receive(:constantize).and_raise(LoadError, "Unable to autoload constant NonExistent")
- expect { School.belongs_to :city, class_name: 'NonExistent' }.not_to raise_error
- end
- end
-
- describe "#belongs_to_active_hash" do
- before { define_school_classes }
-
- context "setting by id" do
- it "finds the correct records" do
- School.belongs_to_active_hash :city
- city = City.create
- school = School.create :city_id => city.id
- expect(school.city).to eq(city)
- end
-
- it "returns nil when the record does not exist" do
- School.belongs_to_active_hash :city
- school = School.create! :city_id => nil
- expect(school.city).to be_nil
- end
- end
-
- context "setting by association" do
- it "finds the correct records" do
- School.belongs_to_active_hash :city
- city = City.create
- school = School.create :city => city
- expect(school.city).to eq(city)
- end
-
- it "is assignable by name attribute" do
- School.belongs_to_active_hash :city, :shortcuts => [:name]
- City.data = [{:id => 1, :name => 'gothan'}]
- city = City.find_by_name 'gothan'
- school = School.create :city_name => 'gothan'
- expect(school.city).to eq(city)
- expect(school.city_name).to eq('gothan')
- end
-
- it "have custom shortcut" do
- School.belongs_to_active_hash :city, :shortcuts => :friendly_name
- City.data = [{:id => 1, :friendly_name => 'Gothan City'}]
- city = City.find_by_friendly_name 'Gothan City'
- school = School.create :city_friendly_name => 'Gothan City'
- expect(school.city).to eq(city)
- expect(school.city_friendly_name).to eq('Gothan City')
- end
-
- it "returns nil when the record does not exist" do
- School.belongs_to_active_hash :city
- school = School.create! :city => nil
- expect(school.city).to be_nil
- end
- end
-
- it "finds active record metadata for this association" do
- School.belongs_to_active_hash :city
- association = School.reflect_on_association(:city)
- expect(association).not_to be_nil
- expect(association.klass.name).to eq(City.name)
- end
-
- it "handles classes ending with an 's'" do
- School.belongs_to_active_hash :school_status
- association = School.reflect_on_association(:school_status)
- expect(association).not_to be_nil
- expect(association.klass.name).to eq(SchoolStatus.name)
- end
-
- it "handles custom association names" do
- School.belongs_to_active_hash :status, :class_name => 'SchoolStatus'
- association = School.reflect_on_association(:status)
- expect(association).not_to be_nil
- expect(association.klass.name).to eq(SchoolStatus.name)
- end
- end
- end
-
- describe "#belongs_to" do
- context "with an ActiveRecord parent" do
- before { define_school_classes }
-
- it "find the correct records" do
- City.belongs_to :country
- country = Country.create
- city = City.create :country_id => country.id
- expect(city.country).to eq(country)
- end
-
- it "returns nil when the record does not exist" do
- City.belongs_to :country
- city = City.create :country_id => 123
- expect(city.country).to be_nil
- end
- end
- end
-
- describe "#has_one" do
- context "with ActiveRecord children" do
- before do
- define_book_classes
- Author.has_one :book
- end
-
- it "find the correct records" do
- book = Book.create! :author_id => 1, :published => true
- author = Author.create :id => 1
- expect(author.book).to eq(book)
- end
-
- it "returns nil when there is no record" do
- author = Author.create :id => 1
- expect(author.book).to be_nil
- end
- end
- end
- end
-end
diff --git a/spec/associations/associations_spec.rb b/spec/associations/associations_spec.rb
deleted file mode 100644
index 447162bf..00000000
--- a/spec/associations/associations_spec.rb
+++ /dev/null
@@ -1,230 +0,0 @@
-require 'spec_helper'
-
-describe ActiveHash::Base, "associations" do
-
- before do
- class City < ActiveHash::Base
- include ActiveHash::Associations
- end
-
- class Author < ActiveHash::Base
- include ActiveHash::Associations
- end
-
- class SchoolStatus < ActiveHash::Base
- end
- end
-
- after do
- Object.send :remove_const, :City
- Object.send :remove_const, :Author
- Object.send :remove_const, :SchoolStatus
- end
-
- describe "#has_many" do
-
- context "with ActiveHash children" do
- context "with default options" do
- before do
- Author.field :city_id
- @included_author_1 = Author.create :city_id => 1
- @included_author_2 = Author.create :city_id => 1
- @excluded_author = Author.create :city_id => 2
- end
-
- it "find the correct records" do
- City.has_many :authors
- city = City.create :id => 1
- expect(city.authors).to eq([@included_author_1, @included_author_2])
- end
-
- it "uses the correct class name when passed" do
- City.has_many :writers, :class_name => "Author"
- city = City.create :id => 1
- expect(city.writers).to eq([@included_author_1, @included_author_2])
- end
- end
-
- context "with a primary_key option" do
- before do
- Author.field :city_id
- City.field :author_identifier
- @author_1 = Author.create :city_id => 1
- @author_2 = Author.create :city_id => 10
- @author_3 = Author.create :city_id => 10
- City.has_many :authors, :primary_key => :author_identifier
- end
-
- it "finds the correct records" do
- city = City.create :id => 1, :author_identifier => 10
- expect(city.authors).to eq([@author_2, @author_3])
- end
- end
-
- context "with a foreign_key option" do
- before do
- Author.field :city_id
- Author.field :city_identifier
- @author_1 = Author.create :city_id => 1, :city_identifier => 10
- @author_2 = Author.create :city_id => 10, :city_identifier => 10
- @author_3 = Author.create :city_id => 10, :city_identifier => 5
- City.has_many :authors, :foreign_key => :city_identifier
- end
-
- it "finds the correct records" do
- city = City.create :id => 10
- expect(city.authors).to eq([@author_1, @author_2])
- end
- end
- end
-
- end
-
- describe "#belongs_to" do
-
- context "with an ActiveHash parent" do
- it "find the correct records" do
- Author.belongs_to :city
- city = City.create
- author = Author.create :city_id => city.id
- expect(author.city).to eq(city)
- end
-
- it "returns nil when the record does not exist" do
- Author.belongs_to :city
- author = Author.create :city_id => 123
- expect(author.city).to be_nil
- end
- end
-
- describe "#parent=" do
- before do
- Author.belongs_to :city
- @city = City.create :id => 1
- end
-
- it "sets the underlying id of the parent" do
- author = Author.new
- author.city = @city
- expect(author.city_id).to eq(@city.id)
- end
-
- it "works from hash assignment" do
- author = Author.new :city => @city
- expect(author.city_id).to eq(@city.id)
- expect(author.city).to eq(@city)
- end
-
- it "works with nil" do
- author = Author.new :city => @city
- expect(author.city_id).to eq(@city.id)
- expect(author.city).to eq(@city)
-
- author.city = nil
- expect(author.city_id).to be_nil
- expect(author.city).to be_nil
- end
- end
-
- describe "with a different foreign key" do
- before do
- Author.belongs_to :residence, :class_name => "City", :foreign_key => "city_id"
- @city = City.create :id => 1
- end
-
- it "works" do
- author = Author.new
- author.residence = @city
- expect(author.city_id).to eq(@city.id)
- end
- end
-
- describe "with a different primary key" do
- before do
- City.field :long_identifier
- Author.belongs_to :city, :primary_key => "long_identifier"
- @city = City.create :id => 1, :long_identifier => "123"
- end
-
- it "works" do
- author = Author.new
- author.city = @city
- expect(author.city_id).to eq(@city.long_identifier)
- end
- end
- end
-
- describe "#has_one" do
- context "with ActiveHash children" do
- context "with default options" do
- before do
- Author.field :city_id
- City.has_one :author
- end
-
- it "find the correct records" do
- author = Author.create :city_id => 1
- city = City.create :id => 1
- expect(city.author).to eq(author)
- end
-
- it "returns nil when there are no records" do
- Author.create :city_id => 10
- city = City.create :id => 1
- expect(city.author).to be_nil
- end
- end
-
- context "with a primary_key option" do
- before do
- Author.field :city_id
- City.field :author_identifier
- City.has_one :author, :primary_key => :author_identifier
- end
-
- it "find the correct records" do
- Author.create :city_id => 1
- author = Author.create :city_id => 10
- city = City.create :id => 1, :author_identifier => 10
- expect(city.author).to eq(author)
- end
-
- it "returns nil when there are no records" do
- Author.create :city_id => 1
- city = City.create :id => 1, :author_identifier => 10
- expect(city.author).to be_nil
- end
- end
-
- context "with a foreign_key option" do
- before do
- Author.field :city_id
- Author.field :city_identifier
- City.has_one :author, :foreign_key => :city_identifier
- end
-
- it "find the correct records" do
- Author.create :city_id => 1, :city_identifier => 1
- author = Author.create :city_id => 1, :city_identifier => 10
- Author.create :city_id => 10, :city_identifier => 5
- city = City.create :id => 10
- expect(city.author).to eq(author)
- end
-
- it "returns nil when there are no records" do
- Author.create :city_id => 1, :city_identifier => 1
- Author.create :city_id => 10, :city_identifier => 5
- city = City.create :id => 10
- expect(city.author).to be_nil
- end
- end
- end
- end
-
- describe "#marked_for_destruction?" do
- it "should return false" do
- expect(City.new.marked_for_destruction?).to eq(false)
- end
- end
-
-end
diff --git a/spec/enum/enum_spec.rb b/spec/enum/enum_spec.rb
deleted file mode 100644
index 8f089d88..00000000
--- a/spec/enum/enum_spec.rb
+++ /dev/null
@@ -1,182 +0,0 @@
-require 'spec_helper'
-
-describe ActiveHash::Base, "enum" do
-
- before do
- ActiveYaml::Base.set_root_path File.expand_path(File.dirname(__FILE__) + "/../fixtures")
-
- class Borough < ActiveYaml::Base
- include ActiveHash::Enum
- fields :name, :county, :population
- enum_accessor :name
- end
-
- class Neighborhood < ActiveHash::Base
- include ActiveHash::Enum
- fields :name, :county
- enum_accessor :name, :county
-
- self.data = [
- {:name => "Queen Ann", :county => "King"}
- ]
- end
- end
-
- after do
- Object.send(:remove_const, :Borough)
- Object.send(:remove_const, :Neighborhood)
- end
-
- describe "#enum_accessor" do
- it "can use a custom method" do
- expect(Borough::BROOKLYN).to eq(Borough.find_by_name("Brooklyn"))
- end
-
- it "sets the field used for accessing records by constants" do
- expect(Neighborhood::QUEEN_ANN_KING).to eq(Neighborhood.find_by_name("Queen Ann"))
- end
-
- it "ensures that values stored in the field specified are unique" do
- expect do
- Class.new(ActiveHash::Base) do
- include ActiveHash::Enum
- self.data = [
- {:name => 'Woodford Reserve'},
- {:name => 'Bulliet Bourbon'},
- {:name => 'Woodford Reserve'}
- ]
- enum_accessor :name
- end
- end.to raise_error(ActiveHash::Enum::DuplicateEnumAccessor)
- end
-
- it "can use enum accessor constant with same name as top-level constant" do
- expect do
- Class.new(ActiveHash::Base) do
- include ActiveHash::Enum
- self.data = [
- {:type => 'JSON'},
- {:type => 'YAML'},
- {:type => 'XML'}
- ]
- enum_accessor :type
- end
- end.not_to raise_error
- end
-
- it "removes non-word characters from values before setting constants" do
- Movie = Class.new(ActiveHash::Base) do
- include ActiveHash::Enum
- self.data = [
- {:name => 'Die Hard 2', :rating => '4.3'},
- {:name => 'The Informant!', :rating => '4.3'},
- {:name => 'In & Out', :rating => '4.3'}
- ]
- enum_accessor :name
- end
-
- expect(Movie::DIE_HARD_2.name).to eq('Die Hard 2')
- expect(Movie::THE_INFORMANT.name).to eq('The Informant!')
- expect(Movie::IN_OUT.name).to eq('In & Out')
- end
-
- describe "enum(columns)" do
- it "defines a predicate method for each value in the enum" do
- Article = Class.new(ActiveHash::Base) do
- include ActiveHash::Enum
-
- self.data = [
- { name: 'Article 1', status: 'draft'},
- { name: 'Article 2', status: 'published'},
- { name: 'Article 3', status: 'archived'}
- ]
-
- enum_accessor :name
-
- enum status: [:draft, :published, :archived]
- end
-
- expect(Article::ARTICLE_1.draft?).to be_truthy
- expect(Article::ARTICLE_1.published?).to be_falsey
- expect(Article::ARTICLE_1.archived?).to be_falsey
-
- expect(Article::ARTICLE_2.draft?).to be_falsey
- expect(Article::ARTICLE_2.published?).to be_truthy
- expect(Article::ARTICLE_2.archived?).to be_falsey
-
- expect(Article::ARTICLE_3.draft?).to be_falsey
- expect(Article::ARTICLE_3.published?).to be_falsey
- expect(Article::ARTICLE_3.archived?).to be_truthy
- end
-
- it "multi type data (ex: string, integer and symbol) enum" do
- NotifyType = Class.new(ActiveHash::Base) do
- include ActiveHash::Enum
-
- self.data = [
- { name: 'Like', action: 'LIKE'},
- { name: 'Comment', action: 1},
- { name: 'Follow', action: :FOLLOW},
- { name: 'Mention', action: 'MENTION'}
- ]
-
- enum_accessor :name
-
- enum action: { like: 'LIKE', comment: 1, follow: :FOLLOW, mention: 'MENTION' }
- end
-
- expect(NotifyType::LIKE.like?).to be_truthy
- expect(NotifyType::LIKE.comment?).to be_falsey
- expect(NotifyType::LIKE.follow?).to be_falsey
- expect(NotifyType::LIKE.mention?).to be_falsey
-
- expect(NotifyType::COMMENT.like?).to be_falsey
- expect(NotifyType::COMMENT.comment?).to be_truthy
- expect(NotifyType::COMMENT.follow?).to be_falsey
- expect(NotifyType::COMMENT.mention?).to be_falsey
-
- expect(NotifyType::FOLLOW.like?).to be_falsey
- expect(NotifyType::FOLLOW.comment?).to be_falsey
- expect(NotifyType::FOLLOW.follow?).to be_truthy
- expect(NotifyType::FOLLOW.mention?).to be_falsey
-
- expect(NotifyType::MENTION.like?).to be_falsey
- expect(NotifyType::MENTION.comment?).to be_falsey
- expect(NotifyType::MENTION.follow?).to be_falsey
- expect(NotifyType::MENTION.mention?).to be_truthy
- end
- end
- end
-
- context "ActiveHash with an enum_accessor set" do
- describe "#save" do
- it "resets the constant's value to the updated record" do
- expect(Borough::BROOKLYN.population).to eq(2556598)
- brooklyn = Borough.find_by_name("Brooklyn")
- brooklyn.population = 2556600
- expect(brooklyn.save).to be_truthy
- expect(Borough::BROOKLYN.population).to eq(2556600)
- end
- end
-
- describe ".create" do
- it "creates constants for new records" do
- bronx = Borough.create!(:name => "Bronx")
- expect(Borough::BRONX).to eq(bronx)
- end
-
- it "doesn't create constants for records missing the enum accessor field" do
- expect(Borough.create(:name => "")).to be_truthy
- expect(Borough.create(:population => 12)).to be_truthy
- end
- end
-
- describe ".delete_all" do
- it "unsets all constants for deleted records" do
- expect(Borough.const_defined?("STATEN_ISLAND")).to be_truthy
- expect(Borough.delete_all).to be_truthy
- expect(Borough.const_defined?("STATEN_ISLAND")).to be_falsey
- end
- end
- end
-end
diff --git a/spec/fixtures/array_products.yml b/spec/fixtures/array_products.yml
deleted file mode 100644
index 73a0d953..00000000
--- a/spec/fixtures/array_products.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-- /aliases:
- soda_flavor: &soda_flavor
- sweet
- soda_price: &soda_price
- 1.0
- chip_flavor: &chip_flavor
- salty
- chip_price: &chip_price
- 1.5
-
-- id: 1
- name: Coke
- flavor: *soda_flavor
- price: *soda_price
-
-- id: 2
- name: Pepsi
- flavor: *soda_flavor
- price: *soda_price
-
-- id: 3
- name: Pringles
- flavor: *chip_flavor
- price: *chip_price
-
-- id: 4
- name: ETA
- flavor: *chip_flavor
- price: *chip_price
\ No newline at end of file
diff --git a/spec/fixtures/array_products_2.yml b/spec/fixtures/array_products_2.yml
deleted file mode 100644
index 0ded648f..00000000
--- a/spec/fixtures/array_products_2.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-- /aliases:
- soda_flavor: &soda_flavor
- bitter
- soda_price: &soda_price
- 1.5
-
-- id: 5
- name: Schweppes
- flavor: *soda_flavor
- price: *soda_price
diff --git a/spec/fixtures/array_rows.json b/spec/fixtures/array_rows.json
deleted file mode 100644
index 00e74719..00000000
--- a/spec/fixtures/array_rows.json
+++ /dev/null
@@ -1,6 +0,0 @@
-[
- {"id": 1,
- "name": "Row 1"},
- {"id": 2,
- "name": "Row 2"}
-]
diff --git a/spec/fixtures/array_rows.yml b/spec/fixtures/array_rows.yml
deleted file mode 100644
index 3b996cb9..00000000
--- a/spec/fixtures/array_rows.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-- id: 1
- name: Row 1
-- id: 2
- name: Row 2
diff --git a/spec/fixtures/boroughs.yml b/spec/fixtures/boroughs.yml
deleted file mode 100644
index 78d1193e..00000000
--- a/spec/fixtures/boroughs.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-- id: 1
- name: Manhattan
- county: New York
- population: 1634795
-- id: 2
- name: Brooklyn
- county: Kings
- population: 2556598
-- id: 3
- name: Queens
- county: Queens
- population: 2293007
-- id: 4
- name: Staten Island
- county: Richmond
- population: 487407
\ No newline at end of file
diff --git a/spec/fixtures/cities.json b/spec/fixtures/cities.json
deleted file mode 100644
index bad4b4e2..00000000
--- a/spec/fixtures/cities.json
+++ /dev/null
@@ -1,8 +0,0 @@
-[
- {"id": 1,
- "state": "New York",
- "name": "Albany"},
- {"id": 2,
- "state": "Oregon",
- "name": "Portland"}
-]
diff --git a/spec/fixtures/cities.yml b/spec/fixtures/cities.yml
deleted file mode 100644
index 12ac6a34..00000000
--- a/spec/fixtures/cities.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-albany:
- id: 1
- state: :new_york
- name: Albany
-portland:
- id: 2
- state: Oregon
- name: Portland
-melbourne:
- id: 3
- state: Vic
- name: Melbourne
- key: livable
diff --git a/spec/fixtures/commonwealths.json b/spec/fixtures/commonwealths.json
deleted file mode 100644
index b6e9cac1..00000000
--- a/spec/fixtures/commonwealths.json
+++ /dev/null
@@ -1,4 +0,0 @@
-[
- {"name": "Puerto Rico"},
- {"name": "Phillipines"}
-]
diff --git a/spec/fixtures/commonwealths.yml b/spec/fixtures/commonwealths.yml
deleted file mode 100644
index 618ba275..00000000
--- a/spec/fixtures/commonwealths.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-- name: Puerto Rico
- independence_date:
- created_at: Wed Jul 22 22:41:44 -0400 2009
-- name: Phillipines
- independence_date: 1946-07-04
- created_at: Wed Jul 22 22:41:44 -0400 2009
diff --git a/spec/fixtures/countries.json b/spec/fixtures/countries.json
deleted file mode 100644
index 591a60af..00000000
--- a/spec/fixtures/countries.json
+++ /dev/null
@@ -1,17 +0,0 @@
-[
- {
- "id": 1,
- "name": "US",
- "custom_field_1": "value1"
- },
- {
- "id": 2,
- "name": "Canada",
- "custom_field_2": "value2"
- },
- {
- "id": 3,
- "name": "Mexico",
- "custom_field_3": "value3"
- }
-]
diff --git a/spec/fixtures/countries.yml b/spec/fixtures/countries.yml
deleted file mode 100644
index c7812973..00000000
--- a/spec/fixtures/countries.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-- id: 1
- name: US
- independence_date: 1776-07-04
- created_at: Wed Jul 22 22:41:44 -0400 2009
- custom_field_1: value1
-- id: 2
- name: Canada
- independence_date: 1867-07-01
- created_at: Wed Jul 22 22:41:44 -0400 2009
- custom_field_2: value2
-- id: 3
- name: Mexico
- independence_date: 1810-09-16
- created_at: Wed Jul 22 22:41:44 -0400 2009
- custom_field_3: value3
diff --git a/spec/fixtures/empties.json b/spec/fixtures/empties.json
deleted file mode 100644
index e69de29b..00000000
diff --git a/spec/fixtures/empties.yml b/spec/fixtures/empties.yml
deleted file mode 100644
index e69de29b..00000000
diff --git a/spec/fixtures/key_products.yml b/spec/fixtures/key_products.yml
deleted file mode 100644
index 717226fc..00000000
--- a/spec/fixtures/key_products.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-/aliases:
- soda_flavor: &soda_flavor
- sweet
- soda_price: &soda_price
- 1.0
- chip_flavor: &chip_flavor
- salty
- chip_price: &chip_price
- 1.5
-
-
-coke:
- id: 1
- name: Coke
- flavor: *soda_flavor
- price: *soda_price
-
-pepsi:
- id: 2
- name: Pepsi
- flavor: *soda_flavor
- price: *soda_price
-
-pringles:
- id: 3
- name: Pringles
- flavor: *chip_flavor
- price: *chip_price
- slogan: &inner_alias
-
-eta:
- id: 4
- name: ETA
- flavor: *chip_flavor
- price: *chip_price
diff --git a/spec/fixtures/locales/ja.yml b/spec/fixtures/locales/ja.yml
deleted file mode 100644
index 886d2955..00000000
--- a/spec/fixtures/locales/ja.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-ja:
- activemodel:
- models:
- country: "国"
diff --git a/spec/fixtures/provinces.json b/spec/fixtures/provinces.json
deleted file mode 100644
index 213fa3f0..00000000
--- a/spec/fixtures/provinces.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "quebec": {
- "id": "3",
- "name": "Quebec" },
- "british_columbia": {
- "id": "4",
- "name": "British Colombia"}
-}
diff --git a/spec/fixtures/provinces.yml b/spec/fixtures/provinces.yml
deleted file mode 100644
index dc1979b0..00000000
--- a/spec/fixtures/provinces.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-quebec:
- id: 3
- name: Quebec
-british_columbia:
- id: 4
- name: British Colombia
diff --git a/spec/fixtures/states.json b/spec/fixtures/states.json
deleted file mode 100644
index 18833183..00000000
--- a/spec/fixtures/states.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "new_york":
- {"id": 1,
- "name": "New York"},
- "oregon":
- {"id": 2,
- "name": "Oregon"}
-}
diff --git a/spec/fixtures/states.yml b/spec/fixtures/states.yml
deleted file mode 100644
index fea015d0..00000000
--- a/spec/fixtures/states.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-new_york:
- id: 1
- name: New York
-oregon:
- id: 2
- name: Oregon
diff --git a/spec/fixtures/users.yml b/spec/fixtures/users.yml
deleted file mode 100644
index 59d4541a..00000000
--- a/spec/fixtures/users.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-- id: 1
- email: <%= "user#{rand(100)}@email.com" %>
- password: <%= ENV['USER_PASSWORD'] %>
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
deleted file mode 100644
index fab76eee..00000000
--- a/spec/spec_helper.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require "bundler/setup"
-require "pry"
-require 'rspec'
-require 'yaml'
-
-SKIP_ACTIVE_RECORD = ENV['SKIP_ACTIVE_RECORD']
-
-$LOAD_PATH.unshift(File.dirname(__FILE__))
-$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
-require 'active_hash'
-require 'active_record' unless SKIP_ACTIVE_RECORD
-
-Dir["spec/support/**/*.rb"].each { |f|
- require File.expand_path(f)
-}
-
-if !SKIP_ACTIVE_RECORD && ActiveRecord::VERSION::MAJOR < 7
- RSpec.configure do |config|
- config.after(:each) do
- # To isolate tests with temporary classes.
- # ref: https://groups.google.com/g/rspec/c/7CQq0ABS3yQ
- ActiveSupport::Dependencies::Reference.clear!
- end
- end
-end
-
-I18n.load_path << File.expand_path("fixtures/locales/ja.yml", __dir__)
diff --git a/styles/globals.css b/styles/globals.css
new file mode 100644
index 00000000..0ed28879
--- /dev/null
+++ b/styles/globals.css
@@ -0,0 +1,92 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer utilities {
+ .text-balance {
+ text-wrap: balance;
+ }
+}
+
+@layer base {
+ :root {
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+ --background: 0 0% 100%;
+ --foreground: 0 0% 3.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 0 0% 3.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 0 0% 3.9%;
+ --primary: 0 0% 9%;
+ --primary-foreground: 0 0% 98%;
+ --secondary: 0 0% 96.1%;
+ --secondary-foreground: 0 0% 9%;
+ --muted: 0 0% 96.1%;
+ --muted-foreground: 0 0% 45.1%;
+ --accent: 0 0% 96.1%;
+ --accent-foreground: 0 0% 9%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 89.8%;
+ --input: 0 0% 89.8%;
+ --ring: 0 0% 3.9%;
+ --chart-1: 12 76% 61%;
+ --chart-2: 173 58% 39%;
+ --chart-3: 197 37% 24%;
+ --chart-4: 43 74% 66%;
+ --chart-5: 27 87% 67%;
+ --radius: 0.5rem;
+ --sidebar-background: 0 0% 98%;
+ --sidebar-foreground: 240 5.3% 26.1%;
+ --sidebar-primary: 240 5.9% 10%;
+ --sidebar-primary-foreground: 0 0% 98%;
+ --sidebar-accent: 240 4.8% 95.9%;
+ --sidebar-accent-foreground: 240 5.9% 10%;
+ --sidebar-border: 220 13% 91%;
+ --sidebar-ring: 217.2 91.2% 59.8%;
+ }
+ .dark {
+ --background: 0 0% 3.9%;
+ --foreground: 0 0% 98%;
+ --card: 0 0% 3.9%;
+ --card-foreground: 0 0% 98%;
+ --popover: 0 0% 3.9%;
+ --popover-foreground: 0 0% 98%;
+ --primary: 0 0% 98%;
+ --primary-foreground: 0 0% 9%;
+ --secondary: 0 0% 14.9%;
+ --secondary-foreground: 0 0% 98%;
+ --muted: 0 0% 14.9%;
+ --muted-foreground: 0 0% 63.9%;
+ --accent: 0 0% 14.9%;
+ --accent-foreground: 0 0% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 14.9%;
+ --input: 0 0% 14.9%;
+ --ring: 0 0% 83.1%;
+ --chart-1: 220 70% 50%;
+ --chart-2: 160 60% 45%;
+ --chart-3: 30 80% 55%;
+ --chart-4: 280 65% 60%;
+ --chart-5: 340 75% 55%;
+ --sidebar-background: 240 5.9% 10%;
+ --sidebar-foreground: 240 4.8% 95.9%;
+ --sidebar-primary: 224.3 76.3% 48%;
+ --sidebar-primary-foreground: 0 0% 100%;
+ --sidebar-accent: 240 3.7% 15.9%;
+ --sidebar-accent-foreground: 240 4.8% 95.9%;
+ --sidebar-border: 240 3.7% 15.9%;
+ --sidebar-ring: 217.2 91.2% 59.8%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
\ No newline at end of file
diff --git a/tailwind.config.ts b/tailwind.config.ts
new file mode 100644
index 00000000..23a1196d
--- /dev/null
+++ b/tailwind.config.ts
@@ -0,0 +1,81 @@
+import type { Config } from "tailwindcss"
+
+const config = {
+ darkMode: ["class"],
+ content: [
+ "./pages/**/*.{ts,tsx}",
+ "./components/**/*.{ts,tsx}",
+ "./app/**/*.{ts,tsx}",
+ "./src/**/*.{ts,tsx}",
+ "*.{js,ts,jsx,tsx,mdx}",
+ ],
+ prefix: "",
+ theme: {
+ container: {
+ center: true,
+ padding: "2rem",
+ screens: {
+ "2xl": "1400px",
+ },
+ },
+ extend: {
+ colors: {
+ border: "hsl(var(--border))",
+ input: "hsl(var(--input))",
+ ring: "hsl(var(--ring))",
+ background: "hsl(var(--background))",
+ foreground: "hsl(var(--foreground))",
+ primary: {
+ DEFAULT: "hsl(var(--primary))",
+ foreground: "hsl(var(--primary-foreground))",
+ },
+ secondary: {
+ DEFAULT: "hsl(var(--secondary))",
+ foreground: "hsl(var(--secondary-foreground))",
+ },
+ destructive: {
+ DEFAULT: "hsl(var(--destructive))",
+ foreground: "hsl(var(--destructive-foreground))",
+ },
+ muted: {
+ DEFAULT: "hsl(var(--muted))",
+ foreground: "hsl(var(--muted-foreground))",
+ },
+ accent: {
+ DEFAULT: "hsl(var(--accent))",
+ foreground: "hsl(var(--accent-foreground))",
+ },
+ popover: {
+ DEFAULT: "hsl(var(--popover))",
+ foreground: "hsl(var(--popover-foreground))",
+ },
+ card: {
+ DEFAULT: "hsl(var(--card))",
+ foreground: "hsl(var(--card-foreground))",
+ },
+ },
+ borderRadius: {
+ lg: "var(--radius)",
+ md: "calc(var(--radius) - 2px)",
+ sm: "calc(var(--radius) - 4px)",
+ },
+ keyframes: {
+ "accordion-down": {
+ from: { height: "0" },
+ to: { height: "var(--radix-accordion-content-height)" },
+ },
+ "accordion-up": {
+ from: { height: "var(--radix-accordion-content-height)" },
+ to: { height: "0" },
+ },
+ },
+ animation: {
+ "accordion-down": "accordion-down 0.2s ease-out",
+ "accordion-up": "accordion-up 0.2s ease-out",
+ },
+ },
+ },
+ plugins: [require("tailwindcss-animate")],
+} satisfies Config
+
+export default config
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..4b2dc7ba
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "target": "ES6",
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}