pax_global_header 0000666 0000000 0000000 00000000064 14655474101 0014521 g ustar 00root root 0000000 0000000 52 comment=8aaaec0707ef193ff6f6a369c77668d16825c91a
camping-3.2.6/ 0000775 0000000 0000000 00000000000 14655474101 0013147 5 ustar 00root root 0000000 0000000 camping-3.2.6/.github/ 0000775 0000000 0000000 00000000000 14655474101 0014507 5 ustar 00root root 0000000 0000000 camping-3.2.6/.github/dependabot.yml 0000664 0000000 0000000 00000000166 14655474101 0017342 0 ustar 00root root 0000000 0000000 version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
camping-3.2.6/.github/pull_request_template.md 0000664 0000000 0000000 00000001600 14655474101 0021445 0 ustar 00root root 0000000 0000000 Get your PR ready!
- [ ] Name your PR something Snazzy.
- [ ] Make certain that your PR passes the tests.
- [ ] If you made any changes to `camping.rb`, or `camping-unabridged.rb` then make sure they are in sync.
- [ ] If this is a Release PR, make sure that you updated the Camping version in `camping.gemspec`, `lib/version.rb`, and the `CHANGELOG`
- [ ] Add a nice description of your changes in the CHANGELOG.
- [ ] Delete any unnecessary commented out code that you thought you might need while working on the thing.
- [ ] Rebase from main: `git checkout main; git pull upstream main, git checkout my_awesome_branch, git rebase main`.
- [ ] Add a Description of your PR here.
- [ ] Add a bulleted list with your changes.
When all that's done, Ask for a review from Karl.
## Example
This is my great PR! I added kittens. lot's of Kittens.
Changes:
* Added Kittens.
* Removed non Kittens.
camping-3.2.6/.github/workflows/ 0000775 0000000 0000000 00000000000 14655474101 0016544 5 ustar 00root root 0000000 0000000 camping-3.2.6/.github/workflows/camping.yml 0000664 0000000 0000000 00000001616 14655474101 0020711 0 ustar 00root root 0000000 0000000 # This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
name: Camping
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ 'ubuntu-latest', 'macos-latest' ] # 'windows-latest'
ruby: ['3.3', '3.2', '3.1'] #, 'ruby-head']
exclude:
- os: windows-latest
ruby: jruby-head
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run tests
run: bundle exec rake
camping-3.2.6/.github/workflows/integration.yml 0000664 0000000 0000000 00000002203 14655474101 0021607 0 ustar 00root root 0000000 0000000 # This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
name: Integration
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ 'ubuntu-latest', 'macos-latest' ] # 'windows-latest'
ruby: ['3.3', '3.2', '3.1'] #, 'ruby-head']
exclude:
- os: windows-latest
ruby: jruby-head
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: false
- name: Build Camping
run: gem build -o camping.gem
- name: Move to test folder
run: cd test/integration
- name: Install all gems
run: bundle install
- name: Remove all gems
run: bundle clean --force
- name: Run tests
run: bundle exec rake
camping-3.2.6/.gitignore 0000664 0000000 0000000 00000000115 14655474101 0015134 0 ustar 00root root 0000000 0000000 pkg
doc
camping-*.gem
node_modules
camping.gem
test/integration/Gemfile.lock
camping-3.2.6/CHANGELOG 0000664 0000000 0000000 00000022724 14655474101 0014370 0 ustar 00root root 0000000 0000000 = 3.2.6
09 Aug, 2024
* Server now loads everything from the kindling directory. To start your fire.
* Move Camping Gear to their own directory to play nicer with Zeitwerk.
* Add some testing for Camping::Server.
= 3.2.5
== 13 June, 2024
* Fix bug where create method is never called.
* Update KDL dependency to 1.0.6.
= 3.2.4
== 10 June, 2024
* Add new integration test.
* Pack the new Firewatch Logging Gear wrapper by default in Camping apps.
= 3.2.3
== 09 June, 2024
* Remove dead code make a derelict file in the generator.
* Correct some Ruby warnings in Camping.rb, CampingTools, and Camping Gear.
* Hopefully correct some Gemfile errors causing tests to fail.
* Add MIT License to camping.gemspec.
* Correct error where Minitest was misspelled in generated test helper.
= 3.2.2
== 09 June, 2024
* Fix wrong version of dependency.
= 3.2.1
== 09 June, 2024
* Start work on integration testing.
* Change some gem requirements in Gemfile and gemspec.
* Add XSendfile to deployment middleware.
= 3.2.0
== 05 June, 2024
* Add default logging middleware to Camping.
* Some Bugfixes for Nancy.
= 3.1.3
=== 24 June, 2023
* Fix a loader error where the loader would assume that the `/apps` directory exists, instead of checking first.
= 3.1.1
=== 23 June, 2023
* Fix an error that caused the loader not to load because we renamed it Loader from Relaoder.
* Add new dependencies to gemspec.
= 3.1.0
=== 22 June, 2023
* Rewrote Camping's Reloader/Loader to work with the new nested /apps directory.
* Reopening and redefining controllers, views, and helpers is possible by placing them in the /apps directory.
* Added Zeitwerk autoloading, watched directories are /apps and /lib.
* Reloader now calls make camp upon each request in development mode.
* Reloader also now listens for changes in the /apps and /lib directory and eager reloads as needed.
* /apps and /lib are now eager loaded.
* Camping new command now adds a test_helper.
* Started work on CampGuide, an exception handler that provides context aware resolutions to common Camping Problems.
= 3.0.2
=== 18th March, 2023
* App names supplied to the camping new command are now CamelCased.
= 3.0.1
=== 18th March, 2023
* Fixed error with Camping new that would generate a faulty camp.rb.
* App names supplied to the camping new command are now capitlized.
= 3.0
=== 18th March, 2023
* Camping Now uses the latest versions of Rack and Rackup under the hood.
* New Routing Spec gives Sinatra like Routing helpers.
* Inherit from other Controllers without carrying their urls.
* You can now inherit from another Controller, AND set custom urls.
* ActiveRecord now removed in favor of a more flexible database architecture.
* Builtin Gear added for Filtering, and Sinatra style routing.
* Added Camping Tools, small utilities that come with Camping.
* Camping now has a new project generator. run `camping new` from your shell.
* _meta data set in an App's options hash.
* Session secrets are now much longer.
* Camping settings can now be set in a kdl config doc.
* Gems are now loaded from your Gemfile upon startup.
* Add Inspection Camping Gear. Utilities for Camp inspection.
* Camping now looks for a `camp.rb` file to start with. Put your apps there.
= 2.3
=== 28th July, 2022
* New routes command line helper command.
* plugin support added via the gear method.
* Restored support for mounting multiple apps.
* Add url_prefix option for mounting apps at different urls.
* Increase camping test limit to 5120 bytes. We needed just a bit more space.
* Add a book stub about Models.
* Add a chapter about middleware and how it works.
* Camping a '/' is now forcibly terminating each route. May revert this back if we run into trouble.
= 2.2
=== Never Released
* Updated ActiveRecord migrations class to reference version 6.1
* Removed deprecated gemfile definitions
* Rename deprecated methods
* Get tests to pass
= 2.1
=== 19th Aug, 2010 (whyday)
* Helpers#R now calls to_param on any object it passes in
* Fix route generation issue with routes including "." (#22)
* Improved tests
* Improved 1.9 support
* Camping::Server is now built upon Rack::Server
* Add support for ERB, Haml etc through Tilt
* Introducing Camping.options and Camping#set
* Camping::Server only loads ActiveRecord when needed
= 2.0
=== 9th Apr, 2010
* Speed-up of Camping::Mab (thanks zimbatm!)
* @state is now an alias of @env['rack.session']
* Camping.use injects a Rack middleware.
* Update Flipbook to RDoc 2.4
* Removed old examples.
* Updated examples/blog.rb
* Camping::Apps returns!
* Session-cookies now timeout naturally (thanks jenna!)
* You can now `throw :halt` to halt the response in a helper.
* Camping::H#u is gone (was an alias to merge!)
* Camping::Session now uses session-cookies. The AR-backend is gone for now.
* camping/db.rb has been renamed to camping/ar.rb.
* Camping now uses Rack internally. Every app responds to #call.
= 1.6
=== Never released
* Camping::Apps removed, it wasn't reliable.
* bin/camping server kinds splitted in various files.
* NotFound and ServerError controllers changed to methods :
r404 : called when a controller was not found
r500 : called on uncaught exception
r501 : called on undefined method
All of those can be overridden at your taste.
* Markaby no longer required. Like AR, is it autoloaded on (Mab) usage.
* Camping::H is now inheriting from Hash instead of HashWithIndifferentAccess.
* Which made possible to remove the last strict dependency : active_support
* #errors_for removed, it wasn't really used
* Bug fixes !
= 1.5
=== 3rd Oct, 2006
* Camping::Apps stores an array of classes for all loaded apps.
* bin/camping can be given a directory. Like: camping examples/
* Console mode -- thank zimbatm. Use: camping -C yourapp.rb
* Call controllers with Camping.method_missing.
Tepee.get(:Index) #=> (Response)
Blog.post(:Delete, id) #=> (Response)
Blog.post(:Login, :input => {'username' => 'admin', 'password' => 'camping'})
#=> #
Blog.get(:Info, :env => {:HTTP_HOST => 'wagon'})
#=> #'wagon'} ...>
* Using \r\n instead of \n on output. FastCGI has these needs.
* ActiveRecord no longer required or installed.
* If you refer to Models::Base, however, ActiveRecord will be loaded with autoload. (see lib/camping/db.rb)
* new Camping::FastCGI.serve which will serve a whole directory of apps
(see http://code.whytheluckystiff.net/camping/wiki/TheCampingServer)
* ~/.campingrc can contain database connection info if you want your default to be something other than SQLite.
database:
adapter: mysql
username: camping
socket: /tmp/mysql.sock
password: NOFORESTFIRES
database: camping
* controllers are now *ordered*. uses the inherited hook to keep track of all
classes created with R. those classes are scanned, in order, when a request is
made. any other controllers are handled first. so if you plan on overriding the
urls method, be sure to subclass from R().
* Console mode will load .irbrc in the working directory, if present.
(for example, in my ~/git/balloon directory, i have this in the .irbrc:
include Balloon::Models
when camping -C balloon.rb gets run, the models all get included in main.)
* And, of course, many other bugfixes from myself and the loyal+kind zimbatm...
* Markaby updated to 0.5. (See its CHANGELOG.)
= 1.4.2
=== 18th May, 2006
* Efficient file uploads for multipart/form-data POSTs.
* Camping tool now uses Mongrel, if available. If not, sticks with WEBrick.
* Multiple apps can be loaded with the camping tool, each mounted according to their file name.
= 1.4.1
=== 3rd May, 2006
* Streaming HTTP support. If body is IO, will simply pass to the controller. Mongrel, in particular, supports this nicely.
= 1.4
=== 11th April, 2006
* Moved Camping::Controllers::Base to Camping::Base.
* Moved Camping::Controllers::R to Camping::R.
* New session library (lib/camping/session.rb).
* WEBrick handler (lib/camping/webrick.rb) and Mongrel handler (lib/camping/mongrel.rb).
* Helpers#URL, builds a complete URL for a route. Returns a URI object. This way relative links could just return self.URL.path.
* Base#initialize takes over some of Base#service's duties.
* ENV now available as @env in controllers and views.
* Beautiful multi-page docs without frames!
= 1.3
=== 28th January, 2006
* bin/camping: an application launcher.
* Camping.run(request, response) now changed to controller = Camping.run(request, env)
* This means outputting the response is the wrapper/server's job. See bin/camping, you can do a controller.to_s at the least.
* Controllers::Base.env is the new thread-safe home for ENV.
* The input hash now works more like Rails params. You can call keys
like methods or with symbols or strings.
* Queries are now parsed more like PHP/Rails, in that you can denote
structure with brackets: post[user]=_why;post[id]=2
* Auto-prefix table names, to help prevent name clash.
* Helpers.errors_for simple validation.
* Lots of empty :href and :action attributes, a bug.
* New single-page flipbook RDoc template.
= 1.2
=== 23rd January, 2006
* Camping.goes allows fresh modules build from all Camping parts.
* File uploads now supported (multipart/form-data).
* Helpers.R can rebuild routes.
* Helpers./ for tracing paths from the root.
= 1.1
=== 19th January, 2006
* Allowed request and response streams to be passed in, to allow WEBrick and FastCGI support.
= 1.0
=== 17th January, 2006
* Initial checkin, see announcement at http://redhanded.hobix.com/bits/campingAMicroframework.html.
camping-3.2.6/COPYING 0000664 0000000 0000000 00000002030 14655474101 0014175 0 ustar 00root root 0000000 0000000 Copyright (c) 2006 why the lucky stiff
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 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.
camping-3.2.6/Gemfile 0000664 0000000 0000000 00000000773 14655474101 0014451 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
source 'https://rubygems.org'
gem 'rake'
gem 'rack'
gem 'rack-session'
gem 'rackup'
gem 'mab'
gem 'kdl', '~> 1.0.6'
gem 'zeitwerk'
gem 'listen', '~> 3.9.0'
gem 'dry-logger', '~> 1.0.4'
gem 'bundler'
gem 'tilt'
group :development do
gem 'parser'
gem 'unparser'
gem 'pp'
end
group :test do
gem 'minitest', '~> 5.0'
gem 'minitest-reporters'
gem 'rack-test'
gem 'ruby_parser'
gem 'minitest-hooks'
gem "minitest-global_expectations"
gem "minitest-sprint"
end
camping-3.2.6/Gemfile.lock 0000664 0000000 0000000 00000003632 14655474101 0015375 0 ustar 00root root 0000000 0000000 GEM
remote: https://rubygems.org/
specs:
ansi (1.5.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.8)
builder (3.3.0)
diff-lcs (1.5.1)
dry-logger (1.0.4)
ffi (1.17.0)
ffi (1.17.0-arm64-darwin)
ffi (1.17.0-x86_64-darwin)
ffi (1.17.0-x86_64-linux-gnu)
kdl (1.0.6)
base64 (~> 0.2.0)
bigdecimal (~> 3.1.6)
racc (~> 1.5)
simpleidn (~> 0.2.1)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mab (0.0.3)
minitest (5.23.1)
minitest-global_expectations (1.0.1)
minitest (> 5)
minitest-hooks (1.5.1)
minitest (> 5.3)
minitest-reporters (1.6.1)
ansi
builder
minitest (>= 5.0)
ruby-progressbar
minitest-sprint (1.2.2)
path_expander (~> 1.1)
parser (3.3.3.0)
ast (~> 2.4.1)
racc
path_expander (1.1.2)
pp (0.5.0)
prettyprint
prettyprint (0.2.0)
racc (1.8.0)
rack (3.1.3)
rack-session (2.0.0)
rack (>= 3.0.0)
rack-test (2.1.0)
rack (>= 1.3)
rackup (2.1.0)
rack (>= 3)
webrick (~> 1.8)
rake (13.2.1)
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
ruby-progressbar (1.13.0)
ruby_parser (3.21.0)
racc (~> 1.5)
sexp_processor (~> 4.16)
sexp_processor (4.17.1)
simpleidn (0.2.3)
tilt (2.3.0)
unparser (0.6.13)
diff-lcs (~> 1.3)
parser (>= 3.3.0)
webrick (1.8.1)
zeitwerk (2.6.15)
PLATFORMS
aarch64-darwin
arm64-darwin
arm64-darwin-22
arm64-darwin-23
x86_64-darwin-20
x86_64-linux
DEPENDENCIES
bundler
dry-logger (~> 1.0.4)
kdl (~> 1.0.6)
listen (~> 3.9.0)
mab
minitest (~> 5.0)
minitest-global_expectations
minitest-hooks
minitest-reporters
minitest-sprint
parser
pp
rack
rack-session
rack-test
rackup
rake
ruby_parser
tilt
unparser
zeitwerk
BUNDLED WITH
2.5.11
camping-3.2.6/README.md 0000664 0000000 0000000 00000011330 14655474101 0014424 0 ustar 00root root 0000000 0000000  
# ⛺️ Camping, a Microframework
Camping is a micro web framework which stays as small as possible.
You can probably view the complete source code on a single page. But, you
know, it's so small that, if you think about it, what can it really do? Apparently
it can do a lot. It's pretty swell.
The idea here is to store a complete fledgling web application in a single
file like many small CGIs. But to organize it as a Model-View-Controller
application. And with time, you can move your Models, Views, and Controllers into
other files as your app grows.
Camping supports multiple *apps*, capsuled code that runs together. Each app can
have independent models, routes, and controllers.
Pack your gear when you go Camping! With a simple plugin system, Camping is easily
extensible. Add all sorts of useful and silly things.
## A Camping Skeleton
A skeletal Camping blog could look like this:
```ruby
require 'camping'
Camping.goes :Blog
module Blog::Models
class Post < Base; belongs_to :user; end
class Comment < Base; belongs_to :user; end
class User < Base; end
end
module Blog::Controllers
class Index
def get
@posts = Post.find :all
render :index
end
end
end
module Blog::Views
def layout
html do
head { title "My Blog" }
body do
h1 "My Blog"
self << yield
end
end
end
def index
@posts.each do |post|
h1 post.title
end
end
end
```
## Installation
Interested yet? Luckily it's quite easy to install Camping. We'll be using
a tool called RubyGems, and Bundler, so if you don't have that installed
yet, go grab it! Once that's sorted out, open up a Terminal or Command
Line and enter:
```
gem install camping
```
~~Even better, install the Camping Omnibus, a full package of recommended libs:~~ Camping Omnibus will return for summer vacation.
Now make a new directory filled with your camp essentials using the `camping new` command:
```
camping new Donuts # You can replace Donuts with whatever but CamelCased.
```
Move to your new directory, then use bundler to install all of your camp's dependencies:
```
cd donuts; bundle install
```
You can now run camping using the `camping` command. We recommend running camping in development mode locally. Make certain to prefix the camping command with `bundle exec` to run your app with the gems you've installed just for your camp:
```
bundle exec camping -e development
```
## Learning
First of all, you should read [the first chapters](/book/01_introduction.md)
of The Camping Book. It should hopefully get you started pretty quick. While
you're doing that, you should be aware of the _reference_ which contains
documentation for all the different parts of Camping.
[The wiki](https://github.com/camping/camping/wiki) is the place for all tiny,
useful tricks that we've collected over the years. Don't be afraid to share
your own discoveries; the more, the better!
We have a Discord channel over at the Ruby Punks community, so if you feel
like chatting with us, you should join [#camping](https://discord.gg/JSmPBsWgFt). Once our chattin' leads to action, we open up an issue on Github and move the conversation over there.
## Contributing
Camping is under active development, and we'd love some help!. Our current focus is bug fixes, documentation, and tests. We have a [number of issues](https://github.com/camping/camping/issues?q=is%3Aissue+is%3Aopen+label%3A%22Great+for+new+Contributors%22) open for new contributors to get crackin!
To begin contributing, [Fork Camping](https://github.com/camping/camping/fork), Then make a new branch, (`git checkout -b my_branch_name`) with your changes. When you're ready to bring all that hard work on over, open a draft PR and ping Karl to take a look, He'll help you fix any issues and help you get it inside of old Camping.
Have a suggestion? Open an issue with what you think we ought to be doing with our lives, and we'll talk about it. Don't be shy.
## Running Tests
Tests should be run using bundler and rake: `bundle exec rake`.
## Minting Releases
We use Ruby Gems to distribute versions of Camping. When you're ready to mint a release, run: `gem build -o camping.gem`, then `gem push camping.gem`.
## Authors
Camping was originally crafted by [why the lucky stiff](http://en.wikipedia.org/wiki/Why_the_lucky_stiff), but is now maintained by the _community_. This simply means that if we like your patch, it will be applied. Everything is managed through this Github repo, so just [open an issue](https://github.com/camping/camping/issues/new) and you can instantly take part in shaping Camping.
camping-3.2.6/Rakefile 0000664 0000000 0000000 00000010760 14655474101 0014620 0 ustar 00root root 0000000 0000000 $:.unshift 'extras'
begin
require 'rake/dsl_definition'
require 'rake/alt_system'
rescue LoadError
else
begin
if defined?(Rake::DeprecatedObjectDSL)
Rake::DeprecatedObjectDSL.class_eval do
private_instance_methods(false).each do |meth|
remove_method meth
end
end
end
rescue Exception
end
end
require "bundler/gem_tasks"
require 'rake'
require 'rake/clean'
require 'rake/testtask'
require 'tempfile'
require 'open3'
# require File.expand_path('../constants', __FILE__)
CLEAN.include ['**/.*.sw?', '*.gem', '.config', 'test/test.log', '.*.pt']
task :default => :check
# New Docs
## context for the docs sections: we're going to move to using Rdoc and yard.
## With some extras. The Docs folder will probably go away.
## New docs directly serve a website like a boss.
desc "Serves the docs locally"
task :serve do
sh "ruby -run -e httpd docs -p 8000"
end
## RDoc
begin
gem 'rdoc', '~>3.9.0'
rescue LoadError
task :docs do
puts "** Camping needs RDoc 3.9 in order to use the Flipbook template."
end
else
require 'rdoc/task'
RDoc::Task.new(:docs) do |rdoc|
# We have a recent version of RDoc, so let's use flipbook.
require 'rdoc/generator/singledarkfish'
rdoc.options += ['-f', 'singledarkfish', *RDOC_OPTS]
rdoc.template = "flipbook"
rdoc.rdoc_dir = 'doc'
rdoc.title = "Camping, a Microframework"
rdoc.rdoc_files.add ['README', 'lib/camping-unabridged.rb', 'lib/camping/**/*.rb', 'book/*']
end
end
task :rubygems_docs do
require 'rubygems/doc_manager'
def spec.installation_path; '.' end
def spec.full_gem_path; '.' end
manager = Gem::DocManager.new(spec)
manager.generate_rdoc
end
desc "Packages Camping."
task :package => :clean
## Tests
namespace :test do
Rake::TestTask.new(:camping) do |t|
t.libs << "test"
t.test_files = FileList['test/app_*.rb']
end
Rake::TestTask.new(:gear) do |t|
t.libs << "test"
t.test_files = FileList['test/gear/gear_*.rb']
end
## Reloader Tests
Rake::TestTask.new(:reloader) do |t|
t.libs << "test"
t.test_files = FileList['test/reload_*.rb']
end
## Config Reloader Tests
Rake::TestTask.new(:configreloader) do |t|
t.libs << "test"
t.test_files = FileList['test/config_*.rb']
end
desc "Run Camping::Server tests"
Rake::TestTask.new("server") do |t|
t.libs << 'test/server'
t.test_files = FileList["test/server/**/spec_*.rb"]
t.warning = false
t.verbose = false
end
end
## Diff
desc "Compare camping and camping-unabridged"
task :diff do
require 'parser/current'
require 'unparser'
require 'pp'
u = Tempfile.new('unabridged')
m = Tempfile.new('mural')
usexp = Parser::CurrentRuby.parse(File.read("lib/camping-unabridged.rb"))
msexp = Parser::CurrentRuby.parse(File.read("lib/camping.rb"))
u << Unparser.unparse(usexp)
m << Unparser.unparse(msexp)
u.flush
m.flush
sh "diff -u #{u.path} #{m.path} | less"
u.delete
m.delete
end
error = false
## Check
task :check => ["test:camping", "test:gear", "test:reloader", "test:configreloader", "test:server", "check:valid", "check:equal", "check:size", "check:lines", "check:exit"]
namespace :check do
desc "Check source code validity"
task :valid do
sh "ruby -c lib/camping.rb"
end
desc "Check equality between mural and unabridged"
task :equal do
require 'ruby_parser'
u = RubyParser.new.parse(File.read("lib/camping-unabridged.rb"))
m = RubyParser.new.parse(File.read("lib/camping.rb"))
u.reject! do |sexp|
sexp.is_a?(Sexp) and sexp[1] == s(:gvar, :$LOADED_FEATURES)
end
unless u == m
STDERR.puts "camping.rb and camping-unabridged.rb are not synchronized."
error = true
else
puts "✅ synchronized....."
end
end
SIZE_LIMIT = 6144
desc "Compare camping sizes to unabridged"
task :size do
FileList["lib/camping*.rb"].each do |path|
s = File.size(path)
puts "%21s : % 6d % 4d%%" % [File.basename(path), s, (100 * s / SIZE_LIMIT)]
end
if File.size("lib/camping.rb") > SIZE_LIMIT
STDERR.puts "lib/camping.rb: file is too big (> #{SIZE_LIMIT})"
error = true
end
end
desc "Verify that line length doesn't exceed 80 (120) chars for camping.rb"
task :lines do
i = 1
File.open("lib/camping.rb").each_line do |line|
if line.size > 121 # 1 added for \n
error = true
STDERR.puts "lib/camping.rb:#{i}: line too long (#{line[-10..-1].inspect})"
end
i += 1
end
end
task :exit do
exit 1 if error
end
end
camping-3.2.6/bin/ 0000775 0000000 0000000 00000000000 14655474101 0013717 5 ustar 00root root 0000000 0000000 camping-3.2.6/bin/camping 0000775 0000000 0000000 00000000661 14655474101 0015266 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
$:.unshift File.dirname(__FILE__) + "/../lib"
require 'camping'
# require 'camping-unabridged' # comment out the above line and uncomment this line to run camping unabridged with all sorts of stuff in it.
require 'camping/server'
begin
Camping::Server.start
rescue OptionParser::ParseError => ex
STDERR.puts "!! #{ex.message}"
puts "** use `#{File.basename($0)} --help` for more details..."
exit 1
end
camping-3.2.6/book/ 0000775 0000000 0000000 00000000000 14655474101 0014101 5 ustar 00root root 0000000 0000000 camping-3.2.6/book/01_introduction.md 0000664 0000000 0000000 00000001613 14655474101 0017445 0 ustar 00root root 0000000 0000000 # Introduction
Camping is a small, micro web framework, less than 5k, a little white blood cell in
the vein of Ruby. This little book will start with a tutorial which takes
about fifteen minutes - by the end you should have a little Camping site up.
The following chapters will eventually go deeper into how Camping, HTTP and
Rack work.
("Eventually", because these chapters are not written yet. This book is
currently a very much work in progress, and we'll be very grateful if you want
to help out.)
If you at any moment need some help or have any questions or comments, we
highly recommend [opening an issue](https://github.com/camping/camping/issues/new) to ask for help. We've got plenty of nice people willing to help. We also have a Discord channel
at [rubypunks](https://discord.gg/JSmPBsWgFt)
if you're into that sort of thing.
Enough talk. Ready? Let's ["get started"](02_getting_started.md).
camping-3.2.6/book/02_getting_started.md 0000664 0000000 0000000 00000030530 14655474101 0020114 0 ustar 00root root 0000000 0000000 # Getting Started
You'll need Ruby Gems to get camping. Install camping like so:
```bash
gem install camping
```
Start a new text file called camp.rb. Here's what you put inside:
```ruby
require 'camping'
Camping.goes :Nuts
```
Save it. Then, open a command prompt in the same directory. You'll want to
run:
```bash
$ camping
```
And you should get a message which reads:
```ruby
** Camping running on 0.0.0.0:3301.
```
This means that right now The Camping Server is running on port 3301 on your machine. Open your browser and visit http://localhost:3301/.
Your browser window should show:
```
Camping Problem!
/ Not found
```
No problem with that. The Camping Server is running, but it doesn't know what to show. Let's tell them.
## Hello clock
So, you've got Camping installed and it's running. Keep it running. You can edit files and The Camping Server will reload automatically. When you need to stop the server, press **Control-C**.
Let's show something. At the bottom of camp.rb add:
```ruby
module Nuts::Controllers
class Index < R '/'
def get = Time.now.to_s
end
end
```
Save the file and refresh the browser window. Your browser window should show the time, e.g.
```
Sun Jul 15 12:56:15 +0200 2007
```
## Enjoying the view
The Camping microframework allows us to separate our code using the MVC (Model-View-Controller) design pattern. Let's add a view to our Nuts application. Replace the module Nuts::Controllers with:
```ruby
module Nuts::Controllers
class Index < R '/'
def get
@time = Time.now
render :sundial
end
end
end
```
And below it add:
```ruby
module Nuts::Views
def layout
html do
head do
title { "Nuts And GORP" }
end
body { self << yield }
end
end
def sundial = p "The current time is: #{@time}"
end
```
Save the file, refresh your browser window and it should show a message like:
```
The current time is: Sun Jul 15 13:05:41 +0200 2013
```
And the window title reads "Nuts And GORP".
Here you can see we call render :sundial from our controller. This does exactly what it says, and renders our sundial method. We've also added a special method called layout which Camping will automatically wrap our sundial output in. If you're familiar with HTML, you'll see that our view contains what looks HTML tag names. This is Markaby, which is like writing HTML using Ruby!
Soon enough, you'll find that you can return anything from the controller, and it will be sent to the browser. But let's keep that for later and start investigating the routes.
## Routes
You probably noticed the weird R '/' syntax in the previous page. This is an uncommon feature of Ruby that is used in our favorite microframework, to describe the routes which the controller can be accessed on.
These routes can be very powerful, but we're going to have look at the simplest ones first.
```ruby
module Nuts::Controllers
class Words < R '/welcome/to/my/site'
def get = "You got here by: /welcome/to/my/site"
end
class Digits < R '/nuts/(\d+)'
def get(number) = "You got here by: /nuts/#{number}"
end
class Segment < R '/gorp/([^/]+)'
def get(everything_else_than_a_slash) = "You got here by: /gorp/#{everything_else_than_a_slash}"
end
class DigitsAndEverything < R '/nuts/(\d+)/([^/]+)'
def get(number, everything) = "You got here by: /nuts/#{number}/#{everything}"
end
end
```
Add this to `camp.rb` and try if you can hit all of the controllers.
Also notice how everything inside a parenthesis gets passed into the method, and is ready at your disposal.
### Simpler routes
This just in:
```ruby
module Nuts::Controllers
class Index
def get = "You got here by: /"
end
class WelcomeToMySite
def get = "You got here by: /welcome/to/my/site"
end
class NutsN
def get(number)= "You got here by: /nuts/#{number}"
end
class GorpX
def get(everything_else_than_a_slash) = "You got here by: /gorp/#{everything_else_than_a_slash}"
end
class NutsNX
def get(number, everything) = "You got here by: /nuts/#{number}/#{everything}"
end
end
```
Drop the < R-part and it attempts to read your mind. It won't always succeed, but it can simplify your application once in a while.
## Modeling the world
You can get pretty far with what you've learned now, and hopefully you've been playing a bit off-book, but it's time to take the next step: Storing data.
Let's start over again.
```ruby
Camping.goes :Nuts
module Nuts::Models
class Page < Base
end
end
```
Obviously, this won't do anything, since we don't have any controllers, but let's rather have a look at what we _do_ have.
We have a model named Page. This means we now can store wiki pages and
retrieve them later. In fact, we can have as many models as we want. Need one
for your users and one for your blog posts? Well, I think you already know how
to do it.
However, our model is missing something essential: a skeleton.
```ruby
Camping.goes :Nuts
module Nuts::Models
class Page < Base
end
class BasicFields < V 1.0
def self.up
create_table Page.table_name do |t|
t.string :title
t.text :content
# This gives us created_at and updated_at
t.timestamps
end
end
def self.down
drop_table Page.table_name
end
end
end
```
Now we have our first version of our model. It says:
```
If you want to migrate up to version one,
create the skeleton for the Page model,
which should be able to store,
"title" which is a string,
"content" which is a larger text,
"created_at" which is the time it was created,
"updated_at" which is the previous time it was updated.
If you want to migrate down from version one,
remove the skeleton for the Page model.
```
This is called a
[migration](http://api.rubyonrails.org/classes/ActiveRecord/Migration.html).
Whenever you want to change or add new models you simply add a new migration
below, where you increase the version number. All of these migrations builds
upon each other like LEGO blocks. Each new Migrations must have different
class's names, is a good idea name migration's explicit. For example:
```ruby
class AddTagColumn < V 1.1
def self.change
add_column Page.table_name, :tag, :string
Page.reset_column_information
end
end
```
Now we just need to tell Camping to use our migration. Write this at the bottom of camp.rb
```ruby
def Nuts.create
Nuts::Models.create_schema
end
```
When The Camping Server boots up, it will automatically call
Nuts.create. You can put all kind of startup-code here, but right now
we only want to create our skeleton (or upgrade if needed). Start The Camping
Server again and observe:
```bash
$ camping camp.rb
** Starting Mongrel on 0.0.0.0:3301
-- create_table("nuts_schema_infos")
-> 0.1035s
== Nuts::Models::BasicFields: migrating ===================================
-- create_table(:nuts_pages)
-> 0.0033s
== Nuts::Models::BasicFields: migrated (0.0038s) ==========================
```
Restart it, and enjoy the silence. There's no point of re-creating the
skeleton this time.
Before we go on, there's one rule you must know: Always place your models
before your migrations.
## Using our model
Let's explore how our model works by going into the _console_. The console
is good way to familiarize with your models. Test your models adding some
data by bare hand before addin it to the application.
```bash
$ camping -C camp.rb
** Starting console
>>
```
Now it's waiting for your input, and will give you the answer when you press
Enter. Here's what I did, leaving out the boring answers. You should add your
own pages.
```bash
>> Page = Nuts::Models::Page
>> hiking = Page.new(:title => "Hiking")
>> hiking.content = "You can also set the values like this."
>> hiking.save
>> page = Page.find_by_title("Hiking")
=> #
>> page = Page.find(1)
=> #
>> page.title
>> page.content
>> page.created_at
>> page.updated_at
>> Page.find_by_title("Fishing")
=> nil
## Page.create automatically saves the page for you.
>> Page.create(:title => "Fishing", :content => "Go fish!")
>> Page.count
=> 2
```
Now I have two pages: One about hiking and one about fishing.
## Wrapping it up
Wouldn't it be nice if we could show this wonderful our pages in a browser? Update camp.rb so it also contains something like this:
```ruby
module Nuts::Controllers
class Pages
def get
# Only fetch the titles of the pages.
@pages = Page.all(:select => "title")
render :list
end
end
class PageX
def get(title)
@page = Page.find_by_title(title)
render :view
end
end
end
module Nuts::Views
def list
h1 "All pages"
ul do
@pages.each do |page|
li do
a page.title, :href => R(PageX, page.title)
end
end
end
end
def view
h1 @page.title
self << @page.content
end
end
```
Here we meet our first _helper_:
```ruby
R(PageX, page.title)
```
This is the reversed router and it generates a URL based on a
controller. R takes the controller you want to link to, followed by the router
parameters. . Instead of typing:
```ruby
:href=>'/welcome/to/my/site'
```
You can let Camping do the hard work for you.
```ruby
:href=>R(Words)
```
If the route would have some parameter, you shall write like this:
```ruby
:href=>R(WordsX,'someword')
```
Camping ships with a few, but very useful, helpers, and you can easily add your
owns. Have a look at Camping::Helpers for how you use these.
There's a lot of improvements you could do here. Let me suggest:
* Show when the page was created and last updated.
* What happens when the page doesn't exist?
* What should the front page show?
* Allow or disallow authenticated users.
* Add a layout.
* Jazz it up a bit.
Helpers can work generating Markaby's code. You could write a helper for show
some kind of data and call it from your views (Add a layout).
## The last touch
We have one major flaw in our little application. You can't edit or add new
pages. Let's see if we can fix that:
```ruby
module Nuts::Controllers
class PageX
def get(title)
if @page = Page.find_by_title(title)
render :view
else
redirect PageXEdit, title
end
end
def post(title)
# If it doesn't exist, initialize it:
@page = Page.find_or_initialize_by_title(title)
# This is the same as:
# @page = Page.find_by_title(title) || Page.new(:title => title)
@page.content = @input.content
@page.save
redirect PageX, title
end
end
class PageXEdit
def get(title)
@page = Page.find_or_initialize_by_title(title)
render :edit
end
end
end
```
The core of this code lies in the new post method in the PageX
controller. When someone types an address or follows a link, they'll end up at
the get method, but you can easily create a form which rather sends
you to the post when submitted.
There are other names you can use, but they won't always work. So for now,
don't be fancy and just stick to get and post. We'll show
you how this really works later.
You might also notice that we use @input.content. The
@input-hash contains any extra parameters sent, like those in the
forms and those in the URL (/posts?page=50).
Here's an edit-view, but you can probably do better. See if you can
integrate all of this with what you already have.
```ruby
module Nuts::Views
def edit
h1 @page.title
form :action => R(PageX, @page.title), :method => :post do
textarea @page.content, :name => :content,
:rows => 10, :cols => 50
br
input :type => :submit, :value => "Submit!"
end
end
end
```
## Phew.
You've taken quite a few steps in the last minutes. You deserve a break. But
let's recap for a moment:
* Always place Camping.goes :App at the top of your file.
* Every route ends at a controller, but ...
* ... the controller only delegates the work.
* @input contains the extra parameters.
* The views are HTML disguised as Ruby.
* They can access the instances variables (those that starts with a single
at-sign) from the controller.
* The models allows you to store all kinds of data.
* Place your models before your migrations.
* Helpers are helpful.
If you wanna dive deep, stay reading for a [more extended tutorial](03_more_about_controllers.md) step by step...
Enjoy the code
camping-3.2.6/book/03_more_about_controllers.md 0000664 0000000 0000000 00000010022 14655474101 0021502 0 ustar 00root root 0000000 0000000 # Controllers
What are these _controllers_? This is a good question for a Camping newb. In the [MVC
paradigm](http://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller#Overview)
a Models could be described as a very weird and hard to understand thing.
When you look in the browser's navigation bar, you will see something like:
`http://localhost:3301/welcome/to/my/site`
The Universal Resource Locator (URL) is showing you a "structured" web site
running inside a server that listening in the `3301` port. The site's internal
routes are "drawing" the path: Inside the `root_dir/` is the directory `/welcome/`
and it recursively adds the names of the deeper directories: "to", "my", and "site" (`./to/my/site`).
But that is virtual, the site doesn't really have a directory structure like that...
That would be useless. The site uses a "route drawing system" to get _control_
of that route; this is what *the controller* does.
## Camping Routes and Controllers
In camping, each _capitalized_ word in a camel-cased contoller declaration is like the
words between each slash in a URL. For example:
```ruby
WelcomeToMySite
```
will draw the route:
```
/welcome/to/my/site
```
Or you could instead use the weird R helper to get more specific control of your routes:
```ruby
Welcome < R '/welcome/to/my/site'
```
All of this will be declared inside the Nuts::Controllers module.
## Controller Parameters
Controllers can also handle your application's parameters. For example,
when the client asks for a route like `/post/1`, a static web server would
look out for the directory named "1" and serve the content in that directory.
It would need a lot of "number-named" directories to do that simple job.
But in our case, the controller draws a dynamic path for every asked post. We just need
to tell him about the size of the flock.
In camping, adding `N` or `X` to a controller's declaration tells it to expect a parameter.
The `N` suffix, which will match a numbered route, is declared like this:
```ruby
class PostN
```
With this controller, adding a number to the simple `/post` route in your browser will trigger this route. For example,
either of these will work:
```
/post/1
/post/99
```
But this `N` route will not match against a word. For example, a request for `/pots/mypost`
will return 404 (Not Found). Because the `PostN` declaration will only match _Numeric_ parameters.
If you would like to match something other than a number, you should use the `X` suffix:
```ruby
class PostX
```
The _X_ tells the controller to match anything, including number and words. For example, it will match:
```
/post/1
/post/99
/post/mypost
```
But it will NOT match: `/post/mypost/1` (or anything that has "/" in the name). Since slashes signify
deeper directories, you would need to tell the controller to recognize the deeper directory before using a parameter.
You can do this using camel case, followed by the "X" or "N":
```ruby
class PostMypostX
```
## Getting parameters from the controller
Ok, we have the controller that match parameters; and now what?
Say that you want to show the post number N requested in the controller. You'll need the
number.
```ruby
class PostN
def get number
p "the parameter was: #{number}"
end
end
```
Please, do not try that at home. It's very dirty to use a _view_ inside the controller (more on that soon).
The method `get`, for the `/post/N route`, will take a parameter. That number will by
inside the "number parameter". From now, if you want to route something to your
post route, you can write a link 100% pragmatically like this:
```ruby
@post=rand 1..9
a "See the post number: #{@post}",:href=>R(PostN,@post)
```
For that example, we just choose a random post and then displayed a link to its path.
> but you told me that I shouldn't write that in the controller...
Yep...I said that. These things are called "views" because they will
be in the user's face. Our client will not see the controllers; they will be hidden from them when they visit our site.
Our client will only see the [views](04_more_about_views.md).
camping-3.2.6/book/04_more_about_views.md 0000664 0000000 0000000 00000006501 14655474101 0020301 0 ustar 00root root 0000000 0000000 # Views
The view is the scene for our show. The user is sitting in his chair
(the browser) and see on screen actors (the view). Enjoy the show
without think that behind the scenes there is a whole team. The team
behind the cameras is our controller but the user don't care about
that.
The user only see their browser and our application is just and HTML document.
## Camping Views
Inside the Nut::Views module, we will write methods. That method shall called
with the render sentence. The views do not use class.
```ruby
module Nust::Views
def post_number
p "you asked the post number @post"
end
end
```
Well, well, that was a views, but now: How we show it to the user? We will call
the view from the controller. And we pass to the view all the parameters that we
want to show.
```ruby
module Nuts::Controller
class PostN
def get number
@postn=number
render :post_number
end
end
end
```
We just declared a controller for the route /post/(number here). When the browser
ask for the route /post/1 the controller will be trigged and the get
method defined inside the class, will respond to the "get" request in
the web server.
The first instruction in our controller, will by write the number in the @postn
variable and then "render"
But what is `render`? This sentence is not from ruby, this is a camping's
sentence and mean: -show now the view named (:symbol). It take a symbol
as parameter, and the symbol's name, shall be one of these methods
declared in the views. Now we have only a view named post_number.
You could "associate" it MENTALLY as a hash like this:
```ruby
def my_view => render :my_view
```
But that will happen in your mind. In camping these will be happening in
the modules, not in a hash, therefore, their are very associated too.
Imagine your applications as a big building. The controller as the
corridors and the views as the offices. Where are the offices and we do
in each office?
## Views and Controllers
Model View and Controller, are joined but not scrambled. The views use
R(ControllerName) for call the controllers and "move". The controller
will use "render" for call the view.
-And now... what do you think we have behind the curtains?
```ruby
module Why::Controllers
class CircusCourtains
def get
require 'endertromb'
@monkey=Endertromb::Animals::StartMonkey.new :frog=>true,:place=>:hand
render :behind_curtain
end
end
end
```
-OMG! It's a monkey with a start in the hand!
-yes, ladies and gentleman, we have it just here for you
```ruby
module Views
def behind_curtain
p @monkey
end
end
```
No, we don't have it behind the curtains, but the user believe it. There
is the view, enjoy the show.
## Template engine
We spoke about the views, and HTML, but we are not using html's tags for
our view...
How happening this?
The rubyist have not necessary to write HTML code. Exists some options
named template. That the "p" before the `@monkey` in the view. A template
engine is a kind of "pseudo-language" for handle HTML code. In some
template engines you will also write HTML in a more easy way. Their also
handle ruby data inside HTML. Templates engine are the wheels of the
views.
In camping, you will write views using a template engine named
[Markaby](05_more_about_markaby.md) and you will write HTML pragmatically.
camping-3.2.6/book/05_more_about_markaby.md 0000664 0000000 0000000 00000012131 14655474101 0020567 0 ustar 00root root 0000000 0000000 ## Where Markaby's come from
A great musician, writer and programmer (and a bit crazy) named
[_why](http://en.wikipedia.org/wiki/Why_the_lucky_stiff) wrote camping. Before
banish himself to the dimension from where him come (a place named "the
keyhole"); hi also wanted, that the developers, should have not write pure HTML
code. Him dream with a world were the programmer write html in their
programming language.
Rails users are very hard guys. Their eat the soup using a fork and generate
HTML views in a template engine named Erb. Their use a long set of weird tools
for generate that Embedded Ruby (erb), their call it "scaffolding". The basic
idea is: "let me rails write html code for you" Many framework have in the
README a line that say: "support more populars template engines" but normally
peoples uses the default in the framework.
"Mab" is the template engine used by default in camping. _why Wrote the first
implementation named Markaby and then, judofyr and Jenna, power-up markaby
writing a more compact version of it.
While you are writing with mab, will see ruby code front you are eyes, but your
mind, will be seen pure HTML without tags. We will be "metaprograming HTML
code". In order to show a Header-1 tag, we just call a method named h1 and send
the content as parameter. It will make the dirty work writing all the tags just
like this:
```ruby
h1 'This is a header one'
```
"Write HTML pragmatically" mean: -write html using not html tags.
Markaby is a way for write Hyper Text Markup Language (HTML) using Ruby.
That do not mean "html knowledge unneeded"
But that is only the beginning, we can do more wild things.
## Writing HTML pragmatically
For example: if we want show a table for show users and their real
names:
```ruby
# call me using render :users
# from a get in the controllers
def users
table do
th 'User'
th 'Realname'
@users.each do |user,realname|
tr do
td user
td realname
end # tr
end # each
end # table
end # def
```
Take a look better [here](https://github.com/camping/mab/blob/master/README.md)
## Markaby and the layout
There is a special view named "layout". The layout, is view that will be
rendered each time any other view are rendered. In fact, the layout will take
the other views for compose himself. The layout is rendered before anything.
Each time you use the "render" sentence, you will be rendering the
layout and the desired view. Because that, wee need some "special"
tweaks in the layout. It must have a door with a poster that say:
"other view will enter using this door"
The layout will be rendered, and in some place you will put the other view.
This will be done with the "yield" sentence. Put the yield sentence whenever
you want rendering all the other views.
This is very useful, for example: We wan to write a footer in ALL the
pages that we are rendering. You can write the footer in every views
definition, or just write the footer in the layout.
Without a layout, if you render the table's views. Camping will drop out the
table just like that to the browser's render. It will not draw any body or head
tag. Writing body and head in every page could be a very bored task. But do not
worry, the layout is here.
Lets write our layout, with all the HTML shape and including a footer
```ruby
def layout
html do
head do
title 'My Blog'
end
body do
div.wrapper! do
self << yield
end
p.footer! do
text 'Powered by Camping'
end
end
end
end # layout
```
Well, that was wild. Let's see: First we have everything inside a block, the
html's block. At the next level, the first block is head, that is rendering
something like:
```html
My Blog
```
If you look at the HTML's source of camping, you will see a VERY LOOOONG
line with every the HTML code, better do not look it.
Then, come the more weird thing. A div with a estrange sentence:
```ruby
self << yield
```
That mean: -put just right here, the other view called.
In that place, in the center of the div.wrapper, will be placed all our
views called using render's sentence.
When you call:
```ruby
render :someview
```
Camping will rendering before, the layout view, and put all the content
of "someview" inside div.wrapper! You shall not write a lot of tag like
head or title.
Finally, it will be rendering a "p" named footer. That will be the footer in
all our pages.
## Tip
What would happen? If you do this in the layout:
```ruby
head do
title "#{@title}"
end
```
Hummm... You could "rewrite" the titles of each pages. In the
controller, you just need to declare the variable @title, and that will
be the title for that page.
Remember:
* Views take @variables from the controller
* Layout is rendered before any called view.
* Markaby is ruby code and it can be embedded
* View's modules use not `class` declarations
In the table example, we used a hash named @users. But. Where come from all
thats data?
It come from the controller, but the controller took it from the
[model](06_more_about_models.md). The M of the MVC, the layer who stare
the whole bunch of persistent data.
camping-3.2.6/book/06_more_about_models.md 0000664 0000000 0000000 00000003643 14655474101 0020435 0 ustar 00root root 0000000 0000000 ## More about Models
Models are used to persist data. That data could be anything. The balance in a bank account, A list of your favorite restaurants, your blog posts, you name it. Camping uses the *ActiveRecord* Gem, an ORM (object-relational mapper), that maps Database tables to objects.
We define models by inheriting from a base model named Base:
```ruby
class User < Base end
```
Very creative. Base is really just an alias for ActiveRecord, nothing fancy. We put our models into a namespaced module named after our App:
```ruby
Camping.goes :Nuts
module Nuts::Models
class User < Base end
end
```
Remember from earlier that Models need to be defined before our controllers, otherwise we can't use em. So keep them close to the top.
The new User model we've defined has a small problem, it's completely empty, it doesn't have any data that can be stored in it. Camping models map Database tables to objects automatically, but this model doesn't have a database table yet. To fix that we'll create a migration:
```ruby
Camping.goes :Nuts
module Nuts::Models
class User < Base; end
# Define a migration to add users
class AddUser < V 1.2
def self.up
create_table User.table_name do |t|
t.string :username
t.string :email
t.timestamps
end
end
def self.down
drop_table User.table_name
end
end
end
```
Databases, like birds, migrate. Migrations move our database from one configuration to another. In our case we're adding users. So cool. User's should be able to log in, sign up, maybe make some pages. We could make the next myspace. To get our database to make a users table we need force our app to create the schema.
```ruby
def Nuts.create
Nuts::Models.create_schema
end
```
Now that puppy will migrate when we launch our app.
Our Users now have greater hope to survive. So great. I love it.
camping-3.2.6/book/06_rules_of_thumb.md 0000664 0000000 0000000 00000007476 14655474101 0017763 0 ustar 00root root 0000000 0000000 # Keep it in one file
Generally, the idea is keep your app small and store in a single file. Your app will end up with four sections:
1. Camping setup
```ruby
do
require 'rubygems'
require 'camping'
Camping.goes :Blog
end
```
2. Models
```ruby
do
module Blog::Models
class User < Base; end
class Post < Base; belongs_to :user end
end
end
```
3. Controllers
```ruby
do
module Blog::Controllers
class Index < R '/'
def get; render :index end
end
end
end
```
4. Views
```ruby
do
module Blog::Views
def layout
html { body { self << yield } }
end
def index
div.page "Welcome!"
end
end
end
```
# What if things get out of hand?
If you’re piling up models and controllers, your file may begin to exceed 200 lines, which means lots of paging up and down. Go ahead and store your models, controllers and views in three separate files. Your directory structure should end up like this:
```
blog.rb
blog/
models.rb
controllers.rb
views.rb
```
(Note, for the development reloading to work, your required files (models.rb etc.) must be in a subdirectory named after your app.)
Your blog.rb would still contain the setup (No. 1):
```ruby
do
require 'rubygems'
require 'camping'
Camping.goes :Blog
require 'blog/helpers' # if needed
require 'blog/models'
require 'blog/views'
require 'blog/controllers'
end
```
# Small apps, many mounts
Rather than building huge Camping apps, the idea here is to write small apps which can each be mounted at directories on your web server. One restriction: these apps will share a database. However, this allows applications to access each other’s tables and simplifies setup and configuration.
To mount multiple camping apps, you can `require` the app files. When you mount more than one app they won't be mounted according to their parent directory, routing is explicit. If you'd like to give one of your apps a prefix, set the `url_prefix` option in your app:
```ruby
# camp.rb
require 'camping'
Camping.goes :Blog
require 'blog.rb'
Camping.goes :Wiki
require 'wiki.rb'
Wiki.set :url_prefix, 'tepee/'
Camping.goes :Charty
require 'charty.rb'
Charty.set :url_prefix, 'charts/'
```
By default Camping will look for a file called `camp.rb` in the root where Camping is executed. You can also supply a ruby filename to load your apps from there.
You’ll end up with:
```
http://localhost:3301/blog, a blogging app.
http://localhost:3301/tepee, a wiki app.
http://localhost:3301/charts, a charting app.
```
In your app, if you’re using the R() method to build your links, Camping will make sure the mount is added properly to links.
For example, if R(View, 1) is used in the blogging app mounted at /blog, the link will be written as /blog/view/1. If later you mount the blog at /articles instead, Camping will write the link as /articles/view/1.
# Give us a create method
```ruby
do
def Blog.create
# Code in here will run when the app starts, or reloads, but not when requests happen.
# You can use it to create database bits and pieces, files, folders, and so on.
end
end
```
# The camping server
The Camping Server is basically a set of rules. At the very least, The Camping Server must:
- Load all Camping apps in a directory.
- Load new apps that appear in that directory.
- Mount those apps according to their filename. (e.g. blog.rb is mounted at /blog.)
- Run each app’s create method upon startup.
- Reload the app if its modification time changes.
- Reload the app if it requires any files under the same directory and one of their modification times changes.
- Support the X-Sendfile header.
# Bin/camping
Camping comes with a very simple version of The Camping Server. bin/camping uses either WEBrick or Mongrel (if you have it installed.)
Run it like this: `camping /var/www/camping/*.` It will follow all of the rules above.
camping-3.2.6/book/07_philosophy.md 0000664 0000000 0000000 00000006063 14655474101 0017134 0 ustar 00root root 0000000 0000000 # Why's origin story
The philosophy of Camping is a long and winding tail of origin, met by the ideals for the future of many mad hackers. The story of camping begins in 2006 on a cool winters night in Pittsburgh, where we find a hacker hunched over his computer, typing ruby code with his right hand and playing a laser theremin with his left. To the spooky sounds of his own left hand, he hacked through the night. This night, camping was born.
Lets step back though time for a minute though, to the origins of this man. Once an upstanding PHP developer, he grew weary and tired of his day job, writing endless login pages and checkouts. He dreamt of a world free of his C-flavoured prison. Tales of promised lands, where snake powered ponies run wild, dancing around campfires full of rubies glowing red as blood. They worked him to the bone, until one day his bones just up and left. He couldn't do it any longer! He was on a mission to find that fire which fueled his dreams.
In seclusion, there isn't much known about this odd man's life. Some say he went crazy. Others say he became a well respected professor. Still others suggest both of these are true. But what we do know, is that it is here, that he developed his love of chunky bacon, foxes, and children shaped like keyholes.
And so he went on, crafting his mad writings, scribblings of foxes explaining ruby symbols, and making strange music. Soon this man found himself concerned that children had no good way to make their own eBay competitors. For this reason, he created Camping.
The End.
# Actual philosophy
Why The Lucky Stiff is no longer around, so those of us who contributed early on to Camping have since become its caretakers. We continue to push the framework forward, to be more compact, to be faster, easier, more fun. These are our guiding principals:
- Camping is for everyone. It sure is great to turn an idea in to a single ruby file which creates a website. It's also great to organise an app in to several files sometimes. You can plug bits together all in a row, and grow your evil monkey in to an entire army of evil circus animals!
- Camping isn't for making money. You can make money using camping. Nobody will stop you. But we don't have any buzzwords to offer, we won't make your unit tests easier, nor help you do market research. Our main contributors certainly aren't using camping in large scale deployments, and while camping is blazingly fast, we have no idea how well it would work if you ran it on lots of servers!
- Camping is really simple. You don't need to know much, to make nifty things with it, and you can really easily add more detailed bits as needed. Your brain will thank you.
- Camping apps are easy to automatically reload. Because most of them are just one ruby file.
- Camping encourages experimentation. The whole thing is an experiment.
- If you're new to ruby, there are heaps of quirky hacks in here which will teach you all sorts of obscure, nifty, and outright strange things about the Ruby language.
- Camping doesn't take itself too seriously. We're a fun bunch, living on the edge of zany!
camping-3.2.6/book/08_publishing_an_app.md 0000664 0000000 0000000 00000016317 14655474101 0020424 0 ustar 00root root 0000000 0000000 Once you've built your Camping app, you'll almost certainly want to get it online some place, so others can get at it! There are tons of ways to do this, some easy, some hard, some free, and some expensive. Some of these techniques also come with limitations.
# Using a Rackup compatible host
First make a config.ru file and fill it with goodies:
```ruby
# config.ru
require 'camping'
require 'camping/server'
Camping::Server.start
```
If your app has any settings, like the port number, or the file where your camping app is located if it's not camp.rb, then pass those along as a hash:
```ruby
# config.ru
require 'camping'
require 'camping/server'
Camping::Server.start({
:script => 'blog.rb',
:port => '80'
})
```
# Using a spare computer
Cost: Usually free; Limitations: None really; Extra Requirements: Need to have a computer you can leave on all the time.
This is probably the easiest way. Just find a computer you can leave on all the time, and use The Camping Server on Port 80, by running something like this in the Command Prompt or Terminal:
```
camping --port 80 myapp.rb
```
Then check to make sure it's working by visiting http://localhost:80/ on that computer. Next, you'll need to find out if your internet provider gives you a Static IP address, or a Dynamic IP. Most home internet providers give you a Dynamic IP, where most business accounts come with a Static IP. If you don't know, you can find out by phoning your internet provider.
# If you have a static IP address
This is really a great situation to be in. You'll next want to get a domain name of some sort. You can register one on a Domain Name Registrar, but this will generally cost about $12 per year to keep. You can also get a free subdomain name from services like afraid.org. Regardless how you do it, you'll want to create an A Record, and for it's value, provide your IP address. You can usually find out your IP address by visiting an IP Lookup Website, or by asking your Internet Provider.
# If you have a dynamic IP address
Your IP Address will change from time to time, so you'll need to use a Dynamic DNS service. Popular DDNS services include Afraid.org, DynDNS, and No-IP. These will either require you to run a little program on your computer to watch when your IP changes and update the domain name, or have you enter a special username and password in to certain home routers.
# Troubleshooting
If you find others can't access your website through the domain name, and you have a dynamic IP, your Internet Provider might be applying Port Blocking. If you phone them, they might be able to remove the blocks. Otherwise, you'll need to run the camping server on a different port. 8080 is a common alternative. Of note, you'll need to include :8080 (or whatever) after the domain name and before the slash in your web address for this to work. For example: http://funkytown.afraid.org:8080/ - otherwise, make sure the DDNS updating software is running and working correctly.
# Using Dreamhost
Cost: About $5 per month; Limitations: None really; Extra Requirements: SFTP or FTP software, and an SSH client like PuTTY on windows, or the Terminal on Linuxes and Mac OS.
Dreamhost have some really cheap hosting plans, and are quite easy to use. They include support for Rack on their shared hosting plans, and are fairly reliable and fast.
Enable the "Passenger (Ruby/Python apps only):" option in a domain's settings, from the Manage Domains page within the Dreamhost Panel. Ensure the "Shell account - allows SFTP/FTP plus ssh access." option is enabled on your main user, from the Manage Users page. Use your SSH software to login, and if you haven't already, set up rubygems. Once that's done, it's time to install stuff!
```
gem install camping-omnibus
```
Install any other gems you might need for your app, then use your SFTP or FTP software to upload the file to the folder labeled with your domain's name. Lastly, you'll need to add a few things:
A folder called 'tmp', containing a file called 'restart.txt', which you'll need to change whenever you need to make Dreamhost reload your app's source code (while you're trying things out, you can change this filename to 'always_restart.txt' to reload the app with each request).
A file called config.ru, containing something like:
```ruby
require 'rubygems'
ENV['GEM_PATH'] = File.join(File.dirname(__FILE__), '..', '.gems') if File.exist?('/dh')
Gem.clear_paths
require 'rack'
require 'blog.rb'
use Rack::ShowExceptions # optional, but helpful if there's any errors
Blog.create if Blog.respond_to? :create
run Blog
```
Make sure it's all uploaded, and your app should be online! Yay!
# Using Heroku
Cost: None for small apps; Limitations: Cannot change files in the filesystem, must use database; Extra Requirements: Need to install and use git to upload your app to the Heroku servers.
Someone should really add stuff to this section. For now, see How to run Camping 2.0 apps on Heroku or Heroku's own guide for Rack-based apps (scroll down for Camping).
# Using Google App Engine
Cost: None for small apps; Limitations: Cannot change files in the filesystem, must use the google database; Extra Requirements: ???.
Someone should also add information to this section. It's possible using jRuby somehow. I imagine this guide would help a lot!
# Using a CGI Webhost
Firstly, make sure your webhost can run ruby apps. Most can, thankfully. If you have shell access, you can check by entering 'which ruby' and seeing if it finds anything. Unfortunately, different webhosts work differently, so we can't provide specific instructions for installing rubygems on your webhost. If all else fails, you can extract the files from the lib folder within each gem, put the files in a folder, and add the big folder to your load path. Once you've done this, you'll just need to make sure your camping app file starts with:
```ruby
#!/usr/bin/ruby
```
And then either the simple postamble, or the complex postamble:
```ruby
# Plug it in to CGI
Rackup::Handler::CGI.run(Blog)) if __FILE__ == $0
```
Then upload it to your server, and change the file's permissions to have all the executable bits set. You should now be able to visit the file using your web browser (for example, http://example.com/blog.rb). In fact, you don't even need the .rb. the #!/usr/bin/ruby line lets your server know what kind of file it is. :)
# Using a Rack Compatible Web Host
Follow your Webhost's instructions, and in the config.ru file, add a little something like this:
```ruby
... require 'blog' run Blog end
```
To set up Passenger and Rack for really easy deployment under Apache or Nginx see the screencasts on the Passenger site. If you have (e.g.) Apache on your computer, this is also good for local testing too.
You can also use Rack::URLMap to plug a whole bunch of different apps in to one folder. A camping app here, a rails project there, a sinatra doodad over there in the corner messing up the whole global namespace. The possibilities are severely limited!
# And Also Some Luck
We wish you good luck! Publishing your app to a server can be an annoying experience the first time you do it. Often little bugs will appear you never noticed before on your computer, or things just might not work for mysterious reasons. Stick to it, and don't be afraid to ask for help from those more battle hardened.
camping-3.2.6/book/09_upgrade_notes.md 0000664 0000000 0000000 00000005635 14655474101 0017603 0 ustar 00root root 0000000 0000000 # Upgrade notes
This document includes everything needed in order to upgrade your applications. If you’re looking for all the new features in a version, please have a look at the CHANGELOG in the Camping source.
# From 2.0 to 2.1
## Options
In Camping 2.1 there is now a built-in way to store options and settings. If you use cookie session, it means that you’ll now have to change to:
```ruby
module Nuts
set :secret, "Very secret text, which no-one else should know!"
include Camping::Session
end
```
# From 1.5 to 2.0
## Rack
The biggest change in 2.0 is that it now uses Rack internally. This means that you’ll now have to deploy Camping differently, but hopefully more easily too. Now every Camping application is also a Rack application, so simply check out the documentation to the server of your choice.
## Require 'camping/db'
In earlier versions of Camping, you loaded database support by:
```ruby
require 'camping/db'
```
Actually, this loaded a very thin layer on top of ActiveRecord, and in the future we want to experiment with other libraries. Therefore you should now simply remove the line, and instead just inherit from Base right away. This also means you’ll have to place your migrations after the models.
We also encourage you to use `Model.table_name` instead of `:appname_model`, just to make sure it’s named correctly.
```ruby
## Don't require anything:
# require 'camping/db'
module Nuts::Models
## Just inherit Base:
class Page < Base; end
## Migrations have to come *after* the models:
class CreateTheBasics < V 0.1
def self.up
create_table Page.table_name do |t|
...
end
end
def self.down
drop_table Page.table_name
end
end
end
```
## Cookie Sessions
Camping 2.0 now uses a cookie-based session system, which means you no longer need a database in order to use sessions. The disadvantage of this is that you are restricted to only around 4k of data. See below for the changes required, and see Camping::Session more details.
```ruby
module Nuts
## Include Camping::Session as before:
include Camping::Session
## But also define a secret:
secret "Very secret text, which no-one else should know!"
end
def Nuts.create
## And remove the following line:
# Camping::Models::Session.create_schema
end
```
## Error Handling
Camping now uses three methods in order to handle errors. These replaces the old classes NotFound and ServerError.
r404 is called when a route can’t be found.
r501 is called when a route is found, but doesn’t respond to the method.
r500 is called when an error happens.
You can override these in your application:
```ruby
module Nuts
def r404(path)
"Sorry, but I can't find #{path}."
end
def r501(method)
"Sorry, but I can't respond to #{method}."
end
def r500(klass, method, ex)
"Sorry, but #{klass}##{method} failed with #{ex}."
end
end
```
It should be noted that this might change in the future.
camping-3.2.6/book/10_middleware.md 0000664 0000000 0000000 00000007420 14655474101 0017043 0 ustar 00root root 0000000 0000000 Middleware in Camping is just Rack Middleware, and if you're familiar with Rack middleware then you'll be right at home. I'm going to assume that you have no idea what Rack or Rack middleware *is*, Let's learn about it.
# The HTTP Request
The web works in a request -> response pattern. When we *GO* to a webpage our browser is making a *request* to a web server. That request is processed and a *response* is sent. We have already covered how a response is mapped to our ruby code through controllers in Chapter 3. Those responses could be any of a number of things, HTML, an image, JSON, CSS. Web servers are pretty capable. When using Camping you'll generally respond with HTML, CSS, or Javascript, maybe some images, usually a bit of everything.
As that request passes through your application you may need to make some decisions about it. Does the request try to get something with restricted access? Is it a request to a dynamic or cached file? Whatever it is we can write some Ruby code to make some decisions about that request before passing it along to Camping's router. That's what Middleware is for.
Middleware is Rack based and follows Rack Middleware conventions. Write a class, name it whatever you want but it must have an `#initialize` and a `#call` method.
```ruby
class MyMiddleware
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
# Do something to status, headers, and body here...
[status, headers, body] # return an array of the status, headers, body.
end
end
```
The `#initialize` method must store a reference to an `app` object that is passed in as a parameter. The `#call` method accepts an environment parameter that we call `env` and returns an array with 3 values: `status`, `headers`, and `body`.
Now, when I first saw this I was confused, why do we immediately call `Call` on the app? Each Rack app receives an array to represent the environment, and then returns that same array at the end. It's just passing along the status, headers, and body of our request/response object. There could be a lot of middleware all chained along. In fact, the `app` provided in the initialize method probably isn't the app at all, but some other middleware. Calling the app with the `env` data, then making our own decisions on the `status`, `headers`, and `body`, is how we actually chain the middleware together.
Calling @app sets up each middleware in the middleware chain. It's like taking a break in the middle of washing the dishes, to take out the trash. If you have a lot of middleware it's like:
* start washing dishes.
* start taking out trash.
* start sweeping the floor.
* finish sweeping the floor.
* finish taking out the trash.
* finish washing the dishes.
Sometimes middleware accepts settings or a block, to get your own middleware to do that write it like this:
```ruby
class MyMiddleware
def initialize(app, *a, &block)
@app = app
a.each { |setting|
# do something with each setting
}
block.call()
end
# ... call and stuff ...
end
```
Execute implicitly passed blocks with yield:
```ruby
class MyOtherMiddleware
def initialize(app, *a) # &block can be omitted, because it's implicitly passed
@app = app
a.each { |setting|
# do something with each setting
}
yield # calls implicitly passed block
end
end
```
Good Rack Middleware shouldn't know if you're running a Camping app, or a Sinatra app, or a Rails app. Keep it framework agnostic. The Rack specification is pretty great at keeping that up.
### notes:
* really good railscast about it that's like... 13 years old: http://railscasts.com/episodes/151-rack-middleware?autoplay=true
* An extremely old article about it: https://web.archive.org/web/20150105094611/https://www.amberbit.com/blog/2011/07/13/introduction-to-rack-middleware/
camping-3.2.6/book/11_gear.md 0000664 0000000 0000000 00000002023 14655474101 0015637 0 ustar 00root root 0000000 0000000 # Pack Your Gear
Camping provides a way to include and expand Camping without messing with it's innards too much, we call these plugins gear.
To use gear you need to *pack* it into camping:
```ruby
Camping.goes :Blog
module Blog
pack Camping::Gear::CSRF
end
# or
Blog.pack Camping::Gear::CSRF
```
Define your gear by opening a module:
```ruby
module Royalty
def queens
@queens ||= [ "Beyonce", "Niki", "Doja"]
end
end
```
Gear define methods and helpers that are included in your app. Define a `ClassMethods` module to have class methods included:
```ruby
module Royalty
module ClassMethods
def secret_sauce
@_secret_sauce ||= SecureRandom.base64(32)
end
end
# /...
end
```
You can also supply a setup callback method that runs after your gear is packed:
```ruby
module Royalty
# Run a setup routine with this Gear.
def self.setup(app)
@app = app
@app.set :saucy_secret, "top_secret_sauce"
end
end
```
We'll be adding some really great gear soon. In the meantime, try making your own gear.
camping-3.2.6/camping-omnibus.gemspec-old 0000664 0000000 0000000 00000003622 14655474101 0020363 0 ustar 00root root 0000000 0000000 # require File.expand_path('../constants', __FILE__)
# camping_omni
NAME = "camping"
BRANCH = "3.0.0"
GIT = ENV['GIT'] || "git"
REV = `#{GIT} rev-list HEAD`.strip.split.length
VERS = ENV['VERSION'] || BRANCH # (REV.zero? ? BRANCH : [BRANCH, REV] * '.')
RDOC_OPTS = ["--line-numbers", "--quiet", "--main", "README"]
def camping_spec
@spec ||= Gem::Specification.new do |s|
s.name = NAME
s.version = VERS
s.platform = Gem::Platform::RUBY
# s.extra_rdoc_files = FileList["README.md", "CHANGELOG", "COPYING", "book/*"].to_a
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb']
s.summary = "miniature rails for anyone"
s.author = "why the lucky stiff"
s.email = 'why@ruby-lang.org'
s.homepage = 'http://camping.rubyforge.org/'
s.executables = ['camping']
s.add_runtime_dependency('rack', '~> 3.0', '>= 3.0.4.1')
s.add_runtime_dependency('rack-session', '~> 2.0', '>=2.0.0')
s.add_runtime_dependency('mab', '~> 0.0', '>=0.0.3')
s.add_runtime_dependency('rackup', '~> 0.2', '>=0.2.2')
s.required_ruby_version = '>= 2.5.3'
s.files = %w(COPYING README.md Rakefile) +
Dir.glob("{bin,doc,test,lib,extras,book}/**/*") +
Dir.glob("ext/**/*.{h,c,rb}") +
Dir.glob("examples/**/*.rb") +
Dir.glob("tools/*.rb")
s.require_path = "lib"
s.bindir = "bin"
end
end
camping_spec
def camping_omni
@omni ||= Gem::Specification.new do |s|
s.name = "camping-omnibus"
s.version = VERS
s.platform = Gem::Platform::RUBY
s.summary = "the camping meta-package for updating ActiveRecord, and SQLite3 bindings"
%w[author email homepage].each { |x| s.__send__("#{x}=", camping_spec.__send__(x)) }
s.add_dependency('camping', ">=#{BRANCH}")
s.add_dependency('guidebook')
s.add_dependency('sqlite3', '~> 1.4', '>= 1.4.4')
s.add_dependency('cairn', '>= 7.1.0')
end
end
camping_omni
camping-3.2.6/camping.gemspec 0000664 0000000 0000000 00000003435 14655474101 0016137 0 ustar 00root root 0000000 0000000 # require File.expand_path('../constants', __FILE__)
# camping_spec
NAME = "camping"
BRANCH = "3.2.6"
GIT = ENV['GIT'] || "git"
REV = `#{GIT} rev-list HEAD`.strip.split.length
VERS = ENV['VERSION'] || BRANCH
RDOC_OPTS = ["--line-numbers", "--quiet", "--main", "README"]
@spec ||= Gem::Specification.new do |s|
s.name = NAME
s.version = VERS
s.licenses = ['MIT']
s.platform = Gem::Platform::RUBY
# s.extra_rdoc_files = FileList["README.md", "CHANGELOG", "COPYING", "book/*"].to_a
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb']
s.summary = "miniature rails for anyone"
s.author = "why the lucky stiff"
s.email = 'why@ruby-lang.org'
s.homepage = 'http://rubycamping.org/'
s.executables = ['camping']
s.add_runtime_dependency('rake', '~> 13.2.1')
s.add_runtime_dependency('mab', '~> 0.0', '>=0.0.3')
s.add_runtime_dependency('tilt', '~> 2.3.0',)
s.add_runtime_dependency('rack', '~> 3.0', '>= 3.0.4.1')
s.add_runtime_dependency('rack-session', '~> 2.0', '>=2.0.0')
s.add_runtime_dependency('rackup', '~> 2.1.0')
s.add_runtime_dependency('kdl', '~> 1.0', '>=1.0.5')
s.add_runtime_dependency('zeitwerk', '~> 2.6.15', '>=2.6.15')
s.add_runtime_dependency('listen', '~> 3.9.0', '>=3.9.0')
s.add_runtime_dependency('dry-logger', '~> 1.0.4')
s.add_development_dependency "bundler"
s.add_development_dependency "minitest", "~> 5.0"
s.add_development_dependency "minitest-global_expectations"
s.add_development_dependency "minitest-sprint"
s.required_ruby_version = '>= 3.1.2'
s.files = %w(COPYING README.md Rakefile) +
Dir.glob("{bin,doc,test,lib,extras,book}/**/*") +
Dir.glob("ext/**/*.{h,c,rb}") +
Dir.glob("examples/**/*.rb") +
Dir.glob("tools/*.rb")
s.require_path = "lib"
s.bindir = "bin"
end
camping-3.2.6/constants.rb 0000664 0000000 0000000 00000003140 14655474101 0015506 0 ustar 00root root 0000000 0000000 require 'rake'
NAME = "camping"
BRANCH = "2.3"
GIT = ENV['GIT'] || "git"
REV = `#{GIT} rev-list HEAD`.strip.split.length
VERS = ENV['VERSION'] || (REV.zero? ? BRANCH : [BRANCH, REV] * '.')
RDOC_OPTS = ["--line-numbers", "--quiet", "--main", "README"]
def camping_spec
@spec ||= Gem::Specification.new do |s|
s.name = NAME
s.version = VERS
s.platform = Gem::Platform::RUBY
# s.extra_rdoc_files = FileList["README.md", "CHANGELOG", "COPYING", "book/*"].to_a
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/camping.rb']
s.summary = "miniature rails for anyone"
s.author = "why the lucky stiff"
s.email = 'why@ruby-lang.org'
s.homepage = 'http://camping.rubyforge.org/'
s.executables = ['camping']
s.add_dependency('rack', '>=1.0')
s.add_dependency('mab', '>=0.0.3')
s.required_ruby_version = '>= 1.8.2'
s.files = %w(COPYING README.md Rakefile) +
Dir.glob("{bin,doc,test,lib,extras,book}/**/*") +
Dir.glob("ext/**/*.{h,c,rb}") +
Dir.glob("examples/**/*.rb") +
Dir.glob("tools/*.rb")
s.require_path = "lib"
s.bindir = "bin"
end
end
def camping_omni
@omni ||= Gem::Specification.new do |s|
s.name = "camping-omnibus"
s.version = VERS
s.platform = Gem::Platform::RUBY
s.summary = "the camping meta-package for updating ActiveRecord, and SQLite3 bindings"
%w[author email homepage].each { |x| s.__send__("#{x}=", camping_spec.__send__(x)) }
s.add_dependency('camping', ">=#{BRANCH}")
s.add_dependency('activerecord')
s.add_dependency('sqlite3', '>=1.1.0.1')
end
end
camping-3.2.6/docs/ 0000775 0000000 0000000 00000000000 14655474101 0014077 5 ustar 00root root 0000000 0000000 camping-3.2.6/docs/assets/ 0000775 0000000 0000000 00000000000 14655474101 0015401 5 ustar 00root root 0000000 0000000 camping-3.2.6/docs/assets/layout.css 0000664 0000000 0000000 00000005363 14655474101 0017437 0 ustar 00root root 0000000 0000000 /* Layout.scss */
/* Most of these styles lifted from picocss.com */
body > main {
display: grid;
grid-template-rows: 1fr;
row-gap: 2rem;
margin-bottom: calc(var(--camp-spacing) * 4);
padding: 0;
}
body > main > * {
min-width: 0;
margin-bottom:0
}
@media (min-width: 1024px) {
body > main {
grid-template-rows: auto 1fr;
grid-template-columns: 11rem 1fr;
grid-template-areas: "menu header" "menu body";
-moz-column-gap: 3rem;
column-gap: 3rem;
row-gap:3rem
}
body > main:has(aside#table-of-contents) {
grid-template-rows: auto auto 1fr;
grid-template-columns: 11rem 1fr;
grid-template-areas: "menu header" "menu table-of-content" "menu body"
}
body > main > nav[aria-label=breadcrumb] {
display:none
}
body > main > aside > nav {
margin-top:calc(var(--pico-block-spacing-vertical)/2)
}
/* body > main > aside > nav.is-sticky-above-lg {
position: sticky;
top: calc(var(--pico-main-top-offset) + var(--pico-block-spacing-vertical) /2);
max-height: calc(var(--max-height) - var(--pico-spacing));
overflow: auto;
transition: top var(--pico-transition);
transition-delay:50ms
} */
body > main > aside#documentation-menu {
grid-area:menu
}
body > main > hgroup {
grid-area: header;
margin-top:calc(var(--pico-block-spacing-vertical)/2)
}
body > main > aside#table-of-contents {
grid-area:table-of-content
}
body > main > [role=document] {
grid-area:body
}
}
@media (min-width: 1280px) {
body > main {
grid-template-rows: auto 1fr;
grid-template-columns: 10.5rem 1fr;
grid-template-areas: "menu header" "menu body";
row-gap:4rem
}
body > main:has(aside#table-of-contents) {
grid-template-rows: auto 1fr;
grid-template-columns: 10.5rem 1fr 10.5rem;
grid-template-areas: "menu header table-of-content" "menu body table-of-content"
}
body > main > aside#documentation-menu {
grid-area:menu
}
body > main > hgroup {
grid-area:header
}
body > main > aside#table-of-contents {
grid-area:table-of-content
}
body > main > [role=document] {
grid-area:body
}
}
@media (min-width: 1536px) {
body > main {
grid-template-rows: auto 1fr;
grid-template-columns: 10rem 1fr 10rem;
grid-template-areas: "menu header table-of-content" "menu body table-of-content";
row-gap:5rem
}
}
/* Wells */
well {
display: flex;
flex-direction: column;
gap: var(--camp-space-1);
margin-bottom: calc(var(--camp-spacing) * 4);
}
main > well {
width: 70%;
margin: auto;
}
camping-3.2.6/docs/assets/pico.css 0000664 0000000 0000000 00000231627 14655474101 0017060 0 ustar 00root root 0000000 0000000 @charset "UTF-8";
/*!
* Pico CSS ✨ v2.0.6 (https://picocss.com)
* Copyright 2019-2024 - Licensed under MIT
*/
/**
* Styles
*/
:root {
--pico-font-family-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--pico-font-family-sans-serif: system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, "Helvetica Neue", sans-serif, var(--pico-font-family-emoji);
--pico-font-family-monospace: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace, var(--pico-font-family-emoji);
--pico-font-family: var(--pico-font-family-sans-serif);
--pico-line-height: 1.5;
--pico-font-weight: 400;
--pico-font-size: 100%;
--pico-text-underline-offset: 0.1rem;
--pico-border-radius: 0.25rem;
--pico-border-width: 0.0625rem;
--pico-outline-width: 0.125rem;
--pico-transition: 0.2s ease-in-out;
--pico-spacing: 1rem;
--pico-typography-spacing-vertical: 1rem;
--pico-block-spacing-vertical: var(--pico-spacing);
--pico-block-spacing-horizontal: var(--pico-spacing);
--pico-form-element-spacing-vertical: 0.75rem;
--pico-form-element-spacing-horizontal: 1rem;
--pico-group-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-group-box-shadow-focus-with-button: 0 0 0 var(--pico-outline-width) var(--pico-primary-focus);
--pico-group-box-shadow-focus-with-input: 0 0 0 0.0625rem var(--pico-form-element-border-color);
--pico-modal-overlay-backdrop-filter: blur(0.375rem);
--pico-nav-element-spacing-vertical: 1rem;
--pico-nav-element-spacing-horizontal: 0.5rem;
--pico-nav-link-spacing-vertical: 0.5rem;
--pico-nav-link-spacing-horizontal: 0.5rem;
--pico-nav-breadcrumb-divider: ">";
--pico-icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
--pico-icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");
--pico-icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
--pico-icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");
--pico-icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");
--pico-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");
--pico-icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");
--pico-icon-loading: url("data:image/svg+xml,%3Csvg fill='none' height='24' width='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3E%3Cstyle%3E g %7B animation: rotate 2s linear infinite; transform-origin: center center; %7D circle %7B stroke-dasharray: 75,100; stroke-dashoffset: -5; animation: dash 1.5s ease-in-out infinite; stroke-linecap: round; %7D @keyframes rotate %7B 0%25 %7B transform: rotate(0deg); %7D 100%25 %7B transform: rotate(360deg); %7D %7D @keyframes dash %7B 0%25 %7B stroke-dasharray: 1,100; stroke-dashoffset: 0; %7D 50%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -17.5; %7D 100%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -62; %7D %7D %3C/style%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='10' fill='none' stroke='rgb(136, 145, 164)' stroke-width='4' /%3E%3C/g%3E%3C/svg%3E");
}
@media (min-width: 576px) {
:root {
--pico-font-size: 106.25%;
}
}
@media (min-width: 768px) {
:root {
--pico-font-size: 112.5%;
}
}
@media (min-width: 1024px) {
:root {
--pico-font-size: 118.75%;
}
}
@media (min-width: 1280px) {
:root {
--pico-font-size: 125%;
}
}
@media (min-width: 1536px) {
:root {
--pico-font-size: 131.25%;
}
}
a {
--pico-text-decoration: underline;
}
small {
--pico-font-size: 0.875em;
}
h1,
h2,
h3,
h4,
h5,
h6 {
--pico-font-weight: 700;
}
h1 {
--pico-font-size: 2rem;
--pico-line-height: 1.125;
--pico-typography-spacing-top: 3rem;
}
h2 {
--pico-font-size: 1.75rem;
--pico-line-height: 1.15;
--pico-typography-spacing-top: 2.625rem;
}
h3 {
--pico-font-size: 1.5rem;
--pico-line-height: 1.175;
--pico-typography-spacing-top: 2.25rem;
}
h4 {
--pico-font-size: 1.25rem;
--pico-line-height: 1.2;
--pico-typography-spacing-top: 1.874rem;
}
h5 {
--pico-font-size: 1.125rem;
--pico-line-height: 1.225;
--pico-typography-spacing-top: 1.6875rem;
}
h6 {
--pico-font-size: 1rem;
--pico-line-height: 1.25;
--pico-typography-spacing-top: 1.5rem;
}
thead th,
thead td,
tfoot th,
tfoot td {
--pico-font-weight: 600;
--pico-border-width: 0.1875rem;
}
pre,
code,
kbd,
samp {
--pico-font-family: var(--pico-font-family-monospace);
}
kbd {
--pico-font-weight: bolder;
}
input:not([type=submit],
[type=button],
[type=reset],
[type=checkbox],
[type=radio],
[type=file]),
:where(select, textarea) {
--pico-outline-width: 0.0625rem;
}
[type=search] {
--pico-border-radius: 5rem;
}
[type=checkbox],
[type=radio] {
--pico-border-width: 0.125rem;
}
[type=checkbox][role=switch] {
--pico-border-width: 0.1875rem;
}
[role=search] {
--pico-border-radius: 5rem;
}
[role=search] button,
[role=search] [type=submit],
[role=search] [type=button],
[role=search] [role=button],
[role=group] button,
[role=group] [type=submit],
[role=group] [type=button],
[role=group] [role=button] {
--pico-form-element-spacing-horizontal: 2rem;
}
details summary[role=button]::after {
filter: brightness(0) invert(1);
}
[aria-busy=true]:not(input, select, textarea):is(button, [type=submit], [type=button], [type=reset], [role=button])::before {
filter: brightness(0) invert(1);
}
/**
* Color schemes
*/
[data-theme=light],
:root:not([data-theme=dark]) {
--pico-background-color: #fff;
--pico-color: #373c44;
--pico-text-selection-color: rgba(71, 164, 23, 0.25);
--pico-muted-color: #646b79;
--pico-muted-border-color: #e7eaf0;
--pico-primary: #33790f;
--pico-primary-background: #398712;
--pico-primary-border: var(--pico-primary-background);
--pico-primary-underline: rgba(51, 121, 15, 0.5);
--pico-primary-hover: #265e09;
--pico-primary-hover-background: #33790f;
--pico-primary-hover-border: var(--pico-primary-hover-background);
--pico-primary-hover-underline: var(--pico-primary-hover);
--pico-primary-focus: rgba(71, 164, 23, 0.5);
--pico-primary-inverse: #fff;
--pico-secondary: #5d6b89;
--pico-secondary-background: #525f7a;
--pico-secondary-border: var(--pico-secondary-background);
--pico-secondary-underline: rgba(93, 107, 137, 0.5);
--pico-secondary-hover: #48536b;
--pico-secondary-hover-background: #48536b;
--pico-secondary-hover-border: var(--pico-secondary-hover-background);
--pico-secondary-hover-underline: var(--pico-secondary-hover);
--pico-secondary-focus: rgba(93, 107, 137, 0.25);
--pico-secondary-inverse: #fff;
--pico-contrast: #181c25;
--pico-contrast-background: #181c25;
--pico-contrast-border: var(--pico-contrast-background);
--pico-contrast-underline: rgba(24, 28, 37, 0.5);
--pico-contrast-hover: #000;
--pico-contrast-hover-background: #000;
--pico-contrast-hover-border: var(--pico-contrast-hover-background);
--pico-contrast-hover-underline: var(--pico-secondary-hover);
--pico-contrast-focus: rgba(93, 107, 137, 0.25);
--pico-contrast-inverse: #fff;
--pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(129, 145, 181, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(129, 145, 181, 0.024), 0.0625rem 0.125rem 0.75rem rgba(129, 145, 181, 0.03), 0.1125rem 0.225rem 1.35rem rgba(129, 145, 181, 0.036), 0.2085rem 0.417rem 2.502rem rgba(129, 145, 181, 0.04302), 0.5rem 1rem 6rem rgba(129, 145, 181, 0.06), 0 0 0 0.0625rem rgba(129, 145, 181, 0.015);
--pico-h1-color: #2d3138;
--pico-h2-color: #373c44;
--pico-h3-color: #424751;
--pico-h4-color: #4d535e;
--pico-h5-color: #5c6370;
--pico-h6-color: #646b79;
--pico-mark-background-color: #fde7c0;
--pico-mark-color: #0f1114;
--pico-ins-color: #1d6a54;
--pico-del-color: #883935;
--pico-blockquote-border-color: var(--pico-muted-border-color);
--pico-blockquote-footer-color: var(--pico-muted-color);
--pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-table-border-color: var(--pico-muted-border-color);
--pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375);
--pico-code-background-color: #f3f5f7;
--pico-code-color: #646b79;
--pico-code-kbd-background-color: var(--pico-color);
--pico-code-kbd-color: var(--pico-background-color);
--pico-form-element-background-color: #fbfcfc;
--pico-form-element-selected-background-color: #dfe3eb;
--pico-form-element-border-color: #cfd5e2;
--pico-form-element-color: #23262c;
--pico-form-element-placeholder-color: var(--pico-muted-color);
--pico-form-element-active-background-color: #fff;
--pico-form-element-active-border-color: var(--pico-primary-border);
--pico-form-element-focus-color: var(--pico-primary-border);
--pico-form-element-disabled-opacity: 0.5;
--pico-form-element-invalid-border-color: #b86a6b;
--pico-form-element-invalid-active-border-color: #c84f48;
--pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color);
--pico-form-element-valid-border-color: #4c9b8a;
--pico-form-element-valid-active-border-color: #279977;
--pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color);
--pico-switch-background-color: #bfc7d9;
--pico-switch-checked-background-color: var(--pico-primary-background);
--pico-switch-color: #fff;
--pico-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-range-border-color: #dfe3eb;
--pico-range-active-border-color: #bfc7d9;
--pico-range-thumb-border-color: var(--pico-background-color);
--pico-range-thumb-color: var(--pico-secondary-background);
--pico-range-thumb-active-color: var(--pico-primary-background);
--pico-accordion-border-color: var(--pico-muted-border-color);
--pico-accordion-active-summary-color: var(--pico-primary-hover);
--pico-accordion-close-summary-color: var(--pico-color);
--pico-accordion-open-summary-color: var(--pico-muted-color);
--pico-card-background-color: var(--pico-background-color);
--pico-card-border-color: var(--pico-muted-border-color);
--pico-card-box-shadow: var(--pico-box-shadow);
--pico-card-sectioning-background-color: #fbfcfc;
--pico-loading-spinner-opacity: 0.5;
--pico-modal-overlay-background-color: rgba(232, 234, 237, 0.75);
--pico-progress-background-color: #dfe3eb;
--pico-progress-color: var(--pico-primary-background);
--pico-tooltip-background-color: var(--pico-contrast-background);
--pico-tooltip-color: var(--pico-contrast-inverse);
--pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 155, 138)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
--pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200, 79, 72)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
color-scheme: light;
}
[data-theme=light] input:is([type=submit],
[type=button],
[type=reset],
[type=checkbox],
[type=radio],
[type=file]),
:root:not([data-theme=dark]) input:is([type=submit],
[type=button],
[type=reset],
[type=checkbox],
[type=radio],
[type=file]) {
--pico-form-element-focus-color: var(--pico-primary-focus);
}
@media only screen and (prefers-color-scheme: dark) {
:root:not([data-theme]) {
--pico-background-color: #13171f;
--pico-color: #c2c7d0;
--pico-text-selection-color: rgba(78, 179, 27, 0.1875);
--pico-muted-color: #7b8495;
--pico-muted-border-color: #202632;
--pico-primary: #4eb31b;
--pico-primary-background: #398712;
--pico-primary-border: var(--pico-primary-background);
--pico-primary-underline: rgba(78, 179, 27, 0.5);
--pico-primary-hover: #5dd121;
--pico-primary-hover-background: #409614;
--pico-primary-hover-border: var(--pico-primary-hover-background);
--pico-primary-hover-underline: var(--pico-primary-hover);
--pico-primary-focus: rgba(78, 179, 27, 0.375);
--pico-primary-inverse: #fff;
--pico-secondary: #969eaf;
--pico-secondary-background: #525f7a;
--pico-secondary-border: var(--pico-secondary-background);
--pico-secondary-underline: rgba(150, 158, 175, 0.5);
--pico-secondary-hover: #b3b9c5;
--pico-secondary-hover-background: #5d6b89;
--pico-secondary-hover-border: var(--pico-secondary-hover-background);
--pico-secondary-hover-underline: var(--pico-secondary-hover);
--pico-secondary-focus: rgba(144, 158, 190, 0.25);
--pico-secondary-inverse: #fff;
--pico-contrast: #dfe3eb;
--pico-contrast-background: #eff1f4;
--pico-contrast-border: var(--pico-contrast-background);
--pico-contrast-underline: rgba(223, 227, 235, 0.5);
--pico-contrast-hover: #fff;
--pico-contrast-hover-background: #fff;
--pico-contrast-hover-border: var(--pico-contrast-hover-background);
--pico-contrast-hover-underline: var(--pico-contrast-hover);
--pico-contrast-focus: rgba(207, 213, 226, 0.25);
--pico-contrast-inverse: #000;
--pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(7, 9, 12, 0.024), 0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba(7, 9, 12, 0.036), 0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302), 0.5rem 1rem 6rem rgba(7, 9, 12, 0.06), 0 0 0 0.0625rem rgba(7, 9, 12, 0.015);
--pico-h1-color: #f0f1f3;
--pico-h2-color: #e0e3e7;
--pico-h3-color: #c2c7d0;
--pico-h4-color: #b3b9c5;
--pico-h5-color: #a4acba;
--pico-h6-color: #8891a4;
--pico-mark-background-color: #014063;
--pico-mark-color: #fff;
--pico-ins-color: #62af9a;
--pico-del-color: #ce7e7b;
--pico-blockquote-border-color: var(--pico-muted-border-color);
--pico-blockquote-footer-color: var(--pico-muted-color);
--pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-table-border-color: var(--pico-muted-border-color);
--pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375);
--pico-code-background-color: #1a1f28;
--pico-code-color: #8891a4;
--pico-code-kbd-background-color: var(--pico-color);
--pico-code-kbd-color: var(--pico-background-color);
--pico-form-element-background-color: #1c212c;
--pico-form-element-selected-background-color: #2a3140;
--pico-form-element-border-color: #2a3140;
--pico-form-element-color: #e0e3e7;
--pico-form-element-placeholder-color: #8891a4;
--pico-form-element-active-background-color: #1a1f28;
--pico-form-element-active-border-color: var(--pico-primary-border);
--pico-form-element-focus-color: var(--pico-primary-border);
--pico-form-element-disabled-opacity: 0.5;
--pico-form-element-invalid-border-color: #964a50;
--pico-form-element-invalid-active-border-color: #b7403b;
--pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color);
--pico-form-element-valid-border-color: #2a7b6f;
--pico-form-element-valid-active-border-color: #16896a;
--pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color);
--pico-switch-background-color: #333c4e;
--pico-switch-checked-background-color: var(--pico-primary-background);
--pico-switch-color: #fff;
--pico-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-range-border-color: #202632;
--pico-range-active-border-color: #2a3140;
--pico-range-thumb-border-color: var(--pico-background-color);
--pico-range-thumb-color: var(--pico-secondary-background);
--pico-range-thumb-active-color: var(--pico-primary-background);
--pico-accordion-border-color: var(--pico-muted-border-color);
--pico-accordion-active-summary-color: var(--pico-primary-hover);
--pico-accordion-close-summary-color: var(--pico-color);
--pico-accordion-open-summary-color: var(--pico-muted-color);
--pico-card-background-color: #181c25;
--pico-card-border-color: var(--pico-card-background-color);
--pico-card-box-shadow: var(--pico-box-shadow);
--pico-card-sectioning-background-color: #1a1f28;
--pico-loading-spinner-opacity: 0.5;
--pico-modal-overlay-background-color: rgba(8, 9, 10, 0.75);
--pico-progress-background-color: #202632;
--pico-progress-color: var(--pico-primary-background);
--pico-tooltip-background-color: var(--pico-contrast-background);
--pico-tooltip-color: var(--pico-contrast-inverse);
--pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
--pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
color-scheme: dark;
}
:root:not([data-theme]) input:is([type=submit],
[type=button],
[type=reset],
[type=checkbox],
[type=radio],
[type=file]) {
--pico-form-element-focus-color: var(--pico-primary-focus);
}
}
[data-theme=dark] {
--pico-background-color: #13171f;
--pico-color: #c2c7d0;
--pico-text-selection-color: rgba(78, 179, 27, 0.1875);
--pico-muted-color: #7b8495;
--pico-muted-border-color: #202632;
--pico-primary: #4eb31b;
--pico-primary-background: #398712;
--pico-primary-border: var(--pico-primary-background);
--pico-primary-underline: rgba(78, 179, 27, 0.5);
--pico-primary-hover: #5dd121;
--pico-primary-hover-background: #409614;
--pico-primary-hover-border: var(--pico-primary-hover-background);
--pico-primary-hover-underline: var(--pico-primary-hover);
--pico-primary-focus: rgba(78, 179, 27, 0.375);
--pico-primary-inverse: #fff;
--pico-secondary: #969eaf;
--pico-secondary-background: #525f7a;
--pico-secondary-border: var(--pico-secondary-background);
--pico-secondary-underline: rgba(150, 158, 175, 0.5);
--pico-secondary-hover: #b3b9c5;
--pico-secondary-hover-background: #5d6b89;
--pico-secondary-hover-border: var(--pico-secondary-hover-background);
--pico-secondary-hover-underline: var(--pico-secondary-hover);
--pico-secondary-focus: rgba(144, 158, 190, 0.25);
--pico-secondary-inverse: #fff;
--pico-contrast: #dfe3eb;
--pico-contrast-background: #eff1f4;
--pico-contrast-border: var(--pico-contrast-background);
--pico-contrast-underline: rgba(223, 227, 235, 0.5);
--pico-contrast-hover: #fff;
--pico-contrast-hover-background: #fff;
--pico-contrast-hover-border: var(--pico-contrast-hover-background);
--pico-contrast-hover-underline: var(--pico-contrast-hover);
--pico-contrast-focus: rgba(207, 213, 226, 0.25);
--pico-contrast-inverse: #000;
--pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(7, 9, 12, 0.024), 0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba(7, 9, 12, 0.036), 0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302), 0.5rem 1rem 6rem rgba(7, 9, 12, 0.06), 0 0 0 0.0625rem rgba(7, 9, 12, 0.015);
--pico-h1-color: #f0f1f3;
--pico-h2-color: #e0e3e7;
--pico-h3-color: #c2c7d0;
--pico-h4-color: #b3b9c5;
--pico-h5-color: #a4acba;
--pico-h6-color: #8891a4;
--pico-mark-background-color: #014063;
--pico-mark-color: #fff;
--pico-ins-color: #62af9a;
--pico-del-color: #ce7e7b;
--pico-blockquote-border-color: var(--pico-muted-border-color);
--pico-blockquote-footer-color: var(--pico-muted-color);
--pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-table-border-color: var(--pico-muted-border-color);
--pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375);
--pico-code-background-color: #1a1f28;
--pico-code-color: #8891a4;
--pico-code-kbd-background-color: var(--pico-color);
--pico-code-kbd-color: var(--pico-background-color);
--pico-form-element-background-color: #1c212c;
--pico-form-element-selected-background-color: #2a3140;
--pico-form-element-border-color: #2a3140;
--pico-form-element-color: #e0e3e7;
--pico-form-element-placeholder-color: #8891a4;
--pico-form-element-active-background-color: #1a1f28;
--pico-form-element-active-border-color: var(--pico-primary-border);
--pico-form-element-focus-color: var(--pico-primary-border);
--pico-form-element-disabled-opacity: 0.5;
--pico-form-element-invalid-border-color: #964a50;
--pico-form-element-invalid-active-border-color: #b7403b;
--pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color);
--pico-form-element-valid-border-color: #2a7b6f;
--pico-form-element-valid-active-border-color: #16896a;
--pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color);
--pico-switch-background-color: #333c4e;
--pico-switch-checked-background-color: var(--pico-primary-background);
--pico-switch-color: #fff;
--pico-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
--pico-range-border-color: #202632;
--pico-range-active-border-color: #2a3140;
--pico-range-thumb-border-color: var(--pico-background-color);
--pico-range-thumb-color: var(--pico-secondary-background);
--pico-range-thumb-active-color: var(--pico-primary-background);
--pico-accordion-border-color: var(--pico-muted-border-color);
--pico-accordion-active-summary-color: var(--pico-primary-hover);
--pico-accordion-close-summary-color: var(--pico-color);
--pico-accordion-open-summary-color: var(--pico-muted-color);
--pico-card-background-color: #181c25;
--pico-card-border-color: var(--pico-card-background-color);
--pico-card-box-shadow: var(--pico-box-shadow);
--pico-card-sectioning-background-color: #1a1f28;
--pico-loading-spinner-opacity: 0.5;
--pico-modal-overlay-background-color: rgba(8, 9, 10, 0.75);
--pico-progress-background-color: #202632;
--pico-progress-color: var(--pico-primary-background);
--pico-tooltip-background-color: var(--pico-contrast-background);
--pico-tooltip-color: var(--pico-contrast-inverse);
--pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
--pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
color-scheme: dark;
}
[data-theme=dark] input:is([type=submit],
[type=button],
[type=reset],
[type=checkbox],
[type=radio],
[type=file]) {
--pico-form-element-focus-color: var(--pico-primary-focus);
}
progress,
[type=checkbox],
[type=radio],
[type=range] {
accent-color: var(--pico-primary);
}
/**
* Document
* Content-box & Responsive typography
*/
*,
*::before,
*::after {
box-sizing: border-box;
background-repeat: no-repeat;
}
::before,
::after {
text-decoration: inherit;
vertical-align: inherit;
}
:where(:root) {
-webkit-tap-highlight-color: transparent;
-webkit-text-size-adjust: 100%;
-moz-text-size-adjust: 100%;
text-size-adjust: 100%;
background-color: var(--pico-background-color);
color: var(--pico-color);
font-weight: var(--pico-font-weight);
font-size: var(--pico-font-size);
line-height: var(--pico-line-height);
font-family: var(--pico-font-family);
text-underline-offset: var(--pico-text-underline-offset);
text-rendering: optimizeLegibility;
overflow-wrap: break-word;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
}
/**
* Landmarks
*/
body {
width: 100%;
margin: 0;
}
main {
display: block;
}
body > header,
body > main,
body > footer {
width: 100%;
margin-right: auto;
margin-left: auto;
padding: var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal);
}
@media (min-width: 576px) {
body > header,
body > main,
body > footer {
max-width: 510px;
padding-right: 0;
padding-left: 0;
}
}
@media (min-width: 768px) {
body > header,
body > main,
body > footer {
max-width: 700px;
}
}
@media (min-width: 1024px) {
body > header,
body > main,
body > footer {
max-width: 950px;
}
}
@media (min-width: 1280px) {
body > header,
body > main,
body > footer {
max-width: 1200px;
}
}
@media (min-width: 1536px) {
body > header,
body > main,
body > footer {
max-width: 1450px;
}
}
/**
* Section
*/
section {
margin-bottom: var(--pico-block-spacing-vertical);
}
/**
* Typography
*/
b,
strong {
font-weight: bolder;
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
address,
blockquote,
dl,
ol,
p,
pre,
table,
ul {
margin-top: 0;
margin-bottom: var(--pico-typography-spacing-vertical);
color: var(--pico-color);
font-style: normal;
font-weight: var(--pico-font-weight);
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0;
margin-bottom: var(--pico-typography-spacing-vertical);
color: var(--pico-color);
font-weight: var(--pico-font-weight);
font-size: var(--pico-font-size);
line-height: var(--pico-line-height);
font-family: var(--pico-font-family);
}
h1 {
--pico-color: var(--pico-h1-color);
}
h2 {
--pico-color: var(--pico-h2-color);
}
h3 {
--pico-color: var(--pico-h3-color);
}
h4 {
--pico-color: var(--pico-h4-color);
}
h5 {
--pico-color: var(--pico-h5-color);
}
h6 {
--pico-color: var(--pico-h6-color);
}
:where(article, address, blockquote, dl, figure, form, ol, p, pre, table, ul) ~ :is(h1, h2, h3, h4, h5, h6) {
margin-top: var(--pico-typography-spacing-top);
}
p {
margin-bottom: var(--pico-typography-spacing-vertical);
}
hgroup {
margin-bottom: var(--pico-typography-spacing-vertical);
}
hgroup > * {
margin-top: 0;
margin-bottom: 0;
}
hgroup > *:not(:first-child):last-child {
--pico-color: var(--pico-muted-color);
--pico-font-weight: unset;
font-size: 1rem;
}
:where(ol, ul) li {
margin-bottom: calc(var(--pico-typography-spacing-vertical) * 0.25);
}
:where(dl, ol, ul) :where(dl, ol, ul) {
margin: 0;
margin-top: calc(var(--pico-typography-spacing-vertical) * 0.25);
}
ul li {
list-style: square;
}
mark {
padding: 0.125rem 0.25rem;
background-color: var(--pico-mark-background-color);
color: var(--pico-mark-color);
vertical-align: baseline;
}
blockquote {
display: block;
margin: var(--pico-typography-spacing-vertical) 0;
padding: var(--pico-spacing);
border-right: none;
border-left: 0.25rem solid var(--pico-blockquote-border-color);
border-inline-start: 0.25rem solid var(--pico-blockquote-border-color);
border-inline-end: none;
}
blockquote footer {
margin-top: calc(var(--pico-typography-spacing-vertical) * 0.5);
color: var(--pico-blockquote-footer-color);
}
abbr[title] {
border-bottom: 1px dotted;
text-decoration: none;
cursor: help;
}
ins {
color: var(--pico-ins-color);
text-decoration: none;
}
del {
color: var(--pico-del-color);
}
::-moz-selection {
background-color: var(--pico-text-selection-color);
}
::selection {
background-color: var(--pico-text-selection-color);
}
/**
* Link
*/
:where(a:not([role=button])),
[role=link] {
--pico-color: var(--pico-primary);
--pico-background-color: transparent;
--pico-underline: var(--pico-primary-underline);
outline: none;
background-color: var(--pico-background-color);
color: var(--pico-color);
-webkit-text-decoration: var(--pico-text-decoration);
text-decoration: var(--pico-text-decoration);
text-decoration-color: var(--pico-underline);
text-underline-offset: 0.125em;
transition: background-color var(--pico-transition), color var(--pico-transition), box-shadow var(--pico-transition), -webkit-text-decoration var(--pico-transition);
transition: background-color var(--pico-transition), color var(--pico-transition), text-decoration var(--pico-transition), box-shadow var(--pico-transition);
transition: background-color var(--pico-transition), color var(--pico-transition), text-decoration var(--pico-transition), box-shadow var(--pico-transition), -webkit-text-decoration var(--pico-transition);
}
:where(a:not([role=button])):is([aria-current]:not([aria-current=false]), :hover, :active, :focus),
[role=link]:is([aria-current]:not([aria-current=false]), :hover, :active, :focus) {
--pico-color: var(--pico-primary-hover);
--pico-underline: var(--pico-primary-hover-underline);
--pico-text-decoration: underline;
}
:where(a:not([role=button])):focus-visible,
[role=link]:focus-visible {
box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-primary-focus);
}
a[role=button] {
display: inline-block;
}
/**
* Button
*/
button {
margin: 0;
overflow: visible;
font-family: inherit;
text-transform: none;
}
button,
[type=submit],
[type=reset],
[type=button] {
-webkit-appearance: button;
}
button,
[type=submit],
[type=reset],
[type=button],
[type=file]::file-selector-button,
[role=button] {
--pico-background-color: var(--pico-primary-background);
--pico-border-color: var(--pico-primary-border);
--pico-color: var(--pico-primary-inverse);
--pico-box-shadow: var(--pico-button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));
padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);
border: var(--pico-border-width) solid var(--pico-border-color);
border-radius: var(--pico-border-radius);
outline: none;
background-color: var(--pico-background-color);
box-shadow: var(--pico-box-shadow);
color: var(--pico-color);
font-weight: var(--pico-font-weight);
font-size: 1rem;
line-height: var(--pico-line-height);
text-align: center;
text-decoration: none;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
transition: background-color var(--pico-transition), border-color var(--pico-transition), color var(--pico-transition), box-shadow var(--pico-transition);
}
button:is([aria-current]:not([aria-current=false])), button:is(:hover, :active, :focus),
[type=submit]:is([aria-current]:not([aria-current=false])),
[type=submit]:is(:hover, :active, :focus),
[type=reset]:is([aria-current]:not([aria-current=false])),
[type=reset]:is(:hover, :active, :focus),
[type=button]:is([aria-current]:not([aria-current=false])),
[type=button]:is(:hover, :active, :focus),
[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])),
[type=file]::file-selector-button:is(:hover, :active, :focus),
[role=button]:is([aria-current]:not([aria-current=false])),
[role=button]:is(:hover, :active, :focus) {
--pico-background-color: var(--pico-primary-hover-background);
--pico-border-color: var(--pico-primary-hover-border);
--pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));
--pico-color: var(--pico-primary-inverse);
}
button:focus, button:is([aria-current]:not([aria-current=false])):focus,
[type=submit]:focus,
[type=submit]:is([aria-current]:not([aria-current=false])):focus,
[type=reset]:focus,
[type=reset]:is([aria-current]:not([aria-current=false])):focus,
[type=button]:focus,
[type=button]:is([aria-current]:not([aria-current=false])):focus,
[type=file]::file-selector-button:focus,
[type=file]::file-selector-button:is([aria-current]:not([aria-current=false])):focus,
[role=button]:focus,
[role=button]:is([aria-current]:not([aria-current=false])):focus {
--pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--pico-outline-width) var(--pico-primary-focus);
}
[type=submit],
[type=reset],
[type=button] {
margin-bottom: var(--pico-spacing);
}
[type=reset],
[type=file]::file-selector-button {
--pico-background-color: var(--pico-secondary-background);
--pico-border-color: var(--pico-secondary-border);
--pico-color: var(--pico-secondary-inverse);
cursor: pointer;
}
[type=reset]:is([aria-current]:not([aria-current=false]), :hover, :active, :focus),
[type=file]::file-selector-button:is([aria-current]:not([aria-current=false]), :hover, :active, :focus) {
--pico-background-color: var(--pico-secondary-hover-background);
--pico-border-color: var(--pico-secondary-hover-border);
--pico-color: var(--pico-secondary-inverse);
}
[type=reset]:focus,
[type=file]::file-selector-button:focus {
--pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--pico-outline-width) var(--pico-secondary-focus);
}
:where(button, [type=submit], [type=reset], [type=button], [role=button])[disabled],
:where(fieldset[disabled]) :is(button, [type=submit], [type=button], [type=reset], [role=button]) {
opacity: 0.5;
pointer-events: none;
}
/**
* Table
*/
:where(table) {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
text-indent: 0;
}
th,
td {
padding: calc(var(--pico-spacing) / 2) var(--pico-spacing);
border-bottom: var(--pico-border-width) solid var(--pico-table-border-color);
background-color: var(--pico-background-color);
color: var(--pico-color);
font-weight: var(--pico-font-weight);
text-align: left;
text-align: start;
}
tfoot th,
tfoot td {
border-top: var(--pico-border-width) solid var(--pico-table-border-color);
border-bottom: 0;
}
table.striped tbody tr:nth-child(odd) th,
table.striped tbody tr:nth-child(odd) td {
background-color: var(--pico-table-row-stripped-background-color);
}
/**
* Embedded content
*/
:where(audio, canvas, iframe, img, svg, video) {
vertical-align: middle;
}
audio,
video {
display: inline-block;
}
audio:not([controls]) {
display: none;
height: 0;
}
:where(iframe) {
border-style: none;
}
img {
max-width: 100%;
height: auto;
border-style: none;
}
:where(svg:not([fill])) {
fill: currentColor;
}
svg:not(:root) {
overflow: hidden;
}
/**
* Code
*/
pre,
code,
kbd,
samp {
font-size: 0.875em;
font-family: var(--pico-font-family);
}
pre code {
font-size: inherit;
font-family: inherit;
}
pre {
-ms-overflow-style: scrollbar;
overflow: auto;
}
pre,
code,
kbd {
border-radius: var(--pico-border-radius);
background: var(--pico-code-background-color);
color: var(--pico-code-color);
font-weight: var(--pico-font-weight);
line-height: initial;
}
code,
kbd {
display: inline-block;
padding: 0.375rem;
}
pre {
display: block;
margin-bottom: var(--pico-spacing);
overflow-x: auto;
}
pre > code {
display: block;
padding: var(--pico-spacing);
background: none;
line-height: var(--pico-line-height);
}
kbd {
background-color: var(--pico-code-kbd-background-color);
color: var(--pico-code-kbd-color);
vertical-align: baseline;
}
/**
* Figure
*/
figure {
display: block;
margin: 0;
padding: 0;
}
figure figcaption {
padding: calc(var(--pico-spacing) * 0.5) 0;
color: var(--pico-muted-color);
}
/**
* Miscs
*/
hr {
height: 0;
margin: var(--pico-typography-spacing-vertical) 0;
border: 0;
border-top: 1px solid var(--pico-muted-border-color);
color: inherit;
}
[hidden],
template {
display: none !important;
}
canvas {
display: inline-block;
}
/**
* Basics form elements
*/
input,
optgroup,
select,
textarea {
margin: 0;
font-size: 1rem;
line-height: var(--pico-line-height);
font-family: inherit;
letter-spacing: inherit;
}
input {
overflow: visible;
}
select {
text-transform: none;
}
legend {
max-width: 100%;
padding: 0;
color: inherit;
white-space: normal;
}
textarea {
overflow: auto;
}
[type=checkbox],
[type=radio] {
padding: 0;
}
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type=search]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
:-moz-focusring {
outline: none;
}
:-moz-ui-invalid {
box-shadow: none;
}
::-ms-expand {
display: none;
}
[type=file],
[type=range] {
padding: 0;
border-width: 0;
}
input:not([type=checkbox], [type=radio], [type=range]) {
height: calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2);
}
fieldset {
width: 100%;
margin: 0;
margin-bottom: var(--pico-spacing);
padding: 0;
border: 0;
}
label,
fieldset legend {
display: block;
margin-bottom: calc(var(--pico-spacing) * 0.375);
color: var(--pico-color);
font-weight: var(--pico-form-label-font-weight, var(--pico-font-weight));
}
fieldset legend {
margin-bottom: calc(var(--pico-spacing) * 0.5);
}
input:not([type=checkbox], [type=radio]),
button[type=submit],
select,
textarea {
width: 100%;
}
input:not([type=checkbox], [type=radio], [type=range], [type=file]),
select,
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);
}
input,
select,
textarea {
--pico-background-color: var(--pico-form-element-background-color);
--pico-border-color: var(--pico-form-element-border-color);
--pico-color: var(--pico-form-element-color);
--pico-box-shadow: none;
border: var(--pico-border-width) solid var(--pico-border-color);
border-radius: var(--pico-border-radius);
outline: none;
background-color: var(--pico-background-color);
box-shadow: var(--pico-box-shadow);
color: var(--pico-color);
font-weight: var(--pico-font-weight);
transition: background-color var(--pico-transition), border-color var(--pico-transition), color var(--pico-transition), box-shadow var(--pico-transition);
}
input:not([type=submit],
[type=button],
[type=reset],
[type=checkbox],
[type=radio],
[readonly]):is(:active, :focus),
:where(select, textarea):not([readonly]):is(:active, :focus) {
--pico-background-color: var(--pico-form-element-active-background-color);
}
input:not([type=submit], [type=button], [type=reset], [role=switch], [readonly]):is(:active, :focus),
:where(select, textarea):not([readonly]):is(:active, :focus) {
--pico-border-color: var(--pico-form-element-active-border-color);
}
input:not([type=submit],
[type=button],
[type=reset],
[type=range],
[type=file],
[readonly]):focus,
:where(select, textarea):not([readonly]):focus {
--pico-box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-form-element-focus-color);
}
input:not([type=submit], [type=button], [type=reset])[disabled],
select[disabled],
textarea[disabled],
label[aria-disabled=true],
:where(fieldset[disabled]) :is(input:not([type=submit], [type=button], [type=reset]), select, textarea) {
opacity: var(--pico-form-element-disabled-opacity);
pointer-events: none;
}
label[aria-disabled=true] input[disabled] {
opacity: 1;
}
:where(input, select, textarea):not([type=checkbox],
[type=radio],
[type=date],
[type=datetime-local],
[type=month],
[type=time],
[type=week],
[type=range])[aria-invalid] {
padding-right: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem) !important;
padding-left: var(--pico-form-element-spacing-horizontal);
padding-inline-start: var(--pico-form-element-spacing-horizontal) !important;
padding-inline-end: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem) !important;
background-position: center right 0.75rem;
background-size: 1rem auto;
background-repeat: no-repeat;
}
:where(input, select, textarea):not([type=checkbox],
[type=radio],
[type=date],
[type=datetime-local],
[type=month],
[type=time],
[type=week],
[type=range])[aria-invalid=false]:not(select) {
background-image: var(--pico-icon-valid);
}
:where(input, select, textarea):not([type=checkbox],
[type=radio],
[type=date],
[type=datetime-local],
[type=month],
[type=time],
[type=week],
[type=range])[aria-invalid=true]:not(select) {
background-image: var(--pico-icon-invalid);
}
:where(input, select, textarea)[aria-invalid=false] {
--pico-border-color: var(--pico-form-element-valid-border-color);
}
:where(input, select, textarea)[aria-invalid=false]:is(:active, :focus) {
--pico-border-color: var(--pico-form-element-valid-active-border-color) !important;
}
:where(input, select, textarea)[aria-invalid=false]:is(:active, :focus):not([type=checkbox], [type=radio]) {
--pico-box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-form-element-valid-focus-color) !important;
}
:where(input, select, textarea)[aria-invalid=true] {
--pico-border-color: var(--pico-form-element-invalid-border-color);
}
:where(input, select, textarea)[aria-invalid=true]:is(:active, :focus) {
--pico-border-color: var(--pico-form-element-invalid-active-border-color) !important;
}
:where(input, select, textarea)[aria-invalid=true]:is(:active, :focus):not([type=checkbox], [type=radio]) {
--pico-box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-form-element-invalid-focus-color) !important;
}
[dir=rtl] :where(input, select, textarea):not([type=checkbox], [type=radio]):is([aria-invalid], [aria-invalid=true], [aria-invalid=false]) {
background-position: center left 0.75rem;
}
input::placeholder,
input::-webkit-input-placeholder,
textarea::placeholder,
textarea::-webkit-input-placeholder,
select:invalid {
color: var(--pico-form-element-placeholder-color);
opacity: 1;
}
input:not([type=checkbox], [type=radio]),
select,
textarea {
margin-bottom: var(--pico-spacing);
}
select::-ms-expand {
border: 0;
background-color: transparent;
}
select:not([multiple], [size]) {
padding-right: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem);
padding-left: var(--pico-form-element-spacing-horizontal);
padding-inline-start: var(--pico-form-element-spacing-horizontal);
padding-inline-end: calc(var(--pico-form-element-spacing-horizontal) + 1.5rem);
background-image: var(--pico-icon-chevron);
background-position: center right 0.75rem;
background-size: 1rem auto;
background-repeat: no-repeat;
}
select[multiple] option:checked {
background: var(--pico-form-element-selected-background-color);
color: var(--pico-form-element-color);
}
[dir=rtl] select:not([multiple], [size]) {
background-position: center left 0.75rem;
}
textarea {
display: block;
resize: vertical;
}
textarea[aria-invalid] {
--pico-icon-height: calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2);
background-position: top right 0.75rem !important;
background-size: 1rem var(--pico-icon-height) !important;
}
:where(input, select, textarea, fieldset) + small {
display: block;
width: 100%;
margin-top: calc(var(--pico-spacing) * -0.75);
margin-bottom: var(--pico-spacing);
color: var(--pico-muted-color);
}
:where(input, select, textarea, fieldset)[aria-invalid=false] + small {
color: var(--pico-ins-color);
}
:where(input, select, textarea, fieldset)[aria-invalid=true] + small {
color: var(--pico-del-color);
}
label > :where(input, select, textarea) {
margin-top: calc(var(--pico-spacing) * 0.25);
}
/**
* Checkboxes, Radios and Switches
*/
label:has([type=checkbox], [type=radio]) {
width: -moz-fit-content;
width: fit-content;
cursor: pointer;
}
[type=checkbox],
[type=radio] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 1.25em;
height: 1.25em;
margin-top: -0.125em;
margin-inline-end: 0.5em;
border-width: var(--pico-border-width);
vertical-align: middle;
cursor: pointer;
}
[type=checkbox]::-ms-check,
[type=radio]::-ms-check {
display: none;
}
[type=checkbox]:checked, [type=checkbox]:checked:active, [type=checkbox]:checked:focus,
[type=radio]:checked,
[type=radio]:checked:active,
[type=radio]:checked:focus {
--pico-background-color: var(--pico-primary-background);
--pico-border-color: var(--pico-primary-border);
background-image: var(--pico-icon-checkbox);
background-position: center;
background-size: 0.75em auto;
background-repeat: no-repeat;
}
[type=checkbox] ~ label,
[type=radio] ~ label {
display: inline-block;
margin-bottom: 0;
cursor: pointer;
}
[type=checkbox] ~ label:not(:last-of-type),
[type=radio] ~ label:not(:last-of-type) {
margin-inline-end: 1em;
}
[type=checkbox]:indeterminate {
--pico-background-color: var(--pico-primary-background);
--pico-border-color: var(--pico-primary-border);
background-image: var(--pico-icon-minus);
background-position: center;
background-size: 0.75em auto;
background-repeat: no-repeat;
}
[type=radio] {
border-radius: 50%;
}
[type=radio]:checked, [type=radio]:checked:active, [type=radio]:checked:focus {
--pico-background-color: var(--pico-primary-inverse);
border-width: 0.35em;
background-image: none;
}
[type=checkbox][role=switch] {
--pico-background-color: var(--pico-switch-background-color);
--pico-color: var(--pico-switch-color);
width: 2.25em;
height: 1.25em;
border: var(--pico-border-width) solid var(--pico-border-color);
border-radius: 1.25em;
background-color: var(--pico-background-color);
line-height: 1.25em;
}
[type=checkbox][role=switch]:not([aria-invalid]) {
--pico-border-color: var(--pico-switch-background-color);
}
[type=checkbox][role=switch]:before {
display: block;
aspect-ratio: 1;
height: 100%;
border-radius: 50%;
background-color: var(--pico-color);
box-shadow: var(--pico-switch-thumb-box-shadow);
content: "";
transition: margin 0.1s ease-in-out;
}
[type=checkbox][role=switch]:focus {
--pico-background-color: var(--pico-switch-background-color);
--pico-border-color: var(--pico-switch-background-color);
}
[type=checkbox][role=switch]:checked {
--pico-background-color: var(--pico-switch-checked-background-color);
--pico-border-color: var(--pico-switch-checked-background-color);
background-image: none;
}
[type=checkbox][role=switch]:checked::before {
margin-inline-start: calc(2.25em - 1.25em);
}
[type=checkbox][role=switch][disabled] {
--pico-background-color: var(--pico-border-color);
}
[type=checkbox][aria-invalid=false]:checked, [type=checkbox][aria-invalid=false]:checked:active, [type=checkbox][aria-invalid=false]:checked:focus,
[type=checkbox][role=switch][aria-invalid=false]:checked,
[type=checkbox][role=switch][aria-invalid=false]:checked:active,
[type=checkbox][role=switch][aria-invalid=false]:checked:focus {
--pico-background-color: var(--pico-form-element-valid-border-color);
}
[type=checkbox]:checked[aria-invalid=true], [type=checkbox]:checked:active[aria-invalid=true], [type=checkbox]:checked:focus[aria-invalid=true],
[type=checkbox][role=switch]:checked[aria-invalid=true],
[type=checkbox][role=switch]:checked:active[aria-invalid=true],
[type=checkbox][role=switch]:checked:focus[aria-invalid=true] {
--pico-background-color: var(--pico-form-element-invalid-border-color);
}
[type=checkbox][aria-invalid=false]:checked, [type=checkbox][aria-invalid=false]:checked:active, [type=checkbox][aria-invalid=false]:checked:focus,
[type=radio][aria-invalid=false]:checked,
[type=radio][aria-invalid=false]:checked:active,
[type=radio][aria-invalid=false]:checked:focus,
[type=checkbox][role=switch][aria-invalid=false]:checked,
[type=checkbox][role=switch][aria-invalid=false]:checked:active,
[type=checkbox][role=switch][aria-invalid=false]:checked:focus {
--pico-border-color: var(--pico-form-element-valid-border-color);
}
[type=checkbox]:checked[aria-invalid=true], [type=checkbox]:checked:active[aria-invalid=true], [type=checkbox]:checked:focus[aria-invalid=true],
[type=radio]:checked[aria-invalid=true],
[type=radio]:checked:active[aria-invalid=true],
[type=radio]:checked:focus[aria-invalid=true],
[type=checkbox][role=switch]:checked[aria-invalid=true],
[type=checkbox][role=switch]:checked:active[aria-invalid=true],
[type=checkbox][role=switch]:checked:focus[aria-invalid=true] {
--pico-border-color: var(--pico-form-element-invalid-border-color);
}
/**
* Input type color
*/
[type=color]::-webkit-color-swatch-wrapper {
padding: 0;
}
[type=color]::-moz-focus-inner {
padding: 0;
}
[type=color]::-webkit-color-swatch {
border: 0;
border-radius: calc(var(--pico-border-radius) * 0.5);
}
[type=color]::-moz-color-swatch {
border: 0;
border-radius: calc(var(--pico-border-radius) * 0.5);
}
/**
* Input type datetime
*/
input:not([type=checkbox], [type=radio], [type=range], [type=file]):is([type=date], [type=datetime-local], [type=month], [type=time], [type=week]) {
--pico-icon-position: 0.75rem;
--pico-icon-width: 1rem;
padding-right: calc(var(--pico-icon-width) + var(--pico-icon-position));
background-image: var(--pico-icon-date);
background-position: center right var(--pico-icon-position);
background-size: var(--pico-icon-width) auto;
background-repeat: no-repeat;
}
input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=time] {
background-image: var(--pico-icon-time);
}
[type=date]::-webkit-calendar-picker-indicator,
[type=datetime-local]::-webkit-calendar-picker-indicator,
[type=month]::-webkit-calendar-picker-indicator,
[type=time]::-webkit-calendar-picker-indicator,
[type=week]::-webkit-calendar-picker-indicator {
width: var(--pico-icon-width);
margin-right: calc(var(--pico-icon-width) * -1);
margin-left: var(--pico-icon-position);
opacity: 0;
}
@-moz-document url-prefix() {
[type=date],
[type=datetime-local],
[type=month],
[type=time],
[type=week] {
padding-right: var(--pico-form-element-spacing-horizontal) !important;
background-image: none !important;
}
}
[dir=rtl] :is([type=date], [type=datetime-local], [type=month], [type=time], [type=week]) {
text-align: right;
}
/**
* Input type file
*/
[type=file] {
--pico-color: var(--pico-muted-color);
margin-left: calc(var(--pico-outline-width) * -1);
padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) 0;
padding-left: var(--pico-outline-width);
border: 0;
border-radius: 0;
background: none;
}
[type=file]::file-selector-button {
margin-right: calc(var(--pico-spacing) / 2);
padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) var(--pico-form-element-spacing-horizontal);
}
[type=file]:is(:hover, :active, :focus)::file-selector-button {
--pico-background-color: var(--pico-secondary-hover-background);
--pico-border-color: var(--pico-secondary-hover-border);
}
[type=file]:focus::file-selector-button {
--pico-box-shadow: var(--pico-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--pico-outline-width) var(--pico-secondary-focus);
}
/**
* Input type range
*/
[type=range] {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 100%;
height: 1.25rem;
background: none;
}
[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 0.375rem;
border-radius: var(--pico-border-radius);
background-color: var(--pico-range-border-color);
-webkit-transition: background-color var(--pico-transition), box-shadow var(--pico-transition);
transition: background-color var(--pico-transition), box-shadow var(--pico-transition);
}
[type=range]::-moz-range-track {
width: 100%;
height: 0.375rem;
border-radius: var(--pico-border-radius);
background-color: var(--pico-range-border-color);
-moz-transition: background-color var(--pico-transition), box-shadow var(--pico-transition);
transition: background-color var(--pico-transition), box-shadow var(--pico-transition);
}
[type=range]::-ms-track {
width: 100%;
height: 0.375rem;
border-radius: var(--pico-border-radius);
background-color: var(--pico-range-border-color);
-ms-transition: background-color var(--pico-transition), box-shadow var(--pico-transition);
transition: background-color var(--pico-transition), box-shadow var(--pico-transition);
}
[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 1.25rem;
height: 1.25rem;
margin-top: -0.4375rem;
border: 2px solid var(--pico-range-thumb-border-color);
border-radius: 50%;
background-color: var(--pico-range-thumb-color);
cursor: pointer;
-webkit-transition: background-color var(--pico-transition), transform var(--pico-transition);
transition: background-color var(--pico-transition), transform var(--pico-transition);
}
[type=range]::-moz-range-thumb {
-webkit-appearance: none;
width: 1.25rem;
height: 1.25rem;
margin-top: -0.4375rem;
border: 2px solid var(--pico-range-thumb-border-color);
border-radius: 50%;
background-color: var(--pico-range-thumb-color);
cursor: pointer;
-moz-transition: background-color var(--pico-transition), transform var(--pico-transition);
transition: background-color var(--pico-transition), transform var(--pico-transition);
}
[type=range]::-ms-thumb {
-webkit-appearance: none;
width: 1.25rem;
height: 1.25rem;
margin-top: -0.4375rem;
border: 2px solid var(--pico-range-thumb-border-color);
border-radius: 50%;
background-color: var(--pico-range-thumb-color);
cursor: pointer;
-ms-transition: background-color var(--pico-transition), transform var(--pico-transition);
transition: background-color var(--pico-transition), transform var(--pico-transition);
}
[type=range]:active, [type=range]:focus-within {
--pico-range-border-color: var(--pico-range-active-border-color);
--pico-range-thumb-color: var(--pico-range-thumb-active-color);
}
[type=range]:active::-webkit-slider-thumb {
transform: scale(1.25);
}
[type=range]:active::-moz-range-thumb {
transform: scale(1.25);
}
[type=range]:active::-ms-thumb {
transform: scale(1.25);
}
/**
* Input type search
*/
input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search] {
padding-inline-start: calc(var(--pico-form-element-spacing-horizontal) + 1.75rem);
background-image: var(--pico-icon-search);
background-position: center left calc(var(--pico-form-element-spacing-horizontal) + 0.125rem);
background-size: 1rem auto;
background-repeat: no-repeat;
}
input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid] {
padding-inline-start: calc(var(--pico-form-element-spacing-horizontal) + 1.75rem) !important;
background-position: center left 1.125rem, center right 0.75rem;
}
input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid=false] {
background-image: var(--pico-icon-search), var(--pico-icon-valid);
}
input:not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid=true] {
background-image: var(--pico-icon-search), var(--pico-icon-invalid);
}
[dir=rtl] :where(input):not([type=checkbox], [type=radio], [type=range], [type=file])[type=search] {
background-position: center right 1.125rem;
}
[dir=rtl] :where(input):not([type=checkbox], [type=radio], [type=range], [type=file])[type=search][aria-invalid] {
background-position: center right 1.125rem, center left 0.75rem;
}
/**
* Accordion ()
*/
details {
display: block;
margin-bottom: var(--pico-spacing);
}
details summary {
line-height: 1rem;
list-style-type: none;
cursor: pointer;
transition: color var(--pico-transition);
}
details summary:not([role]) {
color: var(--pico-accordion-close-summary-color);
}
details summary::-webkit-details-marker {
display: none;
}
details summary::marker {
display: none;
}
details summary::-moz-list-bullet {
list-style-type: none;
}
details summary::after {
display: block;
width: 1rem;
height: 1rem;
margin-inline-start: calc(var(--pico-spacing, 1rem) * 0.5);
float: right;
transform: rotate(-90deg);
background-image: var(--pico-icon-chevron);
background-position: right center;
background-size: 1rem auto;
background-repeat: no-repeat;
content: "";
transition: transform var(--pico-transition);
}
details summary:focus {
outline: none;
}
details summary:focus:not([role]) {
color: var(--pico-accordion-active-summary-color);
}
details summary:focus-visible:not([role]) {
outline: var(--pico-outline-width) solid var(--pico-primary-focus);
outline-offset: calc(var(--pico-spacing, 1rem) * 0.5);
color: var(--pico-primary);
}
details summary[role=button] {
width: 100%;
text-align: left;
}
details summary[role=button]::after {
height: calc(1rem * var(--pico-line-height, 1.5));
}
details[open] > summary {
margin-bottom: var(--pico-spacing);
}
details[open] > summary:not([role]):not(:focus) {
color: var(--pico-accordion-open-summary-color);
}
details[open] > summary::after {
transform: rotate(0);
}
[dir=rtl] details summary {
text-align: right;
}
[dir=rtl] details summary::after {
float: left;
background-position: left center;
}
/**
* Card ()
*/
article {
margin-bottom: var(--pico-block-spacing-vertical);
padding: var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal);
border-radius: var(--pico-border-radius);
background: var(--pico-card-background-color);
box-shadow: var(--pico-card-box-shadow);
}
article > header,
article > footer {
margin-right: calc(var(--pico-block-spacing-horizontal) * -1);
margin-left: calc(var(--pico-block-spacing-horizontal) * -1);
padding: calc(var(--pico-block-spacing-vertical) * 0.66) var(--pico-block-spacing-horizontal);
background-color: var(--pico-card-sectioning-background-color);
}
article > header {
margin-top: calc(var(--pico-block-spacing-vertical) * -1);
margin-bottom: var(--pico-block-spacing-vertical);
border-bottom: var(--pico-border-width) solid var(--pico-card-border-color);
border-top-right-radius: var(--pico-border-radius);
border-top-left-radius: var(--pico-border-radius);
}
article > footer {
margin-top: var(--pico-block-spacing-vertical);
margin-bottom: calc(var(--pico-block-spacing-vertical) * -1);
border-top: var(--pico-border-width) solid var(--pico-card-border-color);
border-bottom-right-radius: var(--pico-border-radius);
border-bottom-left-radius: var(--pico-border-radius);
}
/**
* Group ([role="group"], [role="search"])
*/
[role=search],
[role=group] {
display: inline-flex;
position: relative;
width: 100%;
margin-bottom: var(--pico-spacing);
border-radius: var(--pico-border-radius);
box-shadow: var(--pico-group-box-shadow, 0 0 0 rgba(0, 0, 0, 0));
vertical-align: middle;
transition: box-shadow var(--pico-transition);
}
[role=search] > *,
[role=search] input:not([type=checkbox], [type=radio]),
[role=search] select,
[role=group] > *,
[role=group] input:not([type=checkbox], [type=radio]),
[role=group] select {
position: relative;
flex: 1 1 auto;
margin-bottom: 0;
}
[role=search] > *:not(:first-child),
[role=search] input:not([type=checkbox], [type=radio]):not(:first-child),
[role=search] select:not(:first-child),
[role=group] > *:not(:first-child),
[role=group] input:not([type=checkbox], [type=radio]):not(:first-child),
[role=group] select:not(:first-child) {
margin-left: 0;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
[role=search] > *:not(:last-child),
[role=search] input:not([type=checkbox], [type=radio]):not(:last-child),
[role=search] select:not(:last-child),
[role=group] > *:not(:last-child),
[role=group] input:not([type=checkbox], [type=radio]):not(:last-child),
[role=group] select:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
[role=search] > *:focus,
[role=search] input:not([type=checkbox], [type=radio]):focus,
[role=search] select:focus,
[role=group] > *:focus,
[role=group] input:not([type=checkbox], [type=radio]):focus,
[role=group] select:focus {
z-index: 2;
}
[role=search] button:not(:first-child),
[role=search] [type=submit]:not(:first-child),
[role=search] [type=reset]:not(:first-child),
[role=search] [type=button]:not(:first-child),
[role=search] [role=button]:not(:first-child),
[role=search] input:not([type=checkbox], [type=radio]):not(:first-child),
[role=search] select:not(:first-child),
[role=group] button:not(:first-child),
[role=group] [type=submit]:not(:first-child),
[role=group] [type=reset]:not(:first-child),
[role=group] [type=button]:not(:first-child),
[role=group] [role=button]:not(:first-child),
[role=group] input:not([type=checkbox], [type=radio]):not(:first-child),
[role=group] select:not(:first-child) {
margin-left: calc(var(--pico-border-width) * -1);
}
[role=search] button,
[role=search] [type=submit],
[role=search] [type=reset],
[role=search] [type=button],
[role=search] [role=button],
[role=group] button,
[role=group] [type=submit],
[role=group] [type=reset],
[role=group] [type=button],
[role=group] [role=button] {
width: auto;
}
@supports selector(:has(*)) {
[role=search]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus),
[role=group]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) {
--pico-group-box-shadow: var(--pico-group-box-shadow-focus-with-button);
}
[role=search]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) input:not([type=checkbox], [type=radio]),
[role=search]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) select,
[role=group]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) input:not([type=checkbox], [type=radio]),
[role=group]:has(button:focus, [type=submit]:focus, [type=button]:focus, [role=button]:focus) select {
border-color: transparent;
}
[role=search]:has(input:not([type=submit], [type=button]):focus, select:focus),
[role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) {
--pico-group-box-shadow: var(--pico-group-box-shadow-focus-with-input);
}
[role=search]:has(input:not([type=submit], [type=button]):focus, select:focus) button,
[role=search]:has(input:not([type=submit], [type=button]):focus, select:focus) [type=submit],
[role=search]:has(input:not([type=submit], [type=button]):focus, select:focus) [type=button],
[role=search]:has(input:not([type=submit], [type=button]):focus, select:focus) [role=button],
[role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) button,
[role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) [type=submit],
[role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) [type=button],
[role=group]:has(input:not([type=submit], [type=button]):focus, select:focus) [role=button] {
--pico-button-box-shadow: 0 0 0 var(--pico-border-width) var(--pico-primary-border);
--pico-button-hover-box-shadow: 0 0 0 var(--pico-border-width) var(--pico-primary-hover-border);
}
[role=search] button:focus,
[role=search] [type=submit]:focus,
[role=search] [type=reset]:focus,
[role=search] [type=button]:focus,
[role=search] [role=button]:focus,
[role=group] button:focus,
[role=group] [type=submit]:focus,
[role=group] [type=reset]:focus,
[role=group] [type=button]:focus,
[role=group] [role=button]:focus {
box-shadow: none;
}
}
[role=search] > *:first-child {
border-top-left-radius: 5rem;
border-bottom-left-radius: 5rem;
}
[role=search] > *:last-child {
border-top-right-radius: 5rem;
border-bottom-right-radius: 5rem;
}
/**
* Loading ([aria-busy=true])
*/
[aria-busy=true]:not(input, select, textarea, html) {
white-space: nowrap;
}
[aria-busy=true]:not(input, select, textarea, html)::before {
display: inline-block;
width: 1em;
height: 1em;
background-image: var(--pico-icon-loading);
background-size: 1em auto;
background-repeat: no-repeat;
content: "";
vertical-align: -0.125em;
}
[aria-busy=true]:not(input, select, textarea, html):not(:empty)::before {
margin-inline-end: calc(var(--pico-spacing) * 0.5);
}
[aria-busy=true]:not(input, select, textarea, html):empty {
text-align: center;
}
button[aria-busy=true],
[type=submit][aria-busy=true],
[type=button][aria-busy=true],
[type=reset][aria-busy=true],
[role=button][aria-busy=true],
a[aria-busy=true] {
pointer-events: none;
}
/**
* Modal (