first
This commit is contained in:
commit
f5dcf4d107
187 changed files with 7066 additions and 0 deletions
51
.dockerignore
Normal file
51
.dockerignore
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
|
||||||
|
|
||||||
|
# Ignore git directory.
|
||||||
|
/.git/
|
||||||
|
/.gitignore
|
||||||
|
|
||||||
|
# Ignore bundler config.
|
||||||
|
/.bundle
|
||||||
|
|
||||||
|
# Ignore all environment files.
|
||||||
|
/.env*
|
||||||
|
|
||||||
|
# Ignore all default key files.
|
||||||
|
/config/master.key
|
||||||
|
/config/credentials/*.key
|
||||||
|
|
||||||
|
# Ignore all logfiles and tempfiles.
|
||||||
|
/log/*
|
||||||
|
/tmp/*
|
||||||
|
!/log/.keep
|
||||||
|
!/tmp/.keep
|
||||||
|
|
||||||
|
# Ignore pidfiles, but keep the directory.
|
||||||
|
/tmp/pids/*
|
||||||
|
!/tmp/pids/.keep
|
||||||
|
|
||||||
|
# Ignore storage (uploaded files in development and any SQLite databases).
|
||||||
|
/storage/*
|
||||||
|
!/storage/.keep
|
||||||
|
/tmp/storage/*
|
||||||
|
!/tmp/storage/.keep
|
||||||
|
|
||||||
|
# Ignore assets.
|
||||||
|
/node_modules/
|
||||||
|
/app/assets/builds/*
|
||||||
|
!/app/assets/builds/.keep
|
||||||
|
/public/assets
|
||||||
|
|
||||||
|
# Ignore CI service files.
|
||||||
|
/.github
|
||||||
|
|
||||||
|
# Ignore Kamal files.
|
||||||
|
/config/deploy*.yml
|
||||||
|
/.kamal
|
||||||
|
|
||||||
|
# Ignore development files
|
||||||
|
/.devcontainer
|
||||||
|
|
||||||
|
# Ignore Docker-related files
|
||||||
|
/.dockerignore
|
||||||
|
/Dockerfile*
|
||||||
9
.gitattributes
vendored
Normal file
9
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# See https://git-scm.com/docs/gitattributes for more about git attribute files.
|
||||||
|
|
||||||
|
# Mark the database schema as having been generated.
|
||||||
|
db/schema.rb linguist-generated
|
||||||
|
|
||||||
|
# Mark any vendored files as having been vendored.
|
||||||
|
vendor/* linguist-vendored
|
||||||
|
config/credentials/*.yml.enc diff=rails_credentials
|
||||||
|
config/credentials.yml.enc diff=rails_credentials
|
||||||
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: bundler
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
open-pull-requests-limit: 10
|
||||||
124
.github/workflows/ci.yml
vendored
Normal file
124
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
scan_ruby:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Scan for common Rails security vulnerabilities using static analysis
|
||||||
|
run: bin/brakeman --no-pager
|
||||||
|
|
||||||
|
- name: Scan for known security vulnerabilities in gems used
|
||||||
|
run: bin/bundler-audit
|
||||||
|
|
||||||
|
scan_js:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Scan for security vulnerabilities in JavaScript dependencies
|
||||||
|
run: bin/importmap audit
|
||||||
|
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
RUBOCOP_CACHE_ROOT: tmp/rubocop
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Prepare RuboCop cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
env:
|
||||||
|
DEPENDENCIES_HASH: ${{ hashFiles('.ruby-version', '**/.rubocop.yml', '**/.rubocop_todo.yml', 'Gemfile.lock') }}
|
||||||
|
with:
|
||||||
|
path: ${{ env.RUBOCOP_CACHE_ROOT }}
|
||||||
|
key: rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-${{ github.ref_name == github.event.repository.default_branch && github.run_id || 'default' }}
|
||||||
|
restore-keys: |
|
||||||
|
rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-
|
||||||
|
|
||||||
|
- name: Lint code for consistent style
|
||||||
|
run: bin/rubocop -f github
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# services:
|
||||||
|
# redis:
|
||||||
|
# image: valkey/valkey:8
|
||||||
|
# ports:
|
||||||
|
# - 6379:6379
|
||||||
|
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
env:
|
||||||
|
RAILS_ENV: test
|
||||||
|
# RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
|
||||||
|
# REDIS_URL: redis://localhost:6379/0
|
||||||
|
run: bin/rails db:test:prepare test
|
||||||
|
|
||||||
|
system-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# services:
|
||||||
|
# redis:
|
||||||
|
# image: valkey/valkey:8
|
||||||
|
# ports:
|
||||||
|
# - 6379:6379
|
||||||
|
# options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Set up Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
|
- name: Run System Tests
|
||||||
|
env:
|
||||||
|
RAILS_ENV: test
|
||||||
|
# RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
|
||||||
|
# REDIS_URL: redis://localhost:6379/0
|
||||||
|
run: bin/rails db:test:prepare test:system
|
||||||
|
|
||||||
|
- name: Keep screenshots from failed system tests
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: screenshots
|
||||||
|
path: ${{ github.workspace }}/tmp/screenshots
|
||||||
|
if-no-files-found: ignore
|
||||||
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
||||||
|
#
|
||||||
|
# Temporary files generated by your text editor or operating system
|
||||||
|
# belong in git's global ignore instead:
|
||||||
|
# `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore`
|
||||||
|
|
||||||
|
# Ignore bundler config.
|
||||||
|
/.bundle
|
||||||
|
|
||||||
|
# Ignore all environment files.
|
||||||
|
/.env*
|
||||||
|
|
||||||
|
# Ignore all logfiles and tempfiles.
|
||||||
|
/log/*
|
||||||
|
/tmp/*
|
||||||
|
!/log/.keep
|
||||||
|
!/tmp/.keep
|
||||||
|
|
||||||
|
# Ignore pidfiles, but keep the directory.
|
||||||
|
/tmp/pids/*
|
||||||
|
!/tmp/pids/
|
||||||
|
!/tmp/pids/.keep
|
||||||
|
|
||||||
|
# Ignore storage (uploaded files in development and any SQLite databases).
|
||||||
|
/storage/*
|
||||||
|
!/storage/.keep
|
||||||
|
/tmp/storage/*
|
||||||
|
!/tmp/storage/
|
||||||
|
!/tmp/storage/.keep
|
||||||
|
/storage
|
||||||
|
storage/
|
||||||
|
/public/assets
|
||||||
|
|
||||||
|
# Ignore key files for decrypting credentials and more.
|
||||||
|
/config/*.key
|
||||||
|
/public/assets
|
||||||
|
db/
|
||||||
3
.kamal/hooks/docker-setup.sample
Executable file
3
.kamal/hooks/docker-setup.sample
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Docker set up on $KAMAL_HOSTS..."
|
||||||
3
.kamal/hooks/post-app-boot.sample
Executable file
3
.kamal/hooks/post-app-boot.sample
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Booted app version $KAMAL_VERSION on $KAMAL_HOSTS..."
|
||||||
14
.kamal/hooks/post-deploy.sample
Executable file
14
.kamal/hooks/post-deploy.sample
Executable file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A sample post-deploy hook
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_ROLES (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
# KAMAL_RUNTIME
|
||||||
|
|
||||||
|
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
|
||||||
3
.kamal/hooks/post-proxy-reboot.sample
Executable file
3
.kamal/hooks/post-proxy-reboot.sample
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
|
||||||
3
.kamal/hooks/pre-app-boot.sample
Executable file
3
.kamal/hooks/pre-app-boot.sample
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Booting app version $KAMAL_VERSION on $KAMAL_HOSTS..."
|
||||||
51
.kamal/hooks/pre-build.sample
Executable file
51
.kamal/hooks/pre-build.sample
Executable file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A sample pre-build hook
|
||||||
|
#
|
||||||
|
# Checks:
|
||||||
|
# 1. We have a clean checkout
|
||||||
|
# 2. A remote is configured
|
||||||
|
# 3. The branch has been pushed to the remote
|
||||||
|
# 4. The version we are deploying matches the remote
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_ROLES (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
echo "Git checkout is not clean, aborting..." >&2
|
||||||
|
git status --porcelain >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
first_remote=$(git remote)
|
||||||
|
|
||||||
|
if [ -z "$first_remote" ]; then
|
||||||
|
echo "No git remote set, aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_branch=$(git branch --show-current)
|
||||||
|
|
||||||
|
if [ -z "$current_branch" ]; then
|
||||||
|
echo "Not on a git branch, aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
|
||||||
|
|
||||||
|
if [ -z "$remote_head" ]; then
|
||||||
|
echo "Branch not pushed to remote, aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$KAMAL_VERSION" != "$remote_head" ]; then
|
||||||
|
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
47
.kamal/hooks/pre-connect.sample
Executable file
47
.kamal/hooks/pre-connect.sample
Executable file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
# A sample pre-connect check
|
||||||
|
#
|
||||||
|
# Warms DNS before connecting to hosts in parallel
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_ROLES (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
# KAMAL_RUNTIME
|
||||||
|
|
||||||
|
hosts = ENV["KAMAL_HOSTS"].split(",")
|
||||||
|
results = nil
|
||||||
|
max = 3
|
||||||
|
|
||||||
|
elapsed = Benchmark.realtime do
|
||||||
|
results = hosts.map do |host|
|
||||||
|
Thread.new do
|
||||||
|
tries = 1
|
||||||
|
|
||||||
|
begin
|
||||||
|
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
|
||||||
|
rescue SocketError
|
||||||
|
if tries < max
|
||||||
|
puts "Retrying DNS warmup: #{host}"
|
||||||
|
tries += 1
|
||||||
|
sleep rand
|
||||||
|
retry
|
||||||
|
else
|
||||||
|
puts "DNS warmup failed: #{host}"
|
||||||
|
host
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tries
|
||||||
|
end
|
||||||
|
end.map(&:value)
|
||||||
|
end
|
||||||
|
|
||||||
|
retries = results.sum - hosts.size
|
||||||
|
nopes = results.count { |r| r == max }
|
||||||
|
|
||||||
|
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
|
||||||
122
.kamal/hooks/pre-deploy.sample
Executable file
122
.kamal/hooks/pre-deploy.sample
Executable file
|
|
@ -0,0 +1,122 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
# A sample pre-deploy hook
|
||||||
|
#
|
||||||
|
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
|
||||||
|
#
|
||||||
|
# Fails unless the combined status is "success"
|
||||||
|
#
|
||||||
|
# These environment variables are available:
|
||||||
|
# KAMAL_RECORDED_AT
|
||||||
|
# KAMAL_PERFORMER
|
||||||
|
# KAMAL_VERSION
|
||||||
|
# KAMAL_HOSTS
|
||||||
|
# KAMAL_COMMAND
|
||||||
|
# KAMAL_SUBCOMMAND
|
||||||
|
# KAMAL_ROLES (if set)
|
||||||
|
# KAMAL_DESTINATION (if set)
|
||||||
|
|
||||||
|
# Only check the build status for production deployments
|
||||||
|
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
require "bundler/inline"
|
||||||
|
|
||||||
|
# true = install gems so this is fast on repeat invocations
|
||||||
|
gemfile(true, quiet: true) do
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem "octokit"
|
||||||
|
gem "faraday-retry"
|
||||||
|
end
|
||||||
|
|
||||||
|
MAX_ATTEMPTS = 72
|
||||||
|
ATTEMPTS_GAP = 10
|
||||||
|
|
||||||
|
def exit_with_error(message)
|
||||||
|
$stderr.puts message
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
class GithubStatusChecks
|
||||||
|
attr_reader :remote_url, :git_sha, :github_client, :combined_status
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@remote_url = github_repo_from_remote_url
|
||||||
|
@git_sha = `git rev-parse HEAD`.strip
|
||||||
|
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
||||||
|
refresh!
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh!
|
||||||
|
@combined_status = github_client.combined_status(remote_url, git_sha)
|
||||||
|
end
|
||||||
|
|
||||||
|
def state
|
||||||
|
combined_status[:state]
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_status_url
|
||||||
|
first_status = combined_status[:statuses].find { |status| status[:state] == state }
|
||||||
|
first_status && first_status[:target_url]
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete_count
|
||||||
|
combined_status[:statuses].count { |status| status[:state] != "pending"}
|
||||||
|
end
|
||||||
|
|
||||||
|
def total_count
|
||||||
|
combined_status[:statuses].count
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_status
|
||||||
|
if total_count > 0
|
||||||
|
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
|
||||||
|
else
|
||||||
|
"Build not started..."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def github_repo_from_remote_url
|
||||||
|
url = `git config --get remote.origin.url`.strip.delete_suffix(".git")
|
||||||
|
if url.start_with?("https://github.com/")
|
||||||
|
url.delete_prefix("https://github.com/")
|
||||||
|
elsif url.start_with?("git@github.com:")
|
||||||
|
url.delete_prefix("git@github.com:")
|
||||||
|
else
|
||||||
|
url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
$stdout.sync = true
|
||||||
|
|
||||||
|
begin
|
||||||
|
puts "Checking build status..."
|
||||||
|
|
||||||
|
attempts = 0
|
||||||
|
checks = GithubStatusChecks.new
|
||||||
|
|
||||||
|
loop do
|
||||||
|
case checks.state
|
||||||
|
when "success"
|
||||||
|
puts "Checks passed, see #{checks.first_status_url}"
|
||||||
|
exit 0
|
||||||
|
when "failure"
|
||||||
|
exit_with_error "Checks failed, see #{checks.first_status_url}"
|
||||||
|
when "pending"
|
||||||
|
attempts += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
|
||||||
|
|
||||||
|
puts checks.current_status
|
||||||
|
sleep(ATTEMPTS_GAP)
|
||||||
|
checks.refresh!
|
||||||
|
end
|
||||||
|
rescue Octokit::NotFound
|
||||||
|
exit_with_error "Build status could not be found"
|
||||||
|
end
|
||||||
3
.kamal/hooks/pre-proxy-reboot.sample
Executable file
3
.kamal/hooks/pre-proxy-reboot.sample
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "Rebooting kamal-proxy on $KAMAL_HOSTS..."
|
||||||
19
.kamal/secrets
Normal file
19
.kamal/secrets
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
|
||||||
|
# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
|
||||||
|
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
|
||||||
|
|
||||||
|
# Example of extracting secrets from 1password (or another compatible pw manager)
|
||||||
|
# SECRETS=$(kamal secrets fetch --adapter 1password --account your-account --from Vault/Item KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
|
||||||
|
# KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS})
|
||||||
|
# RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY ${SECRETS})
|
||||||
|
|
||||||
|
# Example of extracting secrets from Rails credentials
|
||||||
|
# KAMAL_REGISTRY_PASSWORD=$(rails credentials:fetch kamal.registry_password)
|
||||||
|
|
||||||
|
# Use a GITHUB_TOKEN if private repositories are needed for the image
|
||||||
|
# GITHUB_TOKEN=$(gh config get -h github.com oauth_token)
|
||||||
|
|
||||||
|
# Grab the registry password from ENV
|
||||||
|
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
|
||||||
|
# Improve security by using a password manager. Never check config/master.key into git!
|
||||||
|
RAILS_MASTER_KEY=$(cat config/master.key)
|
||||||
8
.rubocop.yml
Normal file
8
.rubocop.yml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Omakase Ruby styling for Rails
|
||||||
|
inherit_gem: { rubocop-rails-omakase: rubocop.yml }
|
||||||
|
|
||||||
|
# Overwrite or add rules to create your own house style
|
||||||
|
#
|
||||||
|
# # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
|
||||||
|
# Layout/SpaceInsideArrayLiteralBrackets:
|
||||||
|
# Enabled: false
|
||||||
1
.ruby-version
Normal file
1
.ruby-version
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ruby-3.4.7
|
||||||
76
Dockerfile
Normal file
76
Dockerfile
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
# check=error=true
|
||||||
|
|
||||||
|
# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
|
||||||
|
# docker build -t galaxy .
|
||||||
|
# docker run -d -p 80:80 -e RAILS_MASTER_KEY=<value from config/master.key> --name galaxy galaxy
|
||||||
|
|
||||||
|
# For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html
|
||||||
|
|
||||||
|
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
|
||||||
|
ARG RUBY_VERSION=3.4.7
|
||||||
|
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
|
||||||
|
|
||||||
|
# Rails app lives here
|
||||||
|
WORKDIR /rails
|
||||||
|
|
||||||
|
# Install base packages
|
||||||
|
RUN apt-get update -qq && \
|
||||||
|
apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \
|
||||||
|
ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \
|
||||||
|
rm -rf /var/lib/apt/lists /var/cache/apt/archives
|
||||||
|
|
||||||
|
# Set production environment variables and enable jemalloc for reduced memory usage and latency.
|
||||||
|
ENV RAILS_ENV="production" \
|
||||||
|
BUNDLE_DEPLOYMENT="1" \
|
||||||
|
BUNDLE_PATH="/usr/local/bundle" \
|
||||||
|
BUNDLE_WITHOUT="development" \
|
||||||
|
LD_PRELOAD="/usr/local/lib/libjemalloc.so"
|
||||||
|
|
||||||
|
# Throw-away build stage to reduce size of final image
|
||||||
|
FROM base AS build
|
||||||
|
|
||||||
|
# Install packages needed to build gems
|
||||||
|
RUN apt-get update -qq && \
|
||||||
|
apt-get install --no-install-recommends -y build-essential git libyaml-dev pkg-config && \
|
||||||
|
rm -rf /var/lib/apt/lists /var/cache/apt/archives
|
||||||
|
|
||||||
|
# Install application gems
|
||||||
|
COPY Gemfile Gemfile.lock vendor ./
|
||||||
|
|
||||||
|
RUN bundle install && \
|
||||||
|
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
|
||||||
|
# -j 1 disable parallel compilation to avoid a QEMU bug: https://github.com/rails/bootsnap/issues/495
|
||||||
|
bundle exec bootsnap precompile -j 1 --gemfile
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Precompile bootsnap code for faster boot times.
|
||||||
|
# -j 1 disable parallel compilation to avoid a QEMU bug: https://github.com/rails/bootsnap/issues/495
|
||||||
|
RUN bundle exec bootsnap precompile -j 1 app/ lib/
|
||||||
|
|
||||||
|
# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
|
||||||
|
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Final stage for app image
|
||||||
|
FROM base
|
||||||
|
|
||||||
|
# Run and own only the runtime files as a non-root user for security
|
||||||
|
RUN groupadd --system --gid 1000 rails && \
|
||||||
|
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash
|
||||||
|
USER 1000:1000
|
||||||
|
|
||||||
|
# Copy built artifacts: gems, application
|
||||||
|
COPY --chown=rails:rails --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
|
||||||
|
COPY --chown=rails:rails --from=build /rails /rails
|
||||||
|
|
||||||
|
# Entrypoint prepares the database.
|
||||||
|
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
|
||||||
|
|
||||||
|
# Start server via Thruster by default, this can be overwritten at runtime
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["./bin/thrust", "./bin/rails", "server"]
|
||||||
68
Gemfile
Normal file
68
Gemfile
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
|
||||||
|
gem "rails", "~> 8.1.1"
|
||||||
|
# The modern asset pipeline for Rails [https://github.com/rails/propshaft]
|
||||||
|
gem "propshaft"
|
||||||
|
# Use sqlite3 as the database for Active Record
|
||||||
|
gem "sqlite3", ">= 2.1"
|
||||||
|
# Use the Puma web server [https://github.com/puma/puma]
|
||||||
|
gem "puma", ">= 5.0"
|
||||||
|
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
|
||||||
|
gem "importmap-rails"
|
||||||
|
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
|
||||||
|
gem "turbo-rails"
|
||||||
|
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
|
||||||
|
gem "stimulus-rails"
|
||||||
|
# Build JSON APIs with ease [https://github.com/rails/jbuilder]
|
||||||
|
gem "jbuilder"
|
||||||
|
|
||||||
|
# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
|
||||||
|
gem "bcrypt", "~> 3.1.7"
|
||||||
|
|
||||||
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||||
|
gem "tzinfo-data", platforms: %i[ windows jruby ]
|
||||||
|
|
||||||
|
# Use the database-backed adapters for Rails.cache, Active Job, and Action Cable
|
||||||
|
gem "solid_cache"
|
||||||
|
gem "solid_queue"
|
||||||
|
gem "solid_cable"
|
||||||
|
|
||||||
|
gem "rss"
|
||||||
|
|
||||||
|
# Reduces boot times through caching; required in config/boot.rb
|
||||||
|
gem "bootsnap", require: false
|
||||||
|
|
||||||
|
# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
|
||||||
|
gem "kamal", require: false
|
||||||
|
|
||||||
|
# Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
|
||||||
|
gem "thruster", require: false
|
||||||
|
|
||||||
|
# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
|
||||||
|
gem "image_processing", "~> 1.2"
|
||||||
|
|
||||||
|
group :development, :test do
|
||||||
|
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
|
||||||
|
gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
|
||||||
|
|
||||||
|
# Audits gems for known security defects (use config/bundler-audit.yml to ignore issues)
|
||||||
|
gem "bundler-audit", require: false
|
||||||
|
|
||||||
|
# Static analysis for security vulnerabilities [https://brakemanscanner.org/]
|
||||||
|
gem "brakeman", require: false
|
||||||
|
|
||||||
|
# Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
|
||||||
|
gem "rubocop-rails-omakase", require: false
|
||||||
|
end
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
# Use console on exceptions pages [https://github.com/rails/web-console]
|
||||||
|
gem "web-console"
|
||||||
|
end
|
||||||
|
|
||||||
|
group :test do
|
||||||
|
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
|
||||||
|
gem "capybara"
|
||||||
|
gem "selenium-webdriver"
|
||||||
|
end
|
||||||
414
Gemfile.lock
Normal file
414
Gemfile.lock
Normal file
|
|
@ -0,0 +1,414 @@
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
action_text-trix (2.1.15)
|
||||||
|
railties
|
||||||
|
actioncable (8.1.1)
|
||||||
|
actionpack (= 8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
nio4r (~> 2.0)
|
||||||
|
websocket-driver (>= 0.6.1)
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
|
actionmailbox (8.1.1)
|
||||||
|
actionpack (= 8.1.1)
|
||||||
|
activejob (= 8.1.1)
|
||||||
|
activerecord (= 8.1.1)
|
||||||
|
activestorage (= 8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
mail (>= 2.8.0)
|
||||||
|
actionmailer (8.1.1)
|
||||||
|
actionpack (= 8.1.1)
|
||||||
|
actionview (= 8.1.1)
|
||||||
|
activejob (= 8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
mail (>= 2.8.0)
|
||||||
|
rails-dom-testing (~> 2.2)
|
||||||
|
actionpack (8.1.1)
|
||||||
|
actionview (= 8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
nokogiri (>= 1.8.5)
|
||||||
|
rack (>= 2.2.4)
|
||||||
|
rack-session (>= 1.0.1)
|
||||||
|
rack-test (>= 0.6.3)
|
||||||
|
rails-dom-testing (~> 2.2)
|
||||||
|
rails-html-sanitizer (~> 1.6)
|
||||||
|
useragent (~> 0.16)
|
||||||
|
actiontext (8.1.1)
|
||||||
|
action_text-trix (~> 2.1.15)
|
||||||
|
actionpack (= 8.1.1)
|
||||||
|
activerecord (= 8.1.1)
|
||||||
|
activestorage (= 8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
globalid (>= 0.6.0)
|
||||||
|
nokogiri (>= 1.8.5)
|
||||||
|
actionview (8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
builder (~> 3.1)
|
||||||
|
erubi (~> 1.11)
|
||||||
|
rails-dom-testing (~> 2.2)
|
||||||
|
rails-html-sanitizer (~> 1.6)
|
||||||
|
activejob (8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
globalid (>= 0.3.6)
|
||||||
|
activemodel (8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
activerecord (8.1.1)
|
||||||
|
activemodel (= 8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
timeout (>= 0.4.0)
|
||||||
|
activestorage (8.1.1)
|
||||||
|
actionpack (= 8.1.1)
|
||||||
|
activejob (= 8.1.1)
|
||||||
|
activerecord (= 8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
marcel (~> 1.0)
|
||||||
|
activesupport (8.1.1)
|
||||||
|
base64
|
||||||
|
bigdecimal
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||||
|
connection_pool (>= 2.2.5)
|
||||||
|
drb
|
||||||
|
i18n (>= 1.6, < 2)
|
||||||
|
json
|
||||||
|
logger (>= 1.4.2)
|
||||||
|
minitest (>= 5.1)
|
||||||
|
securerandom (>= 0.3)
|
||||||
|
tzinfo (~> 2.0, >= 2.0.5)
|
||||||
|
uri (>= 0.13.1)
|
||||||
|
addressable (2.8.7)
|
||||||
|
public_suffix (>= 2.0.2, < 7.0)
|
||||||
|
ast (2.4.3)
|
||||||
|
base64 (0.3.0)
|
||||||
|
bcrypt (3.1.20)
|
||||||
|
bcrypt_pbkdf (1.1.1)
|
||||||
|
bigdecimal (3.3.1)
|
||||||
|
bindex (0.8.1)
|
||||||
|
bootsnap (1.18.6)
|
||||||
|
msgpack (~> 1.2)
|
||||||
|
brakeman (7.1.1)
|
||||||
|
racc
|
||||||
|
builder (3.3.0)
|
||||||
|
bundler-audit (0.9.2)
|
||||||
|
bundler (>= 1.2.0, < 3)
|
||||||
|
thor (~> 1.0)
|
||||||
|
capybara (3.40.0)
|
||||||
|
addressable
|
||||||
|
matrix
|
||||||
|
mini_mime (>= 0.1.3)
|
||||||
|
nokogiri (~> 1.11)
|
||||||
|
rack (>= 1.6.0)
|
||||||
|
rack-test (>= 0.6.3)
|
||||||
|
regexp_parser (>= 1.5, < 3.0)
|
||||||
|
xpath (~> 3.2)
|
||||||
|
concurrent-ruby (1.3.5)
|
||||||
|
connection_pool (2.5.4)
|
||||||
|
crass (1.0.6)
|
||||||
|
date (3.5.0)
|
||||||
|
debug (1.11.0)
|
||||||
|
irb (~> 1.10)
|
||||||
|
reline (>= 0.3.8)
|
||||||
|
dotenv (3.1.8)
|
||||||
|
drb (2.2.3)
|
||||||
|
ed25519 (1.4.0)
|
||||||
|
erb (5.1.3)
|
||||||
|
erubi (1.13.1)
|
||||||
|
et-orbi (1.4.0)
|
||||||
|
tzinfo
|
||||||
|
ffi (1.17.2-aarch64-linux-gnu)
|
||||||
|
ffi (1.17.2-aarch64-linux-musl)
|
||||||
|
ffi (1.17.2-arm-linux-gnu)
|
||||||
|
ffi (1.17.2-arm-linux-musl)
|
||||||
|
ffi (1.17.2-x86_64-linux-gnu)
|
||||||
|
ffi (1.17.2-x86_64-linux-musl)
|
||||||
|
fugit (1.12.1)
|
||||||
|
et-orbi (~> 1.4)
|
||||||
|
raabro (~> 1.4)
|
||||||
|
globalid (1.3.0)
|
||||||
|
activesupport (>= 6.1)
|
||||||
|
i18n (1.14.7)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
image_processing (1.14.0)
|
||||||
|
mini_magick (>= 4.9.5, < 6)
|
||||||
|
ruby-vips (>= 2.0.17, < 3)
|
||||||
|
importmap-rails (2.2.2)
|
||||||
|
actionpack (>= 6.0.0)
|
||||||
|
activesupport (>= 6.0.0)
|
||||||
|
railties (>= 6.0.0)
|
||||||
|
io-console (0.8.1)
|
||||||
|
irb (1.15.3)
|
||||||
|
pp (>= 0.6.0)
|
||||||
|
rdoc (>= 4.0.0)
|
||||||
|
reline (>= 0.4.2)
|
||||||
|
jbuilder (2.14.1)
|
||||||
|
actionview (>= 7.0.0)
|
||||||
|
activesupport (>= 7.0.0)
|
||||||
|
json (2.16.0)
|
||||||
|
kamal (2.8.2)
|
||||||
|
activesupport (>= 7.0)
|
||||||
|
base64 (~> 0.2)
|
||||||
|
bcrypt_pbkdf (~> 1.0)
|
||||||
|
concurrent-ruby (~> 1.2)
|
||||||
|
dotenv (~> 3.1)
|
||||||
|
ed25519 (~> 1.4)
|
||||||
|
net-ssh (~> 7.3)
|
||||||
|
sshkit (>= 1.23.0, < 2.0)
|
||||||
|
thor (~> 1.3)
|
||||||
|
zeitwerk (>= 2.6.18, < 3.0)
|
||||||
|
language_server-protocol (3.17.0.5)
|
||||||
|
lint_roller (1.1.0)
|
||||||
|
logger (1.7.0)
|
||||||
|
loofah (2.24.1)
|
||||||
|
crass (~> 1.0.2)
|
||||||
|
nokogiri (>= 1.12.0)
|
||||||
|
mail (2.9.0)
|
||||||
|
logger
|
||||||
|
mini_mime (>= 0.1.1)
|
||||||
|
net-imap
|
||||||
|
net-pop
|
||||||
|
net-smtp
|
||||||
|
marcel (1.1.0)
|
||||||
|
matrix (0.4.3)
|
||||||
|
mini_magick (5.3.1)
|
||||||
|
logger
|
||||||
|
mini_mime (1.1.5)
|
||||||
|
minitest (5.26.0)
|
||||||
|
msgpack (1.8.0)
|
||||||
|
net-imap (0.5.12)
|
||||||
|
date
|
||||||
|
net-protocol
|
||||||
|
net-pop (0.1.2)
|
||||||
|
net-protocol
|
||||||
|
net-protocol (0.2.2)
|
||||||
|
timeout
|
||||||
|
net-scp (4.1.0)
|
||||||
|
net-ssh (>= 2.6.5, < 8.0.0)
|
||||||
|
net-sftp (4.0.0)
|
||||||
|
net-ssh (>= 5.0.0, < 8.0.0)
|
||||||
|
net-smtp (0.5.1)
|
||||||
|
net-protocol
|
||||||
|
net-ssh (7.3.0)
|
||||||
|
nio4r (2.7.5)
|
||||||
|
nokogiri (1.18.10-aarch64-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.10-aarch64-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.10-arm-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.10-arm-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.10-x86_64-linux-gnu)
|
||||||
|
racc (~> 1.4)
|
||||||
|
nokogiri (1.18.10-x86_64-linux-musl)
|
||||||
|
racc (~> 1.4)
|
||||||
|
ostruct (0.6.3)
|
||||||
|
parallel (1.27.0)
|
||||||
|
parser (3.3.10.0)
|
||||||
|
ast (~> 2.4.1)
|
||||||
|
racc
|
||||||
|
pp (0.6.3)
|
||||||
|
prettyprint
|
||||||
|
prettyprint (0.2.0)
|
||||||
|
prism (1.6.0)
|
||||||
|
propshaft (1.3.1)
|
||||||
|
actionpack (>= 7.0.0)
|
||||||
|
activesupport (>= 7.0.0)
|
||||||
|
rack
|
||||||
|
psych (5.2.6)
|
||||||
|
date
|
||||||
|
stringio
|
||||||
|
public_suffix (6.0.2)
|
||||||
|
puma (7.1.0)
|
||||||
|
nio4r (~> 2.0)
|
||||||
|
raabro (1.4.0)
|
||||||
|
racc (1.8.1)
|
||||||
|
rack (3.2.4)
|
||||||
|
rack-session (2.1.1)
|
||||||
|
base64 (>= 0.1.0)
|
||||||
|
rack (>= 3.0.0)
|
||||||
|
rack-test (2.2.0)
|
||||||
|
rack (>= 1.3)
|
||||||
|
rackup (2.2.1)
|
||||||
|
rack (>= 3)
|
||||||
|
rails (8.1.1)
|
||||||
|
actioncable (= 8.1.1)
|
||||||
|
actionmailbox (= 8.1.1)
|
||||||
|
actionmailer (= 8.1.1)
|
||||||
|
actionpack (= 8.1.1)
|
||||||
|
actiontext (= 8.1.1)
|
||||||
|
actionview (= 8.1.1)
|
||||||
|
activejob (= 8.1.1)
|
||||||
|
activemodel (= 8.1.1)
|
||||||
|
activerecord (= 8.1.1)
|
||||||
|
activestorage (= 8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
bundler (>= 1.15.0)
|
||||||
|
railties (= 8.1.1)
|
||||||
|
rails-dom-testing (2.3.0)
|
||||||
|
activesupport (>= 5.0.0)
|
||||||
|
minitest
|
||||||
|
nokogiri (>= 1.6)
|
||||||
|
rails-html-sanitizer (1.6.2)
|
||||||
|
loofah (~> 2.21)
|
||||||
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
||||||
|
railties (8.1.1)
|
||||||
|
actionpack (= 8.1.1)
|
||||||
|
activesupport (= 8.1.1)
|
||||||
|
irb (~> 1.13)
|
||||||
|
rackup (>= 1.0.0)
|
||||||
|
rake (>= 12.2)
|
||||||
|
thor (~> 1.0, >= 1.2.2)
|
||||||
|
tsort (>= 0.2)
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
|
rainbow (3.1.1)
|
||||||
|
rake (13.3.1)
|
||||||
|
rdoc (6.15.1)
|
||||||
|
erb
|
||||||
|
psych (>= 4.0.0)
|
||||||
|
tsort
|
||||||
|
regexp_parser (2.11.3)
|
||||||
|
reline (0.6.2)
|
||||||
|
io-console (~> 0.5)
|
||||||
|
rexml (3.4.4)
|
||||||
|
rss (0.3.1)
|
||||||
|
rexml
|
||||||
|
rubocop (1.81.7)
|
||||||
|
json (~> 2.3)
|
||||||
|
language_server-protocol (~> 3.17.0.2)
|
||||||
|
lint_roller (~> 1.1.0)
|
||||||
|
parallel (~> 1.10)
|
||||||
|
parser (>= 3.3.0.2)
|
||||||
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
|
regexp_parser (>= 2.9.3, < 3.0)
|
||||||
|
rubocop-ast (>= 1.47.1, < 2.0)
|
||||||
|
ruby-progressbar (~> 1.7)
|
||||||
|
unicode-display_width (>= 2.4.0, < 4.0)
|
||||||
|
rubocop-ast (1.47.1)
|
||||||
|
parser (>= 3.3.7.2)
|
||||||
|
prism (~> 1.4)
|
||||||
|
rubocop-performance (1.26.1)
|
||||||
|
lint_roller (~> 1.1)
|
||||||
|
rubocop (>= 1.75.0, < 2.0)
|
||||||
|
rubocop-ast (>= 1.47.1, < 2.0)
|
||||||
|
rubocop-rails (2.33.4)
|
||||||
|
activesupport (>= 4.2.0)
|
||||||
|
lint_roller (~> 1.1)
|
||||||
|
rack (>= 1.1)
|
||||||
|
rubocop (>= 1.75.0, < 2.0)
|
||||||
|
rubocop-ast (>= 1.44.0, < 2.0)
|
||||||
|
rubocop-rails-omakase (1.1.0)
|
||||||
|
rubocop (>= 1.72)
|
||||||
|
rubocop-performance (>= 1.24)
|
||||||
|
rubocop-rails (>= 2.30)
|
||||||
|
ruby-progressbar (1.13.0)
|
||||||
|
ruby-vips (2.2.5)
|
||||||
|
ffi (~> 1.12)
|
||||||
|
logger
|
||||||
|
rubyzip (3.2.2)
|
||||||
|
securerandom (0.4.1)
|
||||||
|
selenium-webdriver (4.38.0)
|
||||||
|
base64 (~> 0.2)
|
||||||
|
logger (~> 1.4)
|
||||||
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
|
rubyzip (>= 1.2.2, < 4.0)
|
||||||
|
websocket (~> 1.0)
|
||||||
|
solid_cable (3.0.12)
|
||||||
|
actioncable (>= 7.2)
|
||||||
|
activejob (>= 7.2)
|
||||||
|
activerecord (>= 7.2)
|
||||||
|
railties (>= 7.2)
|
||||||
|
solid_cache (1.0.9)
|
||||||
|
activejob (>= 7.2)
|
||||||
|
activerecord (>= 7.2)
|
||||||
|
railties (>= 7.2)
|
||||||
|
solid_queue (1.2.4)
|
||||||
|
activejob (>= 7.1)
|
||||||
|
activerecord (>= 7.1)
|
||||||
|
concurrent-ruby (>= 1.3.1)
|
||||||
|
fugit (~> 1.11)
|
||||||
|
railties (>= 7.1)
|
||||||
|
thor (>= 1.3.1)
|
||||||
|
sqlite3 (2.8.0-aarch64-linux-gnu)
|
||||||
|
sqlite3 (2.8.0-aarch64-linux-musl)
|
||||||
|
sqlite3 (2.8.0-arm-linux-gnu)
|
||||||
|
sqlite3 (2.8.0-arm-linux-musl)
|
||||||
|
sqlite3 (2.8.0-x86_64-linux-gnu)
|
||||||
|
sqlite3 (2.8.0-x86_64-linux-musl)
|
||||||
|
sshkit (1.24.0)
|
||||||
|
base64
|
||||||
|
logger
|
||||||
|
net-scp (>= 1.1.2)
|
||||||
|
net-sftp (>= 2.1.2)
|
||||||
|
net-ssh (>= 2.8.0)
|
||||||
|
ostruct
|
||||||
|
stimulus-rails (1.3.4)
|
||||||
|
railties (>= 6.0.0)
|
||||||
|
stringio (3.1.7)
|
||||||
|
thor (1.4.0)
|
||||||
|
thruster (0.1.16)
|
||||||
|
thruster (0.1.16-aarch64-linux)
|
||||||
|
thruster (0.1.16-x86_64-linux)
|
||||||
|
timeout (0.4.4)
|
||||||
|
tsort (0.2.0)
|
||||||
|
turbo-rails (2.0.20)
|
||||||
|
actionpack (>= 7.1.0)
|
||||||
|
railties (>= 7.1.0)
|
||||||
|
tzinfo (2.0.6)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
unicode-display_width (3.2.0)
|
||||||
|
unicode-emoji (~> 4.1)
|
||||||
|
unicode-emoji (4.1.0)
|
||||||
|
uri (1.1.1)
|
||||||
|
useragent (0.16.11)
|
||||||
|
web-console (4.2.1)
|
||||||
|
actionview (>= 6.0.0)
|
||||||
|
activemodel (>= 6.0.0)
|
||||||
|
bindex (>= 0.4.0)
|
||||||
|
railties (>= 6.0.0)
|
||||||
|
websocket (1.2.11)
|
||||||
|
websocket-driver (0.8.0)
|
||||||
|
base64
|
||||||
|
websocket-extensions (>= 0.1.0)
|
||||||
|
websocket-extensions (0.1.5)
|
||||||
|
xpath (3.2.0)
|
||||||
|
nokogiri (~> 1.8)
|
||||||
|
zeitwerk (2.7.3)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
aarch64-linux
|
||||||
|
aarch64-linux-gnu
|
||||||
|
aarch64-linux-musl
|
||||||
|
arm-linux-gnu
|
||||||
|
arm-linux-musl
|
||||||
|
x86_64-linux
|
||||||
|
x86_64-linux-gnu
|
||||||
|
x86_64-linux-musl
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
bcrypt (~> 3.1.7)
|
||||||
|
bootsnap
|
||||||
|
brakeman
|
||||||
|
bundler-audit
|
||||||
|
capybara
|
||||||
|
debug
|
||||||
|
image_processing (~> 1.2)
|
||||||
|
importmap-rails
|
||||||
|
jbuilder
|
||||||
|
kamal
|
||||||
|
propshaft
|
||||||
|
puma (>= 5.0)
|
||||||
|
rails (~> 8.1.1)
|
||||||
|
rss
|
||||||
|
rubocop-rails-omakase
|
||||||
|
selenium-webdriver
|
||||||
|
solid_cable
|
||||||
|
solid_cache
|
||||||
|
solid_queue
|
||||||
|
sqlite3 (>= 2.1)
|
||||||
|
stimulus-rails
|
||||||
|
thruster
|
||||||
|
turbo-rails
|
||||||
|
tzinfo-data
|
||||||
|
web-console
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.6.9
|
||||||
3
README.md
Normal file
3
README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
this is the code for my silly personal playground site. i use it to learn rails stuff on! it's a mess, don't use this code, or do if you want idk i cant stop you
|
||||||
|
|
||||||
|
this is accessible at https://lesbian.kissing.computer, currently
|
||||||
6
Rakefile
Normal file
6
Rakefile
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
|
require_relative "config/application"
|
||||||
|
|
||||||
|
Rails.application.load_tasks
|
||||||
0
app/assets/images/.keep
Normal file
0
app/assets/images/.keep
Normal file
440
app/assets/stylesheets/actiontext.css
Normal file
440
app/assets/stylesheets/actiontext.css
Normal file
|
|
@ -0,0 +1,440 @@
|
||||||
|
/*
|
||||||
|
* Default Trix editor styles. See Action Text overwrites below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
trix-editor {
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.4em 0.6em;
|
||||||
|
min-height: 5em;
|
||||||
|
outline: none; }
|
||||||
|
|
||||||
|
trix-toolbar * {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
overflow-x: auto; }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button-group {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-top-color: #ccc;
|
||||||
|
border-bottom-color: #888;
|
||||||
|
border-radius: 3px; }
|
||||||
|
trix-toolbar .trix-button-group:not(:first-child) {
|
||||||
|
margin-left: 1.5vw; }
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
trix-toolbar .trix-button-group:not(:first-child) {
|
||||||
|
margin-left: 0; } }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button-group-spacer {
|
||||||
|
flex-grow: 1; }
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
trix-toolbar .trix-button-group-spacer {
|
||||||
|
display: none; } }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
color: rgba(0, 0, 0, 0.6);
|
||||||
|
font-size: 0.75em;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent; }
|
||||||
|
trix-toolbar .trix-button:not(:first-child) {
|
||||||
|
border-left: 1px solid #ccc; }
|
||||||
|
trix-toolbar .trix-button.trix-active {
|
||||||
|
background: #cbeefa;
|
||||||
|
color: black; }
|
||||||
|
trix-toolbar .trix-button:not(:disabled) {
|
||||||
|
cursor: pointer; }
|
||||||
|
trix-toolbar .trix-button:disabled {
|
||||||
|
color: rgba(0, 0, 0, 0.125); }
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
trix-toolbar .trix-button {
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
padding: 0 0.3em; } }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon {
|
||||||
|
font-size: inherit;
|
||||||
|
width: 2.6em;
|
||||||
|
height: 1.6em;
|
||||||
|
max-width: calc(0.8em + 4vw);
|
||||||
|
text-indent: -9999px; }
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
trix-toolbar .trix-button--icon {
|
||||||
|
height: 2em;
|
||||||
|
max-width: calc(0.8em + 3.5vw); } }
|
||||||
|
trix-toolbar .trix-button--icon::before {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0.6;
|
||||||
|
content: "";
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain; }
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
trix-toolbar .trix-button--icon::before {
|
||||||
|
right: 6%;
|
||||||
|
left: 6%; } }
|
||||||
|
trix-toolbar .trix-button--icon.trix-active::before {
|
||||||
|
opacity: 1; }
|
||||||
|
trix-toolbar .trix-button--icon:disabled::before {
|
||||||
|
opacity: 0.125; }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-attach::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M10.5%2018V7.5c0-2.25%203-2.25%203%200V18c0%204.125-6%204.125-6%200V7.5c0-6.375%209-6.375%209%200V18%22%20stroke%3D%22%23000%22%20stroke-width%3D%222%22%20stroke-miterlimit%3D%2210%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
top: 8%;
|
||||||
|
bottom: 4%; }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-bold::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M6.522%2019.242a.5.5%200%200%201-.5-.5V5.35a.5.5%200%200%201%20.5-.5h5.783c1.347%200%202.46.345%203.24.982.783.64%201.216%201.562%201.216%202.683%200%201.13-.587%202.129-1.476%202.71a.35.35%200%200%200%20.049.613c1.259.56%202.101%201.742%202.101%203.22%200%201.282-.483%202.334-1.363%203.063-.876.726-2.132%201.12-3.66%201.12h-5.89ZM9.27%207.347v3.362h1.97c.766%200%201.347-.17%201.733-.464.38-.291.587-.716.587-1.27%200-.53-.183-.928-.513-1.198-.334-.273-.838-.43-1.505-.43H9.27Zm0%205.606v3.791h2.389c.832%200%201.448-.177%201.853-.497.399-.315.614-.786.614-1.423%200-.62-.22-1.077-.63-1.385-.418-.313-1.053-.486-1.905-.486H9.27Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-italic::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M9%205h6.5v2h-2.23l-2.31%2010H13v2H6v-2h2.461l2.306-10H9V5Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-link::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M18.948%205.258a4.337%204.337%200%200%200-6.108%200L11.217%206.87a.993.993%200%200%200%200%201.41c.392.39%201.027.39%201.418%200l1.623-1.613a2.323%202.323%200%200%201%203.271%200%202.29%202.29%200%200%201%200%203.251l-2.393%202.38a3.021%203.021%200%200%201-4.255%200l-.05-.049a1.007%201.007%200%200%200-1.418%200%20.993.993%200%200%200%200%201.41l.05.049a5.036%205.036%200%200%200%207.091%200l2.394-2.38a4.275%204.275%200%200%200%200-6.072Zm-13.683%2013.6a4.337%204.337%200%200%200%206.108%200l1.262-1.255a.993.993%200%200%200%200-1.41%201.007%201.007%200%200%200-1.418%200L9.954%2017.45a2.323%202.323%200%200%201-3.27%200%202.29%202.29%200%200%201%200-3.251l2.344-2.331a2.579%202.579%200%200%201%203.631%200c.392.39%201.027.39%201.419%200a.993.993%200%200%200%200-1.41%204.593%204.593%200%200%200-6.468%200l-2.345%202.33a4.275%204.275%200%200%200%200%206.072Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-strike::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M6%2014.986c.088%202.647%202.246%204.258%205.635%204.258%203.496%200%205.713-1.728%205.713-4.463%200-.275-.02-.536-.062-.781h-3.461c.398.293.573.654.573%201.123%200%201.035-1.074%201.787-2.646%201.787-1.563%200-2.773-.762-2.91-1.924H6ZM6.432%2010h3.763c-.632-.314-.914-.715-.914-1.273%200-1.045.977-1.739%202.432-1.739%201.475%200%202.52.723%202.617%201.914h2.764c-.05-2.548-2.11-4.238-5.39-4.238-3.145%200-5.392%201.719-5.392%204.316%200%20.363.04.703.12%201.02ZM4%2011a1%201%200%201%200%200%202h15a1%201%200%201%200%200-2H4Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-quote::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M4.581%208.471c.44-.5%201.056-.834%201.758-.995C8.074%207.17%209.201%207.822%2010%208.752c1.354%201.578%201.33%203.555.394%205.277-.941%201.731-2.788%203.163-4.988%203.56a.622.622%200%200%201-.653-.317c-.113-.205-.121-.49.16-.764.294-.286.567-.566.791-.835.222-.266.413-.54.524-.815.113-.28.156-.597.026-.908-.128-.303-.39-.524-.72-.69a3.02%203.02%200%200%201-1.674-2.7c0-.905.283-1.59.72-2.088Zm9.419%200c.44-.5%201.055-.834%201.758-.995%201.734-.306%202.862.346%203.66%201.276%201.355%201.578%201.33%203.555.395%205.277-.941%201.731-2.789%203.163-4.988%203.56a.622.622%200%200%201-.653-.317c-.113-.205-.122-.49.16-.764.294-.286.567-.566.791-.835.222-.266.412-.54.523-.815.114-.28.157-.597.026-.908-.127-.303-.39-.524-.72-.69a3.02%203.02%200%200%201-1.672-2.701c0-.905.283-1.59.72-2.088Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-heading-1::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M21.5%207.5v-3h-12v3H14v13h3v-13h4.5ZM9%2013.5h3.5v-3h-10v3H6v7h3v-7Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-code::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M3.293%2011.293a1%201%200%200%200%200%201.414l4%204a1%201%200%201%200%201.414-1.414L5.414%2012l3.293-3.293a1%201%200%200%200-1.414-1.414l-4%204Zm13.414%205.414%204-4a1%201%200%200%200%200-1.414l-4-4a1%201%200%201%200-1.414%201.414L18.586%2012l-3.293%203.293a1%201%200%200%200%201.414%201.414Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-bullet-list::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M5%207.5a1.5%201.5%200%201%200%200-3%201.5%201.5%200%200%200%200%203ZM8%206a1%201%200%200%201%201-1h11a1%201%200%201%201%200%202H9a1%201%200%200%201-1-1Zm1%205a1%201%200%201%200%200%202h11a1%201%200%201%200%200-2H9Zm0%206a1%201%200%201%200%200%202h11a1%201%200%201%200%200-2H9Zm-2.5-5a1.5%201.5%200%201%201-3%200%201.5%201.5%200%200%201%203%200ZM5%2019.5a1.5%201.5%200%201%200%200-3%201.5%201.5%200%200%200%200%203Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-number-list::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M3%204h2v4H4V5H3V4Zm5%202a1%201%200%200%201%201-1h11a1%201%200%201%201%200%202H9a1%201%200%200%201-1-1Zm1%205a1%201%200%201%200%200%202h11a1%201%200%201%200%200-2H9Zm0%206a1%201%200%201%200%200%202h11a1%201%200%201%200%200-2H9Zm-3.5-7H6v1l-1.5%202H6v1H3v-1l1.667-2H3v-1h2.5ZM3%2017v-1h3v4H3v-1h2v-.5H4v-1h1V17H3Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-undo::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M3%2014a1%201%200%200%200%201%201h6a1%201%200%201%200%200-2H6.257c2.247-2.764%205.151-3.668%207.579-3.264%202.589.432%204.739%202.356%205.174%205.405a1%201%200%200%200%201.98-.283c-.564-3.95-3.415-6.526-6.825-7.095C11.084%207.25%207.63%208.377%205%2011.39V8a1%201%200%200%200-2%200v6Zm2-1Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-redo::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M21%2014a1%201%200%200%201-1%201h-6a1%201%200%201%201%200-2h3.743c-2.247-2.764-5.151-3.668-7.579-3.264-2.589.432-4.739%202.356-5.174%205.405a1%201%200%200%201-1.98-.283c.564-3.95%203.415-6.526%206.826-7.095%203.08-.513%206.534.614%209.164%203.626V8a1%201%200%201%201%202%200v6Zm-2-1Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-decrease-nesting-level::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M5%206a1%201%200%200%201%201-1h12a1%201%200%201%201%200%202H6a1%201%200%200%201-1-1Zm4%205a1%201%200%201%200%200%202h9a1%201%200%201%200%200-2H9Zm-3%206a1%201%200%201%200%200%202h12a1%201%200%201%200%200-2H6Zm-3.707-5.707a1%201%200%200%200%200%201.414l2%202a1%201%200%201%200%201.414-1.414L4.414%2012l1.293-1.293a1%201%200%200%200-1.414-1.414l-2%202Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--icon-increase-nesting-level::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M5%206a1%201%200%200%201%201-1h12a1%201%200%201%201%200%202H6a1%201%200%200%201-1-1Zm4%205a1%201%200%201%200%200%202h9a1%201%200%201%200%200-2H9Zm-3%206a1%201%200%201%200%200%202h12a1%201%200%201%200%200-2H6Zm-2.293-2.293%202-2a1%201%200%200%200%200-1.414l-2-2a1%201%200%201%200-1.414%201.414L3.586%2012l-1.293%201.293a1%201%200%201%200%201.414%201.414Z%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E"); }
|
||||||
|
|
||||||
|
trix-toolbar .trix-dialogs {
|
||||||
|
position: relative; }
|
||||||
|
|
||||||
|
trix-toolbar .trix-dialog {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
font-size: 0.75em;
|
||||||
|
padding: 15px 10px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0.3em 1em #ccc;
|
||||||
|
border-top: 2px solid #888;
|
||||||
|
border-radius: 5px;
|
||||||
|
z-index: 5; }
|
||||||
|
|
||||||
|
trix-toolbar .trix-input--dialog {
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: normal;
|
||||||
|
padding: 0.5em 0.8em;
|
||||||
|
margin: 0 10px 0 0;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none; }
|
||||||
|
trix-toolbar .trix-input--dialog.validate:invalid {
|
||||||
|
box-shadow: #F00 0px 0px 1.5px 1px; }
|
||||||
|
|
||||||
|
trix-toolbar .trix-button--dialog {
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 0.5em;
|
||||||
|
border-bottom: none; }
|
||||||
|
|
||||||
|
trix-toolbar .trix-dialog--link {
|
||||||
|
max-width: 600px; }
|
||||||
|
|
||||||
|
trix-toolbar .trix-dialog__link-fields {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline; }
|
||||||
|
trix-toolbar .trix-dialog__link-fields .trix-input {
|
||||||
|
flex: 1; }
|
||||||
|
trix-toolbar .trix-dialog__link-fields .trix-button-group {
|
||||||
|
flex: 0 0 content;
|
||||||
|
margin: 0; }
|
||||||
|
|
||||||
|
trix-editor [data-trix-mutable]:not(.attachment__caption-editor) {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none; }
|
||||||
|
|
||||||
|
trix-editor [data-trix-mutable]::-moz-selection,
|
||||||
|
trix-editor [data-trix-cursor-target]::-moz-selection, trix-editor [data-trix-mutable] ::-moz-selection {
|
||||||
|
background: none; }
|
||||||
|
|
||||||
|
trix-editor [data-trix-mutable]::selection,
|
||||||
|
trix-editor [data-trix-cursor-target]::selection, trix-editor [data-trix-mutable] ::selection {
|
||||||
|
background: none; }
|
||||||
|
|
||||||
|
trix-editor .attachment__caption-editor:focus[data-trix-mutable]::-moz-selection {
|
||||||
|
background: highlight; }
|
||||||
|
|
||||||
|
trix-editor .attachment__caption-editor:focus[data-trix-mutable]::selection {
|
||||||
|
background: highlight; }
|
||||||
|
|
||||||
|
trix-editor [data-trix-mutable].attachment.attachment--file {
|
||||||
|
box-shadow: 0 0 0 2px highlight;
|
||||||
|
border-color: transparent; }
|
||||||
|
|
||||||
|
trix-editor [data-trix-mutable].attachment img {
|
||||||
|
box-shadow: 0 0 0 2px highlight; }
|
||||||
|
|
||||||
|
trix-editor .attachment {
|
||||||
|
position: relative; }
|
||||||
|
trix-editor .attachment:hover {
|
||||||
|
cursor: default; }
|
||||||
|
|
||||||
|
trix-editor .attachment--preview .attachment__caption:hover {
|
||||||
|
cursor: text; }
|
||||||
|
|
||||||
|
trix-editor .attachment__progress {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
height: 20px;
|
||||||
|
top: calc(50% - 10px);
|
||||||
|
left: 5%;
|
||||||
|
width: 90%;
|
||||||
|
opacity: 0.9;
|
||||||
|
transition: opacity 200ms ease-in; }
|
||||||
|
trix-editor .attachment__progress[value="100"] {
|
||||||
|
opacity: 0; }
|
||||||
|
|
||||||
|
trix-editor .attachment__caption-editor {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
font-family: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
color: inherit;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: top;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none; }
|
||||||
|
|
||||||
|
trix-editor .attachment__toolbar {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: -0.9em;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center; }
|
||||||
|
|
||||||
|
trix-editor .trix-button-group {
|
||||||
|
display: inline-flex; }
|
||||||
|
|
||||||
|
trix-editor .trix-button {
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
color: #666;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 80%;
|
||||||
|
padding: 0 0.8em;
|
||||||
|
margin: 0;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent; }
|
||||||
|
trix-editor .trix-button:not(:first-child) {
|
||||||
|
border-left: 1px solid #ccc; }
|
||||||
|
trix-editor .trix-button.trix-active {
|
||||||
|
background: #cbeefa; }
|
||||||
|
trix-editor .trix-button:not(:disabled) {
|
||||||
|
cursor: pointer; }
|
||||||
|
|
||||||
|
trix-editor .trix-button--remove {
|
||||||
|
text-indent: -9999px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0;
|
||||||
|
outline: none;
|
||||||
|
width: 1.8em;
|
||||||
|
height: 1.8em;
|
||||||
|
line-height: 1.8em;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 2px solid highlight;
|
||||||
|
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.25); }
|
||||||
|
trix-editor .trix-button--remove::before {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0.7;
|
||||||
|
content: "";
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg%20height%3D%2224%22%20width%3D%2224%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M19%206.41%2017.59%205%2012%2010.59%206.41%205%205%206.41%2010.59%2012%205%2017.59%206.41%2019%2012%2013.41%2017.59%2019%2019%2017.59%2013.41%2012z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E");
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 90%; }
|
||||||
|
trix-editor .trix-button--remove:hover {
|
||||||
|
border-color: #333; }
|
||||||
|
trix-editor .trix-button--remove:hover::before {
|
||||||
|
opacity: 1; }
|
||||||
|
|
||||||
|
trix-editor .attachment__metadata-container {
|
||||||
|
position: relative; }
|
||||||
|
|
||||||
|
trix-editor .attachment__metadata {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 2em;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
max-width: 90%;
|
||||||
|
padding: 0.1em 0.6em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
border-radius: 3px; }
|
||||||
|
trix-editor .attachment__metadata .attachment__name {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 100%;
|
||||||
|
vertical-align: bottom;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap; }
|
||||||
|
trix-editor .attachment__metadata .attachment__size {
|
||||||
|
margin-left: 0.2em;
|
||||||
|
white-space: nowrap; }
|
||||||
|
|
||||||
|
.trix-content {
|
||||||
|
line-height: 1.5;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-break: break-word; }
|
||||||
|
.trix-content * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
.trix-content h1 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
line-height: 1.2; }
|
||||||
|
.trix-content blockquote {
|
||||||
|
border: 0 solid #ccc;
|
||||||
|
border-left-width: 0.3em;
|
||||||
|
margin-left: 0.3em;
|
||||||
|
padding-left: 0.6em; }
|
||||||
|
.trix-content [dir=rtl] blockquote,
|
||||||
|
.trix-content blockquote[dir=rtl] {
|
||||||
|
border-width: 0;
|
||||||
|
border-right-width: 0.3em;
|
||||||
|
margin-right: 0.3em;
|
||||||
|
padding-right: 0.6em; }
|
||||||
|
.trix-content li {
|
||||||
|
margin-left: 1em; }
|
||||||
|
.trix-content [dir=rtl] li {
|
||||||
|
margin-right: 1em; }
|
||||||
|
.trix-content pre {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
vertical-align: top;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding: 0.5em;
|
||||||
|
white-space: pre;
|
||||||
|
background-color: #eee;
|
||||||
|
overflow-x: auto; }
|
||||||
|
.trix-content img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto; }
|
||||||
|
.trix-content .attachment {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
max-width: 100%; }
|
||||||
|
.trix-content .attachment a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none; }
|
||||||
|
.trix-content .attachment a:hover, .trix-content .attachment a:visited:hover {
|
||||||
|
color: inherit; }
|
||||||
|
.trix-content .attachment__caption {
|
||||||
|
text-align: center; }
|
||||||
|
.trix-content .attachment__caption .attachment__name + .attachment__size::before {
|
||||||
|
content: ' \2022 '; }
|
||||||
|
.trix-content .attachment--preview {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center; }
|
||||||
|
.trix-content .attachment--preview .attachment__caption {
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-height: 1.2; }
|
||||||
|
.trix-content .attachment--file {
|
||||||
|
color: #333;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0 2px 2px 2px;
|
||||||
|
padding: 0.4em 1em;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 5px; }
|
||||||
|
.trix-content .attachment-gallery {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
position: relative; }
|
||||||
|
.trix-content .attachment-gallery .attachment {
|
||||||
|
flex: 1 0 33%;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
max-width: 33%; }
|
||||||
|
.trix-content .attachment-gallery.attachment-gallery--2 .attachment, .trix-content .attachment-gallery.attachment-gallery--4 .attachment {
|
||||||
|
flex-basis: 50%;
|
||||||
|
max-width: 50%; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to override trix.css’s image gallery styles to accommodate the
|
||||||
|
* <action-text-attachment> element we wrap around attachments. Otherwise,
|
||||||
|
* images in galleries will be squished by the max-width: 33%; rule.
|
||||||
|
*/
|
||||||
|
.trix-content .attachment-gallery > action-text-attachment,
|
||||||
|
.trix-content .attachment-gallery > .attachment {
|
||||||
|
flex: 1 0 33%;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
max-width: 33%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trix-content .attachment-gallery.attachment-gallery--2 > action-text-attachment,
|
||||||
|
.trix-content .attachment-gallery.attachment-gallery--2 > .attachment, .trix-content .attachment-gallery.attachment-gallery--4 > action-text-attachment,
|
||||||
|
.trix-content .attachment-gallery.attachment-gallery--4 > .attachment {
|
||||||
|
flex-basis: 50%;
|
||||||
|
max-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trix-content action-text-attachment .attachment {
|
||||||
|
padding: 0 !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
349
app/assets/stylesheets/application.css
Normal file
349
app/assets/stylesheets/application.css
Normal file
|
|
@ -0,0 +1,349 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
body {
|
||||||
|
|
||||||
|
background-image: url("https://file.garden/aJzQmzrHVB4BLKwu/tumblrbg.gif");
|
||||||
|
font-family: ms gothic;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainaboutbox {
|
||||||
|
background-image: url("https://file.garden/aJzQmzrHVB4BLKwu/sky.png");
|
||||||
|
width: 990px;
|
||||||
|
height: 550px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
border: 8px double white;
|
||||||
|
box-shadow: 10px 10px 4px #ffeab0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.shelf {
|
||||||
|
background:url("https://file.garden/aJzQmzrHVB4BLKwu/MYWOOD.png");
|
||||||
|
line-height: 150px;
|
||||||
|
text-decoration: underline white 5px;
|
||||||
|
padding: 0px 20px;
|
||||||
|
outline: #5d1c27 solid 15px;
|
||||||
|
border: #863133 solid 5px;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
margin: 30px 0;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
|
||||||
|
.postz img {
|
||||||
|
width: 50px;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.mainaboutbox2 {
|
||||||
|
background-image: url("https://file.garden/aJzQmzrHVB4BLKwu/sky.png");
|
||||||
|
width: 450px;
|
||||||
|
overflow: scroll;
|
||||||
|
height: 550px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-size:20px;
|
||||||
|
padding: 2%;
|
||||||
|
border: 8px double white;
|
||||||
|
box-shadow: 10px 10px 4px #ffeab0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
|
||||||
|
padding-left: 6px;
|
||||||
|
|
||||||
|
margin-bottom:10px;
|
||||||
|
font-size: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #000;
|
||||||
|
|
||||||
|
}
|
||||||
|
#statuscafe {
|
||||||
|
padding: .5em;
|
||||||
|
background-image: url('images/xraytech.jpg');
|
||||||
|
border: 1px solid midnightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#statuscafe a {
|
||||||
|
|
||||||
|
color:black;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#statuscafe-username {
|
||||||
|
margin-bottom: .5em; color:black;
|
||||||
|
|
||||||
|
}
|
||||||
|
#statuscafe-content {
|
||||||
|
margin: 0 1em 0.5em 1em;
|
||||||
|
color:black;
|
||||||
|
|
||||||
|
}
|
||||||
|
.navigate {
|
||||||
|
background-image: url("https://file.garden/aJzQmzrHVB4BLKwu/sky.png");
|
||||||
|
border: 2px dashed yellow;
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favoritestuff {
|
||||||
|
border: 5px double white;
|
||||||
|
width: 250px;
|
||||||
|
height: 180px;
|
||||||
|
position: absolute;
|
||||||
|
background-image: url("https://file.garden/aJzQmzrHVB4BLKwu/tumblrbg.gif");
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etc {
|
||||||
|
border: 5px double white;
|
||||||
|
width: 250px;
|
||||||
|
height: 180px;
|
||||||
|
left:300px;
|
||||||
|
position: absolute;
|
||||||
|
background-image: url("https://file.garden/aJzQmzrHVB4BLKwu/tumblrbg.gif");
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.badges {
|
||||||
|
|
||||||
|
border: 5px groove white;
|
||||||
|
width: 290px;
|
||||||
|
height: 520px;
|
||||||
|
background:rgba(255, 255, 255, .5);
|
||||||
|
margin-left:650px;
|
||||||
|
overflow: scroll;
|
||||||
|
top:270px;
|
||||||
|
padding: 0.5%;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
color: #000;
|
||||||
|
background-image: url("https://file.garden/aJzQmzrHVB4BLKwu/tumblrbg.gif");
|
||||||
|
border: 4px dashed yellow;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listening {
|
||||||
|
border: 5px groove white;
|
||||||
|
width: 280px;
|
||||||
|
height: 250px;
|
||||||
|
background:rgba(255, 255, 255, .5);
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-left: 495px;
|
||||||
|
overflow: scroll;
|
||||||
|
padding: 0.5%;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#pictureofme {
|
||||||
|
|
||||||
|
width: 250px;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
background-size: 105%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
height: 250px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.maininfo {
|
||||||
|
|
||||||
|
height: 443px;
|
||||||
|
width: 600px;
|
||||||
|
border: 5px double white;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding: 7px;
|
||||||
|
position: relative;
|
||||||
|
margin-left: 10px;
|
||||||
|
background:rgba(255, 255, 255, .5);
|
||||||
|
|
||||||
|
}
|
||||||
|
#mainTable{
|
||||||
|
|
||||||
|
font-size: 19px;
|
||||||
|
|
||||||
|
font-family: 'Times New Roman', serif;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
width: 410px;
|
||||||
|
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
border-collapse: separate;
|
||||||
|
|
||||||
|
border-spacing: 10px 5px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#leftField{
|
||||||
|
|
||||||
|
width: 120px;
|
||||||
|
|
||||||
|
height: 150px;
|
||||||
|
|
||||||
|
border: 1px solid #000;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
}
|
||||||
|
middleField{
|
||||||
|
|
||||||
|
width: 120px;
|
||||||
|
|
||||||
|
height: 70px;
|
||||||
|
|
||||||
|
border: 1px solid #000;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#rightField{
|
||||||
|
|
||||||
|
width: 120px;
|
||||||
|
|
||||||
|
height: 150px;
|
||||||
|
|
||||||
|
border: 1px solid #000;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrapbox {
|
||||||
|
|
||||||
|
height: 443px;
|
||||||
|
width: 600px;
|
||||||
|
border: 5px double white;
|
||||||
|
overflow: scroll;
|
||||||
|
padding: 7px;
|
||||||
|
margin:0 auto;
|
||||||
|
background:rgba(255, 255, 255, .5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.favorites {
|
||||||
|
|
||||||
|
border: 5px groove white;
|
||||||
|
width: 200px;
|
||||||
|
height: 250px;
|
||||||
|
background:rgba(255, 255, 255, .5);
|
||||||
|
|
||||||
|
overflow: scroll;
|
||||||
|
padding: 0.5%;
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-left: 6px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.fronter {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkfronters--member-list--pronouns,
|
||||||
|
.pkfronters--member-card--pronouns
|
||||||
|
{
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkfronters--member-list--pronouns::before,
|
||||||
|
.pkfronters--member-card--pronouns::before
|
||||||
|
{
|
||||||
|
content: '\0020(';
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkfronters--member-list--pronouns::after,
|
||||||
|
.pkfronters--member-card--pronouns::after
|
||||||
|
{
|
||||||
|
content: ')\0020';
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkfronters--member-card-container
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkfronters--member-card
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column wrap;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 8em;
|
||||||
|
background: #fff2b8;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkfronters--member-card--avatar
|
||||||
|
{
|
||||||
|
min-width: 6rem;
|
||||||
|
width: 6rem;
|
||||||
|
min-height: 6rem;
|
||||||
|
height: 6rem;
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 4px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkfronters--member-card--name
|
||||||
|
{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.marquee {
|
||||||
|
margin:0 auto;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
border: 3px double white;
|
||||||
|
align-content: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
|
||||||
|
font-size: 22px;
|
||||||
|
border: 3px dotted white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css.
|
||||||
|
*
|
||||||
|
* With Propshaft, assets are served efficiently without preprocessing steps. You can still include
|
||||||
|
* application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
|
||||||
|
* cascading order, meaning styles declared later in the document or manifest will override earlier ones,
|
||||||
|
* depending on specificity.
|
||||||
|
*
|
||||||
|
* Consider organizing styles into separate files for maintainability.
|
||||||
|
*/
|
||||||
529
app/assets/stylesheets/spiraleye.css
Normal file
529
app/assets/stylesheets/spiraleye.css
Normal file
|
|
@ -0,0 +1,529 @@
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* VARIABLES */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Variables are used like this: var(--text-color) */
|
||||||
|
:root {
|
||||||
|
/* Background Colors: */
|
||||||
|
--background-color: #d1eed9;
|
||||||
|
--content-background-color: #ffffff;
|
||||||
|
--sidebar-background-color: #ffffff;
|
||||||
|
|
||||||
|
/* Text Colors: */
|
||||||
|
--text-color: #000000;
|
||||||
|
--sidebar-text-color: #000000;
|
||||||
|
--link-color: #008127;
|
||||||
|
--link-color-hover: #10ec10;
|
||||||
|
|
||||||
|
/* Text: */
|
||||||
|
--font: Monaco, monospace;
|
||||||
|
--heading-font: Lucida Console, monospace;
|
||||||
|
--font-size: 15px;
|
||||||
|
|
||||||
|
/* Other Settings: */
|
||||||
|
--margin: 10px;
|
||||||
|
--padding: 20px;
|
||||||
|
--border: 9px solid #000000;
|
||||||
|
--round-borders: 0px;
|
||||||
|
--sidebar-width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* BASICS */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainelouisa {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
font-size: var(--font-size);
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--margin);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: var(--font);
|
||||||
|
line-height: 1.2;
|
||||||
|
background: var(--background-color);
|
||||||
|
background-image: url("https://file.garden/aJzQmzrHVB4BLKwu/eyebg.png");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
/* (Text highlighted by the user) */
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
/* Text highlighted by using the <mark> element */
|
||||||
|
text-shadow: 1px 1px 4px var(--link-color);
|
||||||
|
background-color: inherit;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links: */
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:visited {
|
||||||
|
color: var(--link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.maiddnelouisa {
|
||||||
|
margin: 0 auto;
|
||||||
|
background-image: url("https://file.garden/aJzQmzrHVB4BLKwu/eyebg.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover,
|
||||||
|
a:focus {
|
||||||
|
color: var(--link-color-hover);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* LAYOUT */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
.layout {
|
||||||
|
width: 1000px;
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--margin);
|
||||||
|
grid-template: ".headerspiral .headerspiral" auto "leftSidebar .mainspiral" auto "footer footer" auto / var(--sidebar-width) auto;
|
||||||
|
/* Confused by the grid? Check out my tutorial: https://petrapixel.neocities.org/coding/positioning-tutorial#grid */
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral {
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
background: var(--content-background-color);
|
||||||
|
border: var(--border);
|
||||||
|
border-radius: var(--round-borders);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* .headerspiral */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
.headerspiral {
|
||||||
|
grid-area: header;
|
||||||
|
font-size: 1.2em;
|
||||||
|
border: var(--border);
|
||||||
|
border-radius: var(--round-borders);
|
||||||
|
background: var(--content-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral-content {
|
||||||
|
padding: var(--padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral-title {
|
||||||
|
font-family: var(--heading-font);
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral-image img {
|
||||||
|
width: 90%;
|
||||||
|
height: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* SIDEBARS */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
aside {
|
||||||
|
grid-area: aside;
|
||||||
|
border: var(--border);
|
||||||
|
border-radius: var(--round-borders);
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--sidebar-background-color);
|
||||||
|
padding: var(--padding);
|
||||||
|
color: var(--sidebar-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-sidebar {
|
||||||
|
grid-area: leftSidebar;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-sidebar {
|
||||||
|
grid-area: rightSidebar;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-family: var(--heading-font);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-section:not(:last-child) {
|
||||||
|
margin-bottom: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-section ul,
|
||||||
|
.sidebar-section ol {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-section > *:not(p):not(ul):not(ol):not(blockquote) {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar Blockquote: */
|
||||||
|
|
||||||
|
.sidebar-section blockquote {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 15px;
|
||||||
|
margin: 1em 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-section blockquote > *:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-section blockquote > *:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Site Button: */
|
||||||
|
|
||||||
|
.site-button {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-button textarea {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* FOOTER */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
.footerspiral {
|
||||||
|
grid-area: footer;
|
||||||
|
border: var(--border);
|
||||||
|
border-radius: var(--round-borders);
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 0.75em;
|
||||||
|
padding: 15px;
|
||||||
|
background: var(--content-background-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footerspiral a,
|
||||||
|
.footerspiral a:visited {
|
||||||
|
color: var(--link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footerspiral a:hover,
|
||||||
|
.footerspiral a:focus {
|
||||||
|
color: var(--link-color-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* NAVIGATION */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
nav {
|
||||||
|
margin-bottom: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav .sidebar-title {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
margin: 0 -5px;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > ul li > a,
|
||||||
|
nav > ul li > strong {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > ul li > a,
|
||||||
|
nav > ul li > details summary,
|
||||||
|
nav > ul li > strong {
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > ul li > a.active,
|
||||||
|
nav > ul li > details.active summary {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul summary {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul ul li > a {
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NAVIGATION IN .headerspiral */
|
||||||
|
|
||||||
|
.headerspiral nav {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral nav ul {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral nav ul li {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral nav ul li:first-child > a {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral nav ul li:last-child > a {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subnavigation (Drop-Down): */
|
||||||
|
|
||||||
|
.headerspiral nav ul ul {
|
||||||
|
background: var(--content-background-color);
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 10px;
|
||||||
|
padding: 0.5em;
|
||||||
|
z-index: 1;
|
||||||
|
border: var(--border);
|
||||||
|
min-width: 100%;
|
||||||
|
box-shadow: 0px 1px 5px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral nav ul li:hover ul,
|
||||||
|
.headerspiral nav ul li:focus-within ul {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral nav ul li strong {
|
||||||
|
color: var(--link-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerspiral nav ul ul li a {
|
||||||
|
display: block;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* CONTENT */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
.mainspiral {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral a,
|
||||||
|
.mainspiral a:visited {
|
||||||
|
color: var(--link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral a:hover,
|
||||||
|
.mainspiral a:focus {
|
||||||
|
color: var(--link-color-hover);
|
||||||
|
text-decoration-style: wavy;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral p,
|
||||||
|
.mainspiral .image,
|
||||||
|
.mainspiral .full-width-image,
|
||||||
|
.mainspiral .two-columns {
|
||||||
|
margin: 0.75em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral ol,
|
||||||
|
.mainspiral ul {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
padding-left: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral ol li,
|
||||||
|
.mainspiral ul li {
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral ol {
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral blockquote {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 15px;
|
||||||
|
margin: 1em 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral pre {
|
||||||
|
margin: 1em 0 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral code {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral center {
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: var(--border);
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HEADINGS: */
|
||||||
|
|
||||||
|
.mainspiral h1,
|
||||||
|
.mainspiral h2,
|
||||||
|
.mainspiral h3,
|
||||||
|
.mainspiral h4,
|
||||||
|
.mainspiral h5,
|
||||||
|
.mainspiral h6 {
|
||||||
|
font-family: var(--heading-font);
|
||||||
|
margin-bottom: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral h1:first-child,
|
||||||
|
.mainspiral h2:first-child,
|
||||||
|
.mainspiral h3:first-child,
|
||||||
|
.mainspiral h4:first-child,
|
||||||
|
.mainspiral h5:first-child,
|
||||||
|
.mainspiral h6:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral h2 {
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral h3 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral h4 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral h5 {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mainspiral h6 {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* COLUMNS: */
|
||||||
|
|
||||||
|
.two-columns {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-columns > * {
|
||||||
|
flex: 1 1 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-columns > *:first-child {
|
||||||
|
padding-right: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-columns > *:last-child {
|
||||||
|
padding-left: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* CONTENT IMAGES */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
.image {
|
||||||
|
display: block;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width-image {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.images {
|
||||||
|
display: flex;
|
||||||
|
width: calc(100% + 5px + 5px);
|
||||||
|
margin-left: -5px;
|
||||||
|
margin-right: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.images img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
padding: 5px;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
/* ACCESSIBILITY */
|
||||||
|
/* -------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* please do not remove this. */
|
||||||
|
|
||||||
|
#skip-to-content-link {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
line-height: 1;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
background-color: var(--content-background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
transform: translateY(-3rem);
|
||||||
|
transition: transform 0.1s ease-in;
|
||||||
|
z-index: 99999999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
#skip-to-content-link:focus,
|
||||||
|
#skip-to-content-link:focus-within {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
15
app/assets/stylesheets/spirit.css
Normal file
15
app/assets/stylesheets/spirit.css
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
.bodyspirit {
|
||||||
|
line-height: 1;
|
||||||
|
background-color: pink;
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
background-image: url('https://i.ibb.co/kgSHN1YJ/backstar-1.gif');
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.bodyspirit a {
|
||||||
|
color: lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
16
app/channels/application_cable/connection.rb
Normal file
16
app/channels/application_cable/connection.rb
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
module ApplicationCable
|
||||||
|
class Connection < ActionCable::Connection::Base
|
||||||
|
identified_by :current_user
|
||||||
|
|
||||||
|
def connect
|
||||||
|
set_current_user || reject_unauthorized_connection
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def set_current_user
|
||||||
|
if session = Session.find_by(id: cookies.signed[:session_id])
|
||||||
|
self.current_user = session.user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
23
app/controllers/application_controller.rb
Normal file
23
app/controllers/application_controller.rb
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
class ApplicationController < ActionController::Base
|
||||||
|
include Authentication
|
||||||
|
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
|
||||||
|
allow_browser versions: :modern
|
||||||
|
|
||||||
|
# Changes to the importmap will invalidate the etag for HTML responses
|
||||||
|
stale_when_importmap_changes
|
||||||
|
helper_method :current_user
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def current_user
|
||||||
|
|
||||||
|
@current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def authenticate_user!
|
||||||
|
unless Current.user
|
||||||
|
redirect_to new_session_path, alert: "You must be logged in to access this page."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
0
app/controllers/concerns/.keep
Normal file
0
app/controllers/concerns/.keep
Normal file
52
app/controllers/concerns/authentication.rb
Normal file
52
app/controllers/concerns/authentication.rb
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
module Authentication
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
before_action :require_authentication
|
||||||
|
helper_method :authenticated?
|
||||||
|
end
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
def allow_unauthenticated_access(**options)
|
||||||
|
skip_before_action :require_authentication, **options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def authenticated?
|
||||||
|
resume_session
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_authentication
|
||||||
|
resume_session || request_authentication
|
||||||
|
end
|
||||||
|
|
||||||
|
def resume_session
|
||||||
|
Current.session ||= find_session_by_cookie
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_session_by_cookie
|
||||||
|
Session.find_by(id: cookies.signed[:session_id]) if cookies.signed[:session_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_authentication
|
||||||
|
session[:return_to_after_authenticating] = request.url
|
||||||
|
redirect_to new_session_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_authentication_url
|
||||||
|
session.delete(:return_to_after_authenticating) || root_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_new_session_for(user)
|
||||||
|
user.sessions.create!(user_agent: request.user_agent, ip_address: request.remote_ip).tap do |session|
|
||||||
|
Current.session = session
|
||||||
|
cookies.signed.permanent[:session_id] = { value: session.id, httponly: true, same_site: :lax }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def terminate_session
|
||||||
|
Current.session.destroy
|
||||||
|
cookies.delete(:session_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
10
app/controllers/fun_controller.rb
Normal file
10
app/controllers/fun_controller.rb
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
class FunController < ApplicationController
|
||||||
|
allow_unauthenticated_access
|
||||||
|
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
def spirit
|
||||||
|
end
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
end
|
||||||
10
app/controllers/miniblog_controller.rb
Normal file
10
app/controllers/miniblog_controller.rb
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
class MiniblogController < ApplicationController
|
||||||
|
|
||||||
|
allow_unauthenticated_access
|
||||||
|
|
||||||
|
def index
|
||||||
|
@updates = Miniblog.order(created_at: :desc)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
35
app/controllers/passwords_controller.rb
Normal file
35
app/controllers/passwords_controller.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
class PasswordsController < ApplicationController
|
||||||
|
allow_unauthenticated_access
|
||||||
|
before_action :set_user_by_token, only: %i[ edit update ]
|
||||||
|
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_password_path, alert: "Try again later." }
|
||||||
|
|
||||||
|
def new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
if user = User.find_by(email_address: params[:email_address])
|
||||||
|
PasswordsMailer.reset(user).deliver_later
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to new_session_path, notice: "Password reset instructions sent (if user with that email address exists)."
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if @user.update(params.permit(:password, :password_confirmation))
|
||||||
|
@user.sessions.destroy_all
|
||||||
|
redirect_to new_session_path, notice: "Password has been reset."
|
||||||
|
else
|
||||||
|
redirect_to edit_password_path(params[:token]), alert: "Passwords did not match."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def set_user_by_token
|
||||||
|
@user = User.find_by_password_reset_token!(params[:token])
|
||||||
|
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||||
|
redirect_to new_password_path, alert: "Password reset link is invalid or has expired."
|
||||||
|
end
|
||||||
|
end
|
||||||
63
app/controllers/posts_controller.rb
Normal file
63
app/controllers/posts_controller.rb
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
class PostsController < ApplicationController
|
||||||
|
|
||||||
|
before_action :authenticate_user!, only: [:new, :create, :edit, :update, :destroy]
|
||||||
|
allow_unauthenticated_access(only: [:index, :show])
|
||||||
|
|
||||||
|
def index
|
||||||
|
@posts = Post.all.order(created_at: :desc)
|
||||||
|
@post = Post.find_by(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@post = Post.find(params[:id])
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@user = Current.user
|
||||||
|
@post = Post.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@post = Post.new(post_params)
|
||||||
|
if @post.save
|
||||||
|
redirect_to posts_path, notice: "post created."
|
||||||
|
else
|
||||||
|
render :new, status: :unprocessable_entity, notice: "U fucked up somewhere."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@post = Post.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@post = Post.find(params[:id])
|
||||||
|
if @post.update(post_params)
|
||||||
|
redirect_to posts_path, notice: "post edited."
|
||||||
|
else
|
||||||
|
render :edit, status: :unprocessable_entity, notice: "U fucked up somewhere."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@post = Post.find(params[:id])
|
||||||
|
@post.destroy
|
||||||
|
|
||||||
|
redirect_to posts_path, notice: "Post deleted."
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
|
||||||
|
def post_params
|
||||||
|
params.require(:post).permit(:text, :icon_image)
|
||||||
|
end
|
||||||
|
def set_user
|
||||||
|
@user = Current.user
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
53
app/controllers/scraps_controller.rb
Normal file
53
app/controllers/scraps_controller.rb
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
class ScrapsController < ApplicationController
|
||||||
|
allow_unauthenticated_access(only: %i[index show])
|
||||||
|
def index
|
||||||
|
@scraps = Scrap.all
|
||||||
|
end
|
||||||
|
def edit
|
||||||
|
@scrap = Scrap.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@scrap = Scrap.find(params[:id])
|
||||||
|
if @scrap.update(scrap_params)
|
||||||
|
redirect_to posts_path, notice: "scrap edited."
|
||||||
|
else
|
||||||
|
render :edit, status: :unprocessable_entity, notice: "U fucked up somewhere."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@scrap = Scrap.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@scrap = Scrap.new(scrap_params)
|
||||||
|
if @scrap.save
|
||||||
|
redirect_to scraps_path, notice: "scrap created."
|
||||||
|
else
|
||||||
|
render :new, status: :unprocessable_entity, notice: "U fucked up somewhere."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@scrap = Scrap.find(params[:id])
|
||||||
|
@scrap.destroy
|
||||||
|
redirect_to scrap_path(@scrap), notice: "Member deleted."
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@scrap = Scrap.find_by(id: params[:id])
|
||||||
|
if @scrap.nil?
|
||||||
|
flash[:alert] = "Scrapbook entry not found"
|
||||||
|
redirect_to scraps_path
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def scrap_params
|
||||||
|
params.require(:scrap).permit(:text, :image, :music, :mood, :notes)
|
||||||
|
end
|
||||||
|
end
|
||||||
21
app/controllers/sessions_controller.rb
Normal file
21
app/controllers/sessions_controller.rb
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
class SessionsController < ApplicationController
|
||||||
|
allow_unauthenticated_access only: %i[ new create ]
|
||||||
|
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." }
|
||||||
|
|
||||||
|
def new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
if user = User.authenticate_by(params.permit(:email_address, :password))
|
||||||
|
start_new_session_for user
|
||||||
|
redirect_to after_authentication_url
|
||||||
|
else
|
||||||
|
redirect_to new_session_path, alert: "Try another email address or password."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
terminate_session
|
||||||
|
redirect_to new_session_path, status: :see_other
|
||||||
|
end
|
||||||
|
end
|
||||||
6
app/controllers/shrines_controller.rb
Normal file
6
app/controllers/shrines_controller.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
class ShrinesController < ApplicationController
|
||||||
|
allow_unauthenticated_access
|
||||||
|
|
||||||
|
def spiraleye
|
||||||
|
end
|
||||||
|
end
|
||||||
57
app/controllers/updates_controller.rb
Normal file
57
app/controllers/updates_controller.rb
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
class UpdatesController < ApplicationController
|
||||||
|
|
||||||
|
before_action :authenticate_user!, only: [:new, :create, :edit, :update, :destroy]
|
||||||
|
allow_unauthenticated_access(only: [:index, :show])
|
||||||
|
|
||||||
|
def index
|
||||||
|
@updates = Update.order(created_at: :desc)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@user = Current.user
|
||||||
|
@update = Update.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@update = Update.new(update_params)
|
||||||
|
if Updates.save
|
||||||
|
redirect_to updates_path, notice: "Update created."
|
||||||
|
else
|
||||||
|
render :new, status: :unprocessable_entity, notice: "U fucked up somewhere."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if @update.update(post_params)
|
||||||
|
redirect_to updates_path, notice: "Update edited."
|
||||||
|
else
|
||||||
|
render :edit, status: :unprocessable_entity, notice: "U fucked up somewhere."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@update.destroy
|
||||||
|
redirect_to updates_path, notice: "Post deleted."
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
|
||||||
|
def update_params
|
||||||
|
params.require(:update).permit(:text, :icon_image)
|
||||||
|
end
|
||||||
|
def set_user
|
||||||
|
@user = Current.user
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
10
app/controllers/welcome_controller.rb
Normal file
10
app/controllers/welcome_controller.rb
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
class WelcomeController < ApplicationController
|
||||||
|
allow_unauthenticated_access(only: %i[index show homepage])
|
||||||
|
|
||||||
|
def index
|
||||||
|
end
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
def homepage
|
||||||
|
end
|
||||||
|
end
|
||||||
2
app/helpers/application_helper.rb
Normal file
2
app/helpers/application_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
module ApplicationHelper
|
||||||
|
end
|
||||||
2
app/helpers/fun_helper.rb
Normal file
2
app/helpers/fun_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
module FunHelper
|
||||||
|
end
|
||||||
2
app/helpers/miniblog_helper.rb
Normal file
2
app/helpers/miniblog_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
module MiniblogHelper
|
||||||
|
end
|
||||||
2
app/helpers/posts_helper.rb
Normal file
2
app/helpers/posts_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
module PostsHelper
|
||||||
|
end
|
||||||
2
app/helpers/scraps_helper.rb
Normal file
2
app/helpers/scraps_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
module ScrapsHelper
|
||||||
|
end
|
||||||
2
app/helpers/shrines_helper.rb
Normal file
2
app/helpers/shrines_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
module ShrinesHelper
|
||||||
|
end
|
||||||
2
app/helpers/updates_helper.rb
Normal file
2
app/helpers/updates_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
module UpdatesHelper
|
||||||
|
end
|
||||||
2
app/helpers/welcome_helper.rb
Normal file
2
app/helpers/welcome_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
module WelcomeHelper
|
||||||
|
end
|
||||||
6
app/javascript/application.js
Normal file
6
app/javascript/application.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
|
||||||
|
import "@hotwired/turbo-rails"
|
||||||
|
import "controllers"
|
||||||
|
|
||||||
|
import "trix"
|
||||||
|
import "@rails/actiontext"
|
||||||
9
app/javascript/controllers/application.js
Normal file
9
app/javascript/controllers/application.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { Application } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
const application = Application.start()
|
||||||
|
|
||||||
|
// Configure Stimulus development experience
|
||||||
|
application.debug = false
|
||||||
|
window.Stimulus = application
|
||||||
|
|
||||||
|
export { application }
|
||||||
7
app/javascript/controllers/hello_controller.js
Normal file
7
app/javascript/controllers/hello_controller.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
connect() {
|
||||||
|
this.element.textContent = "Hello World!"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
app/javascript/controllers/index.js
Normal file
4
app/javascript/controllers/index.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Import and register all your controllers from the importmap via controllers/**/*_controller
|
||||||
|
import { application } from "controllers/application"
|
||||||
|
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
|
||||||
|
eagerLoadControllersFrom("controllers", application)
|
||||||
544
app/javascript/sorter.js
Normal file
544
app/javascript/sorter.js
Normal file
|
|
@ -0,0 +1,544 @@
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var namMember = new Array(
|
||||||
|
|
||||||
|
"John Sheridan",
|
||||||
|
|
||||||
|
"Delenn",
|
||||||
|
|
||||||
|
"Londo Mollari",
|
||||||
|
|
||||||
|
"Jeffrey Sinclair",
|
||||||
|
|
||||||
|
"Michael Garibaldi",
|
||||||
|
|
||||||
|
"G'Kar",
|
||||||
|
|
||||||
|
"Lyta Alexander",
|
||||||
|
|
||||||
|
"Talia Winters",
|
||||||
|
|
||||||
|
"Susan Ivanova",
|
||||||
|
|
||||||
|
"Na'toth",
|
||||||
|
|
||||||
|
"Lennier",
|
||||||
|
|
||||||
|
"Vir Cotto",
|
||||||
|
|
||||||
|
"Mr. Morden",
|
||||||
|
|
||||||
|
"Antono Refa",
|
||||||
|
|
||||||
|
"Cartagia",
|
||||||
|
|
||||||
|
"Virini",
|
||||||
|
|
||||||
|
"Alfred Bester",
|
||||||
|
|
||||||
|
"David Corwin",
|
||||||
|
|
||||||
|
"Marcus Cole",
|
||||||
|
|
||||||
|
"Stephen Franklin",
|
||||||
|
|
||||||
|
"Byron Gordon",
|
||||||
|
|
||||||
|
"Neroon",
|
||||||
|
|
||||||
|
"Elizabeth Lochley",
|
||||||
|
|
||||||
|
"Zack Allan",
|
||||||
|
|
||||||
|
"Lorien",
|
||||||
|
|
||||||
|
"Warren Keffer",
|
||||||
|
|
||||||
|
"Ambassador Kosh",
|
||||||
|
|
||||||
|
"Ulkesh/Kosh II",
|
||||||
|
|
||||||
|
"Zathras",
|
||||||
|
|
||||||
|
"Ta'Lon",
|
||||||
|
|
||||||
|
"Catherine Sakai"
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//*********************************************************
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var lstMember = new Array();
|
||||||
|
|
||||||
|
var parent = new Array();
|
||||||
|
|
||||||
|
var equal = new Array();
|
||||||
|
|
||||||
|
var rec = new Array();
|
||||||
|
|
||||||
|
var cmp1,cmp2;
|
||||||
|
|
||||||
|
var head1,head2;
|
||||||
|
|
||||||
|
var nrec;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var numQuestion;
|
||||||
|
|
||||||
|
var totalSize;
|
||||||
|
|
||||||
|
var finishSize;
|
||||||
|
|
||||||
|
var finishFlag;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//The initialization of the variable+++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
function initList(){
|
||||||
|
|
||||||
|
var n = 0;
|
||||||
|
|
||||||
|
var mid;
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//The sequence that you should sort
|
||||||
|
|
||||||
|
lstMember[n] = new Array();
|
||||||
|
|
||||||
|
for (i=0; i<namMember.length; i++) {
|
||||||
|
|
||||||
|
lstMember[n][i] = i;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
parent[n] = -1;
|
||||||
|
|
||||||
|
totalSize = 0;
|
||||||
|
|
||||||
|
n++;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (i=0; i<lstMember.length; i++) {
|
||||||
|
|
||||||
|
//And element divides it in two/more than two
|
||||||
|
|
||||||
|
//Increase divided sequence of last in first member
|
||||||
|
|
||||||
|
if(lstMember[i].length>=2) {
|
||||||
|
|
||||||
|
mid = Math.ceil(lstMember[i].length/2);
|
||||||
|
|
||||||
|
lstMember[n] = new Array();
|
||||||
|
|
||||||
|
lstMember[n] = lstMember[i].slice(0,mid);
|
||||||
|
|
||||||
|
totalSize += lstMember[n].length;
|
||||||
|
|
||||||
|
parent[n] = i;
|
||||||
|
|
||||||
|
n++;
|
||||||
|
|
||||||
|
lstMember[n] = new Array();
|
||||||
|
|
||||||
|
lstMember[n] = lstMember[i].slice(mid,lstMember[i].length);
|
||||||
|
|
||||||
|
totalSize += lstMember[n].length;
|
||||||
|
|
||||||
|
parent[n] = i;
|
||||||
|
|
||||||
|
n++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Preserve this sequence
|
||||||
|
|
||||||
|
for (i=0; i<namMember.length; i++) {
|
||||||
|
|
||||||
|
rec[i] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nrec = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//List that keeps your results
|
||||||
|
|
||||||
|
//Value of link initial
|
||||||
|
|
||||||
|
// Value of link initial
|
||||||
|
|
||||||
|
for (i=0; i<=namMember.length; i++) {
|
||||||
|
|
||||||
|
equal[i] = -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cmp1 = lstMember.length-2;
|
||||||
|
|
||||||
|
cmp2 = lstMember.length-1;
|
||||||
|
|
||||||
|
head1 = 0;
|
||||||
|
|
||||||
|
head2 = 0;
|
||||||
|
|
||||||
|
numQuestion = 1;
|
||||||
|
|
||||||
|
finishSize = 0;
|
||||||
|
|
||||||
|
finishFlag = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//リストのソート+++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
//flag:Don't know characters
|
||||||
|
|
||||||
|
// -1:Chose the left
|
||||||
|
|
||||||
|
// 0:Tie
|
||||||
|
|
||||||
|
// 1:Chose the right
|
||||||
|
|
||||||
|
function sortList(flag){
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
|
var str;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//rec preservation
|
||||||
|
|
||||||
|
if (flag<0) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
while (equal[rec[nrec-1]]!=-1) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (flag>0) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
while (equal[rec[nrec-1]]!=-1) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
while (equal[rec[nrec-1]]!=-1) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
equal[rec[nrec-1]] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
while (equal[rec[nrec-1]]!=-1) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Processing after finishing with one list
|
||||||
|
|
||||||
|
if (head1<lstMember[cmp1].length && head2==lstMember[cmp2].length) {
|
||||||
|
|
||||||
|
//List the remainder of cmp2 copies, list cmp1 copies when finished scanning
|
||||||
|
|
||||||
|
while (head1<lstMember[cmp1].length){
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (head1==lstMember[cmp1].length && head2<lstMember[cmp2].length) {
|
||||||
|
|
||||||
|
//List the remainder of cmp1 copies, list cmp2 copies when finished scanning
|
||||||
|
|
||||||
|
while (head2<lstMember[cmp2].length){
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//When it arrives at the end of both lists
|
||||||
|
|
||||||
|
//Update a pro list
|
||||||
|
|
||||||
|
if (head1==lstMember[cmp1].length && head2==lstMember[cmp2].length) {
|
||||||
|
|
||||||
|
for (i=0; i<lstMember[cmp1].length+lstMember[cmp2].length; i++) {
|
||||||
|
|
||||||
|
lstMember[parent[cmp1]][i] = rec[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
lstMember.pop();
|
||||||
|
|
||||||
|
lstMember.pop();
|
||||||
|
|
||||||
|
cmp1 = cmp1-2;
|
||||||
|
|
||||||
|
cmp2 = cmp2-2;
|
||||||
|
|
||||||
|
head1 = 0;
|
||||||
|
|
||||||
|
head2 = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Initialize the rec before performing the new comparison
|
||||||
|
|
||||||
|
if (head1==0 && head2==0) {
|
||||||
|
|
||||||
|
for (i=0; i<namMember.length; i++) {
|
||||||
|
|
||||||
|
rec[i] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nrec = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (cmp1<0) {
|
||||||
|
|
||||||
|
str = "battle #"+(numQuestion-1)+"<br>"+Math.floor(finishSize*100/totalSize)+"% sorted.";
|
||||||
|
|
||||||
|
document.getElementById("battleNumber").innerHTML = str;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
showResult();
|
||||||
|
|
||||||
|
finishFlag = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
showImage();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//The results+++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
//順位=Rank/Grade/Position/Standing/Status
|
||||||
|
|
||||||
|
//名前=Identification term
|
||||||
|
|
||||||
|
function showResult() {
|
||||||
|
|
||||||
|
var ranking = 1;
|
||||||
|
|
||||||
|
var sameRank = 1;
|
||||||
|
|
||||||
|
var str = "";
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
str += "<table style=\"width:200px; font-size:18px; line-height:120%; margin-left:auto; margin-right:auto; border:1px solid #000; border-collapse:collapse\" align=\"center\">";
|
||||||
|
|
||||||
|
str += "<tr><td style=\"color:#ffffff; background-color:#e097d9; text-align:center;\">rank<\/td><td style=\"color:#ffffff; background-color:#e097d9; text-align:center;\">options<\/td><\/tr>";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (i=0; i<namMember.length; i++) {
|
||||||
|
|
||||||
|
str += "<tr><td style=\"border:1px solid #000; text-align:center; padding-right:5px;\">"+ranking+"<\/td><td style=\"border:1px solid #000; padding-left:5px;\">"+namMember[lstMember[0][i]]+"<\/td><\/tr>";
|
||||||
|
|
||||||
|
if (i<namMember.length-1) {
|
||||||
|
|
||||||
|
if (equal[lstMember[0][i]]==lstMember[0][i+1]) {
|
||||||
|
|
||||||
|
sameRank++;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ranking += sameRank;
|
||||||
|
|
||||||
|
sameRank = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
str += "<\/table>";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById("resultField").innerHTML = str;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Indicates two elements to compare+++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
function showImage() {
|
||||||
|
|
||||||
|
var str0 = "battle #"+numQuestion+"<br>"+Math.floor(finishSize*100/totalSize)+"% sorted.";
|
||||||
|
|
||||||
|
var str1 = ""+toNameFace(lstMember[cmp1][head1]);
|
||||||
|
|
||||||
|
var str2 = ""+toNameFace(lstMember[cmp2][head2]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById("battleNumber").innerHTML = str0;
|
||||||
|
|
||||||
|
document.getElementById("leftField").innerHTML = str1;
|
||||||
|
|
||||||
|
document.getElementById("rightField").innerHTML = str2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
numQuestion++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Convert numeric value into a name (emoticon)+++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
function toNameFace(n){
|
||||||
|
|
||||||
|
var str = namMember[n];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
str += '<br />';
|
||||||
|
|
||||||
|
switch(n) {
|
||||||
|
|
||||||
|
//case -1 Because it is a sample, delete it
|
||||||
|
|
||||||
|
case -1: str+=""; break;
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return str;
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
7
app/jobs/application_job.rb
Normal file
7
app/jobs/application_job.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
class ApplicationJob < ActiveJob::Base
|
||||||
|
# Automatically retry jobs that encountered a deadlock
|
||||||
|
# retry_on ActiveRecord::Deadlocked
|
||||||
|
|
||||||
|
# Most jobs are safe to ignore if the underlying records are no longer available
|
||||||
|
# discard_on ActiveJob::DeserializationError
|
||||||
|
end
|
||||||
4
app/mailers/application_mailer.rb
Normal file
4
app/mailers/application_mailer.rb
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
class ApplicationMailer < ActionMailer::Base
|
||||||
|
default from: "from@example.com"
|
||||||
|
layout "mailer"
|
||||||
|
end
|
||||||
6
app/mailers/passwords_mailer.rb
Normal file
6
app/mailers/passwords_mailer.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
class PasswordsMailer < ApplicationMailer
|
||||||
|
def reset(user)
|
||||||
|
@user = user
|
||||||
|
mail subject: "Reset your password", to: user.email_address
|
||||||
|
end
|
||||||
|
end
|
||||||
3
app/models/application_record.rb
Normal file
3
app/models/application_record.rb
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
class ApplicationRecord < ActiveRecord::Base
|
||||||
|
primary_abstract_class
|
||||||
|
end
|
||||||
0
app/models/concerns/.keep
Normal file
0
app/models/concerns/.keep
Normal file
4
app/models/current.rb
Normal file
4
app/models/current.rb
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
class Current < ActiveSupport::CurrentAttributes
|
||||||
|
attribute :session
|
||||||
|
delegate :user, to: :session, allow_nil: true
|
||||||
|
end
|
||||||
2
app/models/miniblog.rb
Normal file
2
app/models/miniblog.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
class Miniblog < ApplicationRecord
|
||||||
|
end
|
||||||
6
app/models/post.rb
Normal file
6
app/models/post.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
class Post < ApplicationRecord
|
||||||
|
has_one_attached :icon_image
|
||||||
|
has_rich_text :text
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
5
app/models/scrap.rb
Normal file
5
app/models/scrap.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
class Scrap < ApplicationRecord
|
||||||
|
has_one_attached :image
|
||||||
|
has_rich_text :text
|
||||||
|
has_rich_text :notes
|
||||||
|
end
|
||||||
3
app/models/session.rb
Normal file
3
app/models/session.rb
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
class Session < ApplicationRecord
|
||||||
|
belongs_to :user
|
||||||
|
end
|
||||||
9
app/models/update.rb
Normal file
9
app/models/update.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
class Update < ApplicationRecord
|
||||||
|
belongs_to :member
|
||||||
|
has_one_attached :icon_image
|
||||||
|
has_rich_text :text
|
||||||
|
validates :text, presence:true
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
6
app/models/user.rb
Normal file
6
app/models/user.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
class User < ApplicationRecord
|
||||||
|
has_secure_password
|
||||||
|
has_many :sessions, dependent: :destroy
|
||||||
|
has_many :updates
|
||||||
|
normalizes :email_address, with: ->(e) { e.strip.downcase }
|
||||||
|
end
|
||||||
14
app/views/active_storage/blobs/_blob.html.erb
Normal file
14
app/views/active_storage/blobs/_blob.html.erb
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
|
||||||
|
<% if blob.representable? %>
|
||||||
|
<%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<figcaption class="attachment__caption">
|
||||||
|
<% if caption = blob.try(:caption) %>
|
||||||
|
<%= caption %>
|
||||||
|
<% else %>
|
||||||
|
<span class="attachment__name"><%= blob.filename %></span>
|
||||||
|
<span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
|
||||||
|
<% end %>
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
602
app/views/fun/b5sorter.html.erb
Normal file
602
app/views/fun/b5sorter.html.erb
Normal file
|
|
@ -0,0 +1,602 @@
|
||||||
|
|
||||||
|
<%= javascript_tag do %>
|
||||||
|
var namMember = new Array(
|
||||||
|
|
||||||
|
"John Sheridan",
|
||||||
|
|
||||||
|
"Delenn",
|
||||||
|
|
||||||
|
"Londo Mollari",
|
||||||
|
|
||||||
|
"Jeffrey Sinclair",
|
||||||
|
|
||||||
|
"Michael Garibaldi",
|
||||||
|
|
||||||
|
"G'Kar",
|
||||||
|
|
||||||
|
"Lyta Alexander",
|
||||||
|
|
||||||
|
"Talia Winters",
|
||||||
|
|
||||||
|
"Susan Ivanova",
|
||||||
|
|
||||||
|
"Na'toth",
|
||||||
|
|
||||||
|
"Lennier",
|
||||||
|
|
||||||
|
"Vir Cotto",
|
||||||
|
|
||||||
|
"Mr. Morden",
|
||||||
|
|
||||||
|
"Antono Refa",
|
||||||
|
|
||||||
|
"Cartagia",
|
||||||
|
|
||||||
|
"Virini",
|
||||||
|
|
||||||
|
"Alfred Bester",
|
||||||
|
|
||||||
|
"David Corwin",
|
||||||
|
|
||||||
|
"Marcus Cole",
|
||||||
|
|
||||||
|
"Stephen Franklin",
|
||||||
|
|
||||||
|
"Byron Gordon",
|
||||||
|
|
||||||
|
"Neroon",
|
||||||
|
|
||||||
|
"Elizabeth Lochley",
|
||||||
|
|
||||||
|
"Zack Allan",
|
||||||
|
|
||||||
|
"Lorien",
|
||||||
|
|
||||||
|
"Warren Keffer",
|
||||||
|
|
||||||
|
"Ambassador Kosh",
|
||||||
|
|
||||||
|
"Ulkesh/Kosh II",
|
||||||
|
|
||||||
|
"Zathras",
|
||||||
|
|
||||||
|
"Ta'Lon",
|
||||||
|
|
||||||
|
"Catherine Sakai"
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//*********************************************************
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var lstMember = new Array();
|
||||||
|
|
||||||
|
var parent = new Array();
|
||||||
|
|
||||||
|
var equal = new Array();
|
||||||
|
|
||||||
|
var rec = new Array();
|
||||||
|
|
||||||
|
var cmp1,cmp2;
|
||||||
|
|
||||||
|
var head1,head2;
|
||||||
|
|
||||||
|
var nrec;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var numQuestion;
|
||||||
|
|
||||||
|
var totalSize;
|
||||||
|
|
||||||
|
var finishSize;
|
||||||
|
|
||||||
|
var finishFlag;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//The initialization of the variable+++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
function initList(){
|
||||||
|
|
||||||
|
var n = 0;
|
||||||
|
|
||||||
|
var mid;
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//The sequence that you should sort
|
||||||
|
|
||||||
|
lstMember[n] = new Array();
|
||||||
|
|
||||||
|
for (i=0; i<namMember.length; i++) {
|
||||||
|
|
||||||
|
lstMember[n][i] = i;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
parent[n] = -1;
|
||||||
|
|
||||||
|
totalSize = 0;
|
||||||
|
|
||||||
|
n++;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (i=0; i<lstMember.length; i++) {
|
||||||
|
|
||||||
|
//And element divides it in two/more than two
|
||||||
|
|
||||||
|
//Increase divided sequence of last in first member
|
||||||
|
|
||||||
|
if(lstMember[i].length>=2) {
|
||||||
|
|
||||||
|
mid = Math.ceil(lstMember[i].length/2);
|
||||||
|
|
||||||
|
lstMember[n] = new Array();
|
||||||
|
|
||||||
|
lstMember[n] = lstMember[i].slice(0,mid);
|
||||||
|
|
||||||
|
totalSize += lstMember[n].length;
|
||||||
|
|
||||||
|
parent[n] = i;
|
||||||
|
|
||||||
|
n++;
|
||||||
|
|
||||||
|
lstMember[n] = new Array();
|
||||||
|
|
||||||
|
lstMember[n] = lstMember[i].slice(mid,lstMember[i].length);
|
||||||
|
|
||||||
|
totalSize += lstMember[n].length;
|
||||||
|
|
||||||
|
parent[n] = i;
|
||||||
|
|
||||||
|
n++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Preserve this sequence
|
||||||
|
|
||||||
|
for (i=0; i<namMember.length; i++) {
|
||||||
|
|
||||||
|
rec[i] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nrec = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//List that keeps your results
|
||||||
|
|
||||||
|
//Value of link initial
|
||||||
|
|
||||||
|
// Value of link initial
|
||||||
|
|
||||||
|
for (i=0; i<=namMember.length; i++) {
|
||||||
|
|
||||||
|
equal[i] = -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cmp1 = lstMember.length-2;
|
||||||
|
|
||||||
|
cmp2 = lstMember.length-1;
|
||||||
|
|
||||||
|
head1 = 0;
|
||||||
|
|
||||||
|
head2 = 0;
|
||||||
|
|
||||||
|
numQuestion = 1;
|
||||||
|
|
||||||
|
finishSize = 0;
|
||||||
|
|
||||||
|
finishFlag = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//リストのソート+++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
//flag:Don't know characters
|
||||||
|
|
||||||
|
// -1:Chose the left
|
||||||
|
|
||||||
|
// 0:Tie
|
||||||
|
|
||||||
|
// 1:Chose the right
|
||||||
|
|
||||||
|
function sortList(flag){
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
|
var str;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//rec preservation
|
||||||
|
|
||||||
|
if (flag<0) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
while (equal[rec[nrec-1]]!=-1) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (flag>0) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
while (equal[rec[nrec-1]]!=-1) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
while (equal[rec[nrec-1]]!=-1) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
equal[rec[nrec-1]] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
while (equal[rec[nrec-1]]!=-1) {
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Processing after finishing with one list
|
||||||
|
|
||||||
|
if (head1<lstMember[cmp1].length && head2==lstMember[cmp2].length) {
|
||||||
|
|
||||||
|
//List the remainder of cmp2 copies, list cmp1 copies when finished scanning
|
||||||
|
|
||||||
|
while (head1<lstMember[cmp1].length){
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp1][head1];
|
||||||
|
|
||||||
|
head1++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (head1==lstMember[cmp1].length && head2<lstMember[cmp2].length) {
|
||||||
|
|
||||||
|
//List the remainder of cmp1 copies, list cmp2 copies when finished scanning
|
||||||
|
|
||||||
|
while (head2<lstMember[cmp2].length){
|
||||||
|
|
||||||
|
rec[nrec] = lstMember[cmp2][head2];
|
||||||
|
|
||||||
|
head2++;
|
||||||
|
|
||||||
|
nrec++;
|
||||||
|
|
||||||
|
finishSize++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//When it arrives at the end of both lists
|
||||||
|
|
||||||
|
//Update a pro list
|
||||||
|
|
||||||
|
if (head1==lstMember[cmp1].length && head2==lstMember[cmp2].length) {
|
||||||
|
|
||||||
|
for (i=0; i<lstMember[cmp1].length+lstMember[cmp2].length; i++) {
|
||||||
|
|
||||||
|
lstMember[parent[cmp1]][i] = rec[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
lstMember.pop();
|
||||||
|
|
||||||
|
lstMember.pop();
|
||||||
|
|
||||||
|
cmp1 = cmp1-2;
|
||||||
|
|
||||||
|
cmp2 = cmp2-2;
|
||||||
|
|
||||||
|
head1 = 0;
|
||||||
|
|
||||||
|
head2 = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Initialize the rec before performing the new comparison
|
||||||
|
|
||||||
|
if (head1==0 && head2==0) {
|
||||||
|
|
||||||
|
for (i=0; i<namMember.length; i++) {
|
||||||
|
|
||||||
|
rec[i] = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nrec = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (cmp1<0) {
|
||||||
|
|
||||||
|
str = "battle #"+(numQuestion-1)+"<br>"+Math.floor(finishSize*100/totalSize)+"% sorted.";
|
||||||
|
|
||||||
|
document.getElementById("battleNumber").innerHTML = str;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
showResult();
|
||||||
|
|
||||||
|
finishFlag = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
showImage();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//The results+++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
//順位=Rank/Grade/Position/Standing/Status
|
||||||
|
|
||||||
|
//名前=Identification term
|
||||||
|
|
||||||
|
function showResult() {
|
||||||
|
|
||||||
|
var ranking = 1;
|
||||||
|
|
||||||
|
var sameRank = 1;
|
||||||
|
|
||||||
|
var str = "";
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
str += "<table style=\"width:200px; font-size:18px; line-height:120%; margin-left:auto; margin-right:auto; border:1px solid #000; border-collapse:collapse\" align=\"center\">";
|
||||||
|
|
||||||
|
str += "<tr><td style=\"color:#ffffff; background-color:#e097d9; text-align:center;\">rank<\/td><td style=\"color:#ffffff; background-color:#e097d9; text-align:center;\">options<\/td><\/tr>";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (i=0; i<namMember.length; i++) {
|
||||||
|
|
||||||
|
str += "<tr><td style=\"border:1px solid #000; text-align:center; padding-right:5px;\">"+ranking+"<\/td><td style=\"border:1px solid #000; padding-left:5px;\">"+namMember[lstMember[0][i]]+"<\/td><\/tr>";
|
||||||
|
|
||||||
|
if (i<namMember.length-1) {
|
||||||
|
|
||||||
|
if (equal[lstMember[0][i]]==lstMember[0][i+1]) {
|
||||||
|
|
||||||
|
sameRank++;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ranking += sameRank;
|
||||||
|
|
||||||
|
sameRank = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
str += "<\/table>";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById("resultField").innerHTML = str;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Indicates two elements to compare+++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
function showImage() {
|
||||||
|
|
||||||
|
var str0 = "battle #"+numQuestion+"<br>"+Math.floor(finishSize*100/totalSize)+"% sorted.";
|
||||||
|
|
||||||
|
var str1 = ""+toNameFace(lstMember[cmp1][head1]);
|
||||||
|
|
||||||
|
var str2 = ""+toNameFace(lstMember[cmp2][head2]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById("battleNumber").innerHTML = str0;
|
||||||
|
|
||||||
|
document.getElementById("leftField").innerHTML = str1;
|
||||||
|
|
||||||
|
document.getElementById("rightField").innerHTML = str2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
numQuestion++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener("turbo:load", function () {
|
||||||
|
initList();
|
||||||
|
showImage();
|
||||||
|
});
|
||||||
|
//Convert numeric value into a name (emoticon)+++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
function toNameFace(n){
|
||||||
|
|
||||||
|
var str = namMember[n];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
str += '<br />';
|
||||||
|
|
||||||
|
switch(n) {
|
||||||
|
|
||||||
|
//case -1 Because it is a sample, delete it
|
||||||
|
|
||||||
|
case -1: str+=""; break;
|
||||||
|
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return str;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
|
<p class="instructions">
|
||||||
|
<center><br /><br />
|
||||||
|
<b>BABYLON 5 FAVORITE CHARACTER SORTER</b><br /><br>pick who you like better in each battle to get an accurate list of your<br />favorite characters from the show. does not include crusade.<br />note: hitting 'no opinion' or 'I like both' frequently will negatively affect your results.<br /><br /></center>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<table id="mainTable" align="center">
|
||||||
|
|
||||||
|
<tbody><tr>
|
||||||
|
|
||||||
|
<td id="battleNumber" colspan="3" style="padding-bottom: 10px;" style="text-align:center;"><b>battle #1<br>0% sorted.</b></td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td id="leftField" onclick="if(finishFlag==0) sortList(-1);" rowspan="2" style="text-align:center;"></td>
|
||||||
|
|
||||||
|
<td class="middleField" onclick="if(finishFlag==0) sortList(0);" style="text-align:center;">
|
||||||
|
|
||||||
|
I like both
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td id="rightField" onclick="if(finishFlag==0) sortList(1);" rowspan="2"style="text-align:center;"></td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td class="middleField" onclick="if(finishFlag==0) sortList(0);"style="text-align:center;">
|
||||||
|
|
||||||
|
no opinion
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody></table>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<div id="resultField" style="text-align: center;">
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<p class="other">
|
||||||
|
<center><small><br /><br />used with permission.<br /><br/><a href="http://biasorter.tumblr.com/">created by biasorter</a>.
|
||||||
|
</small></center></small>
|
||||||
|
|
||||||
38
app/views/fun/index.html.erb
Normal file
38
app/views/fun/index.html.erb
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<div class="mainaboutbox">
|
||||||
|
<h1>little pixel library!!</h1>
|
||||||
|
<a href="https://hillhouse.neocities.org/cliques/library/#library">a book-related pixel clique :')</a>
|
||||||
|
<br><br>
|
||||||
|
my pixel covers underlined in pink...<br>
|
||||||
|
<div class="shelf">
|
||||||
|
<img src="https://file.garden/Zw17vw8ctXTQw7PV/Untitled1769_20260513035715.png" alt="The Orange Eats Creeps by Grace Krilanovich" style="border-bottom:4px hotpink solid;"><img src="https://file.garden/Zw17vw8ctXTQw7PV/Untitled1770_20260513164853.png" alt="Serious Weakness by Porpentine Charity Heartscape" style="border-bottom:4px hotpink solid;"><a href='https://cherrycomet.neocities.org/'><img src='https://file.garden/Zw17vw8ctXTQw7PV/war.png' alt='War and Peace by Leo Tolstoy' style="border-bottom:4px #c78a6d solid;"></a><a href='https://juneish.neocities.org'><img src='https://file.garden/Zw17vw8ctXTQw7PV/hol.png' alt='House of Leaves by Mark Z. Danielewski' style="border-bottom:4px #c78a6d solid;"></a>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<h1>Things I Made</h1>
|
||||||
|
<center>AO3 Clone in HTML</center>
|
||||||
|
|
||||||
|
|
||||||
|
A while ago I made a semi-functional AO3 clone in HTML/css. You use this by copying and pasting your works into the work template, then updating the profile and tag pages to link to each work.
|
||||||
|
<br><br>
|
||||||
|
It's insane, but if you're curious or up for a tedious project, or are dreaming of hosting OTW-Archive one day and want to spin up a prototype, you can download it <a href="https://github.com/owoctober/AO3-html">here</a>.<br><br>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<center>Babylon 5 favorite character sorter</center>
|
||||||
|
I didn't code this, biasorter on Tumblr did, I just made it B5. Do you want to know your ultimate favs in B5? Check it out <a href="/fun/b5sorter">here!</a>
|
||||||
|
<hr>
|
||||||
|
<center>Graphics</center>
|
||||||
|
<img src="https://i.ibb.co/V0FPZT9b/janestamp.png" alt="janestamp" border="0">
|
||||||
|
<img src="https://i.ibb.co/kgwxz7sd/flexstamp.png" alt="flexstamp" border="0">
|
||||||
|
<img src="https://i.ibb.co/dsrLDFLx/cliffstamp.png" alt="cliffstamp" border="0">
|
||||||
|
<img src="https://i.ibb.co/zhsm4VhV/larrystamp.png" alt="larrystamp" border="0">
|
||||||
|
<img src="https://i.ibb.co/355pgmCS/ritastamp.png" alt="ritastamp" border="0">
|
||||||
|
<img src="https://i.ibb.co/s9PvmBqJ/maurastamp.png" alt="maurastamp" border="0">
|
||||||
|
<img src="https://i.ibb.co/21f0Fjv6/laurastamp.png" alt="laurastamp" border="0">
|
||||||
|
<img src="https://i.ibb.co/tpYytR2h/vicstamp.png" alt="vicstamp" border="0">
|
||||||
|
<img src="https://i.ibb.co/v612b01V/babylon5stamp.png" alt="babylon5stamp" border="0"><br/><img src="https://i.ibb.co/ccjjrxNC/DOOM-PATROL.png" alt="DOOM-PATROL" border="0">
|
||||||
|
<img src="https://i.ibb.co/DP5xmbSp/ALIEN-GENDER.png" alt="ALIEN-GENDER" border="0">
|
||||||
|
<img src="https://i.ibb.co/XrZ6y4mc/YAY-LESBIANS.png" alt="YAY-LESBIANS" border="0">
|
||||||
|
<img src="https://i.ibb.co/FLZJgtKz/BUTCH-LESBIAN.png" alt="BUTCH-LESBIAN" border="0">
|
||||||
|
<img src="https://i.ibb.co/rLgsL0K/FEMME-LESBIAN-1.png" alt="FEMME-LESBIAN-1" border="0"></div>
|
||||||
|
|
||||||
|
|
||||||
3
app/views/layouts/action_text/contents/_content.html.erb
Normal file
3
app/views/layouts/action_text/contents/_content.html.erb
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div class="trix-content">
|
||||||
|
<%= yield -%>
|
||||||
|
</div>
|
||||||
39
app/views/layouts/application.html.erb
Normal file
39
app/views/layouts/application.html.erb
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= content_for(:title) || "alien-kissing-computer" %></title>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="application-name" content="Playground">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<%= csrf_meta_tags %>
|
||||||
|
<%= csp_meta_tag %>
|
||||||
|
|
||||||
|
<%= yield :head %>
|
||||||
|
|
||||||
|
<%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
|
||||||
|
<%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
|
||||||
|
|
||||||
|
<link rel="icon" href="/icon.png" type="image/png">
|
||||||
|
<link rel="apple-touch-icon" href="/icon.png">
|
||||||
|
|
||||||
|
<%# Includes all stylesheet files in app/assets/stylesheets %>
|
||||||
|
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
|
||||||
|
<%= javascript_importmap_tags %>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<center> <div class="header">
|
||||||
|
<% if authenticated? %>
|
||||||
|
<%= button_to "Sign out", session_path, method: :delete %>
|
||||||
|
<% else %>
|
||||||
|
<%= link_to "🛸", new_session_path %>
|
||||||
|
<% end %>
|
||||||
|
</div><div class="navigate"><a href="/welcome/homepage">HOME!!!</a> 🛸 <a href="/shrines">SHRINES!!!</a> 🛸
|
||||||
|
<a href="/fun">FUN STUFF!!!</a> 🛸
|
||||||
|
<a href="/posts">MINIBLOG!!!</a> 🛸
|
||||||
|
<a href="/scraps">SCRAPS!!!</a> </center></div>
|
||||||
|
<%= yield %>
|
||||||
|
<center><small><small><small><small><a href="https://treasurechest.alien.town/agnes/playground">source</a></small></small></small></small></center>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
13
app/views/layouts/mailer.html.erb
Normal file
13
app/views/layouts/mailer.html.erb
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<style>
|
||||||
|
/* Email styles need to be inline */
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%= yield %>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
app/views/layouts/mailer.text.erb
Normal file
1
app/views/layouts/mailer.text.erb
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<%= yield %>
|
||||||
12
app/views/miniblog/index.html.erb
Normal file
12
app/views/miniblog/index.html.erb
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="mainaboutbox">
|
||||||
|
<div class="maininfo">
|
||||||
|
<h1>Recent Updates</h1>
|
||||||
|
|
||||||
|
<% if current_user.present? %>
|
||||||
|
|
||||||
|
<%= link_to 'New', new_updates_path(@update) %>
|
||||||
|
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
9
app/views/passwords/edit.html.erb
Normal file
9
app/views/passwords/edit.html.erb
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<h1>Update your password</h1>
|
||||||
|
|
||||||
|
<%= tag.div(flash[:alert], style: "color:red") if flash[:alert] %>
|
||||||
|
|
||||||
|
<%= form_with url: password_path(params[:token]), method: :put do |form| %>
|
||||||
|
<%= form.password_field :password, required: true, autocomplete: "new-password", placeholder: "Enter new password", maxlength: 72 %><br>
|
||||||
|
<%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", placeholder: "Repeat new password", maxlength: 72 %><br>
|
||||||
|
<%= form.submit "Save" %>
|
||||||
|
<% end %>
|
||||||
8
app/views/passwords/new.html.erb
Normal file
8
app/views/passwords/new.html.erb
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<h1>Forgot your password?</h1>
|
||||||
|
|
||||||
|
<%= tag.div(flash[:alert], style: "color:red") if flash[:alert] %>
|
||||||
|
|
||||||
|
<%= form_with url: passwords_path do |form| %>
|
||||||
|
<%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address] %><br>
|
||||||
|
<%= form.submit "Email reset instructions" %>
|
||||||
|
<% end %>
|
||||||
6
app/views/passwords_mailer/reset.html.erb
Normal file
6
app/views/passwords_mailer/reset.html.erb
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<p>
|
||||||
|
You can reset your password on
|
||||||
|
<%= link_to "this password reset page", edit_password_url(@user.password_reset_token) %>.
|
||||||
|
|
||||||
|
This link will expire in <%= distance_of_time_in_words(0, @user.password_reset_token_expires_in) %>.
|
||||||
|
</p>
|
||||||
4
app/views/passwords_mailer/reset.text.erb
Normal file
4
app/views/passwords_mailer/reset.text.erb
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
You can reset your password on
|
||||||
|
<%= edit_password_url(@user.password_reset_token) %>
|
||||||
|
|
||||||
|
This link will expire in <%= distance_of_time_in_words(0, @user.password_reset_token_expires_in) %>.
|
||||||
20
app/views/posts/edit.html.erb
Normal file
20
app/views/posts/edit.html.erb
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<div class="mainaboutbox">
|
||||||
|
|
||||||
|
<div class="maininfo">
|
||||||
|
<%= form_with model: [@post] do |f| %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= f.label :text %>
|
||||||
|
<%= f.rich_text_area :text %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<%= f.label :icon_image %>
|
||||||
|
<%= f.file_field :icon_image %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= f.submit "Edit Update" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
34
app/views/posts/index.html.erb
Normal file
34
app/views/posts/index.html.erb
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
<div class="mainaboutbox">
|
||||||
|
<% if authenticated? %>
|
||||||
|
<%= link_to 'New Update', new_post_path %>
|
||||||
|
<% else %>
|
||||||
|
.
|
||||||
|
<% end %>
|
||||||
|
<div class="maininfo">
|
||||||
|
<h1>Recent Updates</h1>
|
||||||
|
|
||||||
|
<div class="posts">
|
||||||
|
<div class="postz">
|
||||||
|
<% @posts.each do |post| %>
|
||||||
|
|
||||||
|
<% if post.icon_image.attached? %>
|
||||||
|
<%= image_tag post.icon_image %>
|
||||||
|
<% else %>
|
||||||
|
🛸
|
||||||
|
<% end %> <%= time_ago_in_words(@post.created_at) %> ago</p> <% if authenticated? %>
|
||||||
|
|
|
||||||
|
<%= link_to 'Edit', edit_post_path(post) %>
|
||||||
|
<%= link_to 'Destroy', post_path(post), data: {
|
||||||
|
turbo_method: :delete,
|
||||||
|
turbo_confirm: "Are you sureee?"
|
||||||
|
} %>
|
||||||
|
<% else %>
|
||||||
|
<% end %>
|
||||||
|
<%= post.text %>
|
||||||
|
<hr>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
24
app/views/posts/new.html.erb
Normal file
24
app/views/posts/new.html.erb
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<div class="mainaboutbox">
|
||||||
|
<% if authenticated? %>
|
||||||
|
<%= link_to 'New Update', new_post_path %>
|
||||||
|
<% else %>
|
||||||
|
.
|
||||||
|
<% end %>
|
||||||
|
<div class="maininfo">
|
||||||
|
<%= form_with model: [@post] do |f| %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= f.label :text %>
|
||||||
|
<%= f.rich_text_area :text %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<%= f.label :icon_image %>
|
||||||
|
<%= f.file_field :icon_image %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= f.submit "Create Update" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
0
app/views/posts/show.html.erb
Normal file
0
app/views/posts/show.html.erb
Normal file
22
app/views/pwa/manifest.json.erb
Normal file
22
app/views/pwa/manifest.json.erb
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "Playground",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icon.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icon.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"scope": "/",
|
||||||
|
"description": "Playground.",
|
||||||
|
"theme_color": "red",
|
||||||
|
"background_color": "red"
|
||||||
|
}
|
||||||
26
app/views/pwa/service-worker.js
Normal file
26
app/views/pwa/service-worker.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Add a service worker for processing Web Push notifications:
|
||||||
|
//
|
||||||
|
// self.addEventListener("push", async (event) => {
|
||||||
|
// const { title, options } = await event.data.json()
|
||||||
|
// event.waitUntil(self.registration.showNotification(title, options))
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// self.addEventListener("notificationclick", function(event) {
|
||||||
|
// event.notification.close()
|
||||||
|
// event.waitUntil(
|
||||||
|
// clients.matchAll({ type: "window" }).then((clientList) => {
|
||||||
|
// for (let i = 0; i < clientList.length; i++) {
|
||||||
|
// let client = clientList[i]
|
||||||
|
// let clientPath = (new URL(client.url)).pathname
|
||||||
|
//
|
||||||
|
// if (clientPath == event.notification.data.path && "focus" in client) {
|
||||||
|
// return client.focus()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (clients.openWindow) {
|
||||||
|
// return clients.openWindow(event.notification.data.path)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// })
|
||||||
0
app/views/scraps/edit.html.erb
Normal file
0
app/views/scraps/edit.html.erb
Normal file
25
app/views/scraps/index.html.erb
Normal file
25
app/views/scraps/index.html.erb
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<div class="mainaboutbox">
|
||||||
|
<% if authenticated? %>
|
||||||
|
<%= link_to 'New Scrap', new_scrap_path %>
|
||||||
|
<% else %>
|
||||||
|
.
|
||||||
|
<% end %><br><br>
|
||||||
|
<h1>SCRAPBOOK</h1>
|
||||||
|
random thoughts longer than miniblog length will go here :)<br><br>
|
||||||
|
<hr>
|
||||||
|
<% @scraps.each do |scrap| %>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<%= truncate(scrap.text.to_plain_text, length: 350) %> (<%= link_to 'Read', scrap_path(scrap) %>) | <%= time_ago_in_words(scrap.created_at) %> ago
|
||||||
|
<br>
|
||||||
|
<% if authenticated? %>
|
||||||
|
|
|
||||||
|
<%= link_to 'Edit', edit_scrap_path(scrap) %>
|
||||||
|
<%= link_to 'Destroy', scrap_path(scrap), data: {
|
||||||
|
turbo_method: :delete,
|
||||||
|
turbo_confirm: "Are you sureee?"
|
||||||
|
} %>
|
||||||
|
<% else %>
|
||||||
|
<% end %><br><br><hr>
|
||||||
|
<% end %>
|
||||||
33
app/views/scraps/new.html.erb
Normal file
33
app/views/scraps/new.html.erb
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<% if authenticated? %>
|
||||||
|
<%= link_to 'New Scrap', new_scrap_path %>
|
||||||
|
<% else %>
|
||||||
|
.
|
||||||
|
<% end %>
|
||||||
|
<div class="maininfo">
|
||||||
|
<%= form_with model: [@scrap] do |f| %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= f.label :text %>
|
||||||
|
<%= f.rich_text_area :text %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= f.label :image %>
|
||||||
|
<%= f.file_field :image %>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<%= f.label :mood %>
|
||||||
|
<%= f.text_area :mood %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= f.label :music %>
|
||||||
|
<%= f.text_area :music %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= f.label :notes %>
|
||||||
|
<%= f.rich_text_area :notes %>
|
||||||
|
</div>
|
||||||
|
<%= f.submit "Create Scrap" %>
|
||||||
|
<% end %>
|
||||||
20
app/views/scraps/show.html.erb
Normal file
20
app/views/scraps/show.html.erb
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<div class="mainaboutbox">
|
||||||
|
<div class="scrapbox">
|
||||||
|
<%= link_to '[ b a c k . . ? ]', scraps_path %>
|
||||||
|
<h2>SCRAPBOOK ENTRY #<%= @scrap.id %></h2>
|
||||||
|
<%= @scrap.text %>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<hr><hr>
|
||||||
|
<% if @scrap.music.present? %>
|
||||||
|
<strong>Current Music:</strong>
|
||||||
|
<%= @scrap.music %>
|
||||||
|
<% end %>
|
||||||
|
</p>
|
||||||
|
<% if @scrap.mood.present? %>
|
||||||
|
<p>
|
||||||
|
<strong>Current Mood:</strong>
|
||||||
|
<%= @scrap.mood %></p>
|
||||||
|
<% end %>
|
||||||
|
</div></ddiv>
|
||||||
11
app/views/sessions/new.html.erb
Normal file
11
app/views/sessions/new.html.erb
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<%= tag.div(flash[:alert], style: "color:red") if flash[:alert] %>
|
||||||
|
<%= tag.div(flash[:notice], style: "color:green") if flash[:notice] %>
|
||||||
|
|
||||||
|
<%= form_with url: session_path do |form| %>
|
||||||
|
<%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address] %><br>
|
||||||
|
<%= form.password_field :password, required: true, autocomplete: "current-password", placeholder: "Enter your password", maxlength: 72 %><br>
|
||||||
|
<%= form.submit "Sign in" %>
|
||||||
|
<% end %>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<%= link_to "Forgot password?", new_password_path %>
|
||||||
6
app/views/shrines/index.html.erb
Normal file
6
app/views/shrines/index.html.erb
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="mainaboutbox">
|
||||||
|
<a href="/shrines/spiraleye">SPIRAL EYE - A shrine to Elouisa from Palia;</a><br>
|
||||||
|
<a href="https://signsandportents.neocities.org/morden/ambition">AMBITION - A shrine to Morden from Babylon 5;</a> <small><small>currently hosted off-site</small></small><br>
|
||||||
|
<a href="/shrines/spirit">SPIRIT - A shrine to the fictional Negative Spirit species from Doom Patrol TV;</a><br>
|
||||||
|
coming soon...<br>
|
||||||
|
SHADOW OF A GHOST - A shrine to Anna Sheridan from Babylon 5.
|
||||||
72
app/views/shrines/spiraleye.html.erb
Normal file
72
app/views/shrines/spiraleye.html.erb
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
|
||||||
|
<div class="mainelouisa">
|
||||||
|
<script>
|
||||||
|
|
||||||
|
console.log("%cTemplate generated with petrapixel's layout generator: https://petrapixel.neocities.org/coding/layout-generator", "font-size: 14pt; color: #922a45; background: #ffd3ef");
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<a href="#content" id="skip-to-content-link">Skip to content</a>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- headerspiral -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="headerspiral">
|
||||||
|
<div class="headerspiral-image">
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/elouisaheader.png" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="headerspiral-content">
|
||||||
|
<div class="headerspiral-title">a crawly conundrum</div>
|
||||||
|
|
||||||
|
<!-- NAVIGATION -->
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/shrines/spiraleye">Home</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeabout">About</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyefanworks">Fanworks</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeggallery">Gallery</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyelinks">Links</a></li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</headerspiral>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="content"> <!-- Do NOT remove the ID here -->
|
||||||
|
|
||||||
|
<div id="mainspiral">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- MAIN CONTENT -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<center><a href="https://x.com/SashaFrantseva/status/1738231303269708148"><img src="https://file.garden/aJzQmzrHVB4BLKwu/art.png"></a><big><big>
|
||||||
|
<strong>spiral eye,</strong> a fan-shrine for the wonderful elouisa from the video game palia.
|
||||||
|
<p>established oct 15, 2025; last updated oct 17, 2025.</p>
|
||||||
|
<p><small>this site will update as palia updates.</small></p></center></big></big>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="footerspiral">
|
||||||
|
<a href="https://teeth.kissing.computer/shrines">this is a project of agnes the alien.</a></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
93
app/views/shrines/spiraleyeabout.html.erb
Normal file
93
app/views/shrines/spiraleyeabout.html.erb
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
|
||||||
|
<div class="mainelouisa">
|
||||||
|
<script>
|
||||||
|
|
||||||
|
console.log("%cTemplate generated with petrapixel's layout generator: https://petrapixel.neocities.org/coding/layout-generator", "font-size: 14pt; color: #922a45; background: #ffd3ef");
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<a href="#content" id="skip-to-content-link">Skip to content</a>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- headerspiral -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="headerspiral">
|
||||||
|
<div class="headerspiral-image">
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/elouisaheader.png" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="headerspiral-content">
|
||||||
|
<div class="headerspiral-title">a crawly conundrum</div>
|
||||||
|
|
||||||
|
<!-- NAVIGATION -->
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/shrines/spiraleye">Home</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeabout">About</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyefanworks">Fanworks</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeggallery">Gallery</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyelinks">Links</a></li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</headerspiral>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="content"> <!-- Do NOT remove the ID here -->
|
||||||
|
|
||||||
|
<div id="mainspiral" style="padding:1%;">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- MAIN CONTENT -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<center> <img src="https://file.garden/aJzQmzrHVB4BLKwu/Elouisa_Reveal.jpg" width="550px" style="border:6px double darkblue;"></center>
|
||||||
|
<br>"There are several people in Palia who have a thirst for knowledge, and Elouisa is one of them. While her twin sister, Caleri, satiates her desire for information with books, Elouisa explores the world to learn. She enjoys the weird things in life, and if you get close to her, you’ll be dragged along on some of her expeditions.
|
||||||
|
|
||||||
|
Elouisa enjoys Bugs and anything you can gather from the overworld like Sundrop Lilies or Wild Green Onions." - <a href="https://store.epicgames.com/en-US/news/make-friends-with-the-villagers-in-palia-with-our-character-guide">Source</a>
|
||||||
|
<h3>Background</h3><br><br>
|
||||||
|
*note: I am still working through Elouisa's quests, so this may not be comprehensive.<br><br>
|
||||||
|
Elouisa is an adult Majiri.<br><br>Growing up with her twin sister, Caleri, their parents put a significant amount of pressure on both of them --- pressure that shifted entirely to Caleri when Elouisa's mental illness developed later in her life. She is the older twin by a few minutes.<br><br> With her best friend, Gabrina, she took on a significant interest in cryptids and the paranormal, and with Gabrina they were the "paranormal pals". One night, however, after Elouisa begged her to look for a Brighteyes---a cryptid creature--- Gabrina took a lantern made of <a href="https://palia.wiki.gg/wiki/Flow">Flow</a> to their hunt. Elouisa, knowing the Brighteyes feared Flow, extinguished the light, and Gabrina disappeared. Later, she reappeared with a broken arm and began to shun Elouisa for her interest in the paranormal just like everyone else had.<h3>Likes</h3>
|
||||||
|
Favorite items to be gifted:
|
||||||
|
<li>Rainbow-tipped butterfly</li>
|
||||||
|
<li>Aquamarine gemstone</li>
|
||||||
|
<li>Flowtato fries</li>
|
||||||
|
<li>Albino eel</li>
|
||||||
|
<li>Any bug or fish</li>
|
||||||
|
<h3>Theories</h3>
|
||||||
|
Elouisa has a variety of paranormal theories. Here are a few:
|
||||||
|
<li>Humans did not go extinct; they went underground</li>
|
||||||
|
<li>Auni may be a cryptid</li>
|
||||||
|
<li>Bugs may be travelers from another dimension</li>
|
||||||
|
<li>Ancient flowbugs have butts that give off pure Flow energy, and have a psychic link with Silverwing eggs</li>
|
||||||
|
<li>Fish can communicate psychically with plants</li>
|
||||||
|
<li>Palium prevents eavesdropping</li>
|
||||||
|
<li>Gabrina was switched with a Brightfiend, a copy of a person that actually spies for the Brightworld</li>
|
||||||
|
<li>Cutthroat Trout are the serial killers of the fish world</li></menu>
|
||||||
|
<hr>
|
||||||
|
<a href="/shrines/spiraleyeabtsite">You can learn about the webmaster here!</a></center>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="footerspiral">
|
||||||
|
<a href="https://teeth.kissing.computer">this is a project of agnes the alien.</a></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
70
app/views/shrines/spiraleyeabtsite.html.erb
Normal file
70
app/views/shrines/spiraleyeabtsite.html.erb
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
|
||||||
|
<div class="mainelouisa">
|
||||||
|
<script>
|
||||||
|
|
||||||
|
console.log("%cTemplate generated with petrapixel's layout generator: https://petrapixel.neocities.org/coding/layout-generator", "font-size: 14pt; color: #922a45; background: #ffd3ef");
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<a href="#content" id="skip-to-content-link">Skip to content</a>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- headerspiral -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="headerspiral">
|
||||||
|
<div class="headerspiral-image">
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/elouisaheader.png" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="headerspiral-content">
|
||||||
|
<div class="headerspiral-title">a crawly conundrum</div>
|
||||||
|
|
||||||
|
<!-- NAVIGATION -->
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/shrines/spiraleye">Home</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeabout">About</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyefanworks">Fanworks</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeggallery">Gallery</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyelinks">Links</a></li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</headerspiral>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="content"> <!-- Do NOT remove the ID here -->
|
||||||
|
|
||||||
|
<div id="mainspiral" style="padding:1%;">
|
||||||
|
|
||||||
|
<h3>About the Site</h3>
|
||||||
|
<p>I fell in love with Palia immediately after I started playing it in January 2024. I found myself drawn to almost every character, but I found myself particularly drawn to Elouisa when I met her. I decided to make a fansite in October 2025, after the <a href="https://events.oubliette.nu/ghostofyou/">Ghost of You shrine challenge</a> inspired me to get into making websites dedicated to characters I love. While this isn't for that challenge specifically, it was a huge inspiration to me.</p>
|
||||||
|
<p>I love Palia because I love playing video games but I really suck at video games that involve combat-related mechanics. because of this, I never looked into video games as a whole, instead sticking to playing The Sims and making my own browser-based text games. However, Palia's holistic nature -- the ability to have so many skills, the amount of villagers to interact with, the quests, the items, the lack of fall damage or player injury mechanics or any kind, the focus on calmness without rejecting plot --- this got me to be more interested in video games, and I've found myself branching out more and more since I started playing. It has really given me an appreciation for the medium - all genres, not just Palia's.</p>
|
||||||
|
<p>I love Elouisa because... well. I see myself in her. I too struggle with the same mental illness she does, I have the same hyperniche and whimsical nature she does, and many parts of me have been rejected for being too eccentric like she was. I just want the best for her forever. Also she's hot.</p>
|
||||||
|
<h3>About the Webmaster</h3>
|
||||||
|
I'm Agnes (it/zhe/xe/she), a 24 year old computer hobbyist. I run a variety of websites for fun. I really love fantasy and sci-fi, and I've been a writer and artist my whole life. I have been a fan of computers since I was 5 and was given (supervised!) access to what we called back then "the computer room". I love creativity. I also love women. That's about it.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="footerspiral">
|
||||||
|
<a href="https://teeth.kissing.computer/shrines">this is a project of agnes the alien.</a></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
84
app/views/shrines/spiraleyefanworks.html.erb
Normal file
84
app/views/shrines/spiraleyefanworks.html.erb
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
|
||||||
|
<div class="mainelouisa">
|
||||||
|
<script>
|
||||||
|
|
||||||
|
console.log("%cTemplate generated with petrapixel's layout generator: https://petrapixel.neocities.org/coding/layout-generator", "font-size: 14pt; color: #922a45; background: #ffd3ef");
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<a href="#content" id="skip-to-content-link">Skip to content</a>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- headerspiral -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="headerspiral">
|
||||||
|
<div class="headerspiral-image">
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/elouisaheader.png" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="headerspiral-content">
|
||||||
|
<div class="headerspiral-title">a crawly conundrum</div>
|
||||||
|
|
||||||
|
<!-- NAVIGATION -->
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/shrines/spiraleye">Home</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeabout">About</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyefanworks">Fanworks</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeggallery">Gallery</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyelinks">Links</a></li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</headerspiral>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="content"> <!-- Do NOT remove the ID here -->
|
||||||
|
|
||||||
|
<div id="mainspiral">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- MAIN CONTENT -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<center><h1>Fanart</h1>
|
||||||
|
<h3>by me...</h3>
|
||||||
|
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/Untitled1017_20250101224237.png" width="450px">
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/Untitled1645_20251022062326.png" width="450px">
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/SPOILER_Untitled1675_20251103182947.png" width="450px">
|
||||||
|
<h3>by others...</h3>
|
||||||
|
<small>If you would like me to remove your work, <a href="mailto:alienhospitals@gmail.com">email me.</a></small><br>
|
||||||
|
<a href="https://www.tumblr.com/senaink/750369934849212416/bigbrain?source=share">Cute gif art of Elouisa in anime style by senaink on Tumblr</a>
|
||||||
|
<br><a href="https://www.tumblr.com/tillero/761117814240182272/maybe-maybe-we-really-can-fix-things-been?source=share">Elouisa & Caleri art by tillero on Tumblr</a><br>
|
||||||
|
<a href="https://www.tumblr.com/smallestflowtree/741778830335918080/dont-let-them-tell-you-how-to-think-i-love?source=share">Elouisa painting by smallestflowtree on Tumblr</a><br>
|
||||||
|
<a href="https://www.tumblr.com/tvklike/728448114569199616/i-believe-you?source=share">Elouisa/Tamala art by tvklike on Tumblr</a><br>
|
||||||
|
<a href="https://www.reddit.com/r/Palia/comments/1ah48ie/elouisa_fanart/">Elouisa fanart by savbaby0509 on Reddit</a><br>
|
||||||
|
<a href="https://www.tiktok.com/@bscoolart/video/7421996230688984328">Elouisa art by bscoolart on TikTok</a>
|
||||||
|
<h1>Cosplay</h1>
|
||||||
|
<a href="https://www.instagram.com/p/DDsShxEvyxn/">Elouisa Cosplay by corvidwitxh on Instagram</a></center>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="footerspiral">
|
||||||
|
<a href="https://teeth.kissing.computer">this is a project of agnes the alien.</a></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
75
app/views/shrines/spiraleyeggallery.html.erb
Normal file
75
app/views/shrines/spiraleyeggallery.html.erb
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
|
||||||
|
<div class="mainelouisa">
|
||||||
|
<script>
|
||||||
|
|
||||||
|
console.log("%cTemplate generated with petrapixel's layout generator: https://petrapixel.neocities.org/coding/layout-generator", "font-size: 14pt; color: #922a45; background: #ffd3ef");
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<a href="#content" id="skip-to-content-link">Skip to content</a>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- headerspiral -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="headerspiral">
|
||||||
|
<div class="headerspiral-image">
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/elouisaheader.png" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="headerspiral-content">
|
||||||
|
<div class="headerspiral-title">a crawly conundrum</div>
|
||||||
|
|
||||||
|
<!-- NAVIGATION -->
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/shrines/spiraleye">Home</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeabout">About</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyefanworks">Fanworks</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeggallery">Gallery</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyelinks">Links</a></li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</headerspiral>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="content"> <!-- Do NOT remove the ID here -->
|
||||||
|
|
||||||
|
<div id="mainspiral">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- MAIN CONTENT -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<center> A collection of Elouisa screenshots.
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/Screenshot%20(39).png" width="480px"><img src="https://file.garden/aJzQmzrHVB4BLKwu/Screenshot%202025-10-16%20171801.png" width="480px"><img src="https://file.garden/aJzQmzrHVB4BLKwu/Screenshot%20(85).png" width="480px"><img src="https://file.garden/aJzQmzrHVB4BLKwu/Screenshot%20(79).png" width="480px"><img src="https://file.garden/aJzQmzrHVB4BLKwu/Screenshot%20(83).png" width="480px"><img src="https://file.garden/aJzQmzrHVB4BLKwu/Screenshot%202025-10-16%20180248.png" width="480px">
|
||||||
|
<h3>Elouisa's Room</h3>
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/Elouisa's_Room_Interior_3_Ingame.png"><img src="https://file.garden/aJzQmzrHVB4BLKwu/Elouisa's_Room_Entrance_Ingame.png"><img src="https://file.garden/aJzQmzrHVB4BLKwu/Elouisa's_Room_Interior_1_Ingame.png"><img src="https://file.garden/aJzQmzrHVB4BLKwu/Elouisa's_Room_Interior_2_Ingame.png">
|
||||||
|
<h3>Elouisa Concept Art</h3>
|
||||||
|
by <a href="https://x.com/SashaFrantseva/status/1738231620736553275/photo/1">sasha frantseva.</a>. <br>
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/GB9wXffWUAAdqO8.jpg" width="580px"></center>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="footerspiral">
|
||||||
|
<a href="https://teeth.kissing.computer/shrines">this is a project of agnes the alien.</a></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
81
app/views/shrines/spiraleyelinks.html.erb
Normal file
81
app/views/shrines/spiraleyelinks.html.erb
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
<div class="mainelouisa">
|
||||||
|
<script>
|
||||||
|
|
||||||
|
console.log("%cTemplate generated with petrapixel's layout generator: https://petrapixel.neocities.org/coding/layout-generator", "font-size: 14pt; color: #922a45; background: #ffd3ef");
|
||||||
|
</script>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<a href="#content" id="skip-to-content-link">Skip to content</a>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- headerspiral -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="headerspiral">
|
||||||
|
<div class="headerspiral-image">
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/elouisaheader.png" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="headerspiral-content">
|
||||||
|
<div class="headerspiral-title">a crawly conundrum</div>
|
||||||
|
|
||||||
|
<!-- NAVIGATION -->
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/shrines/spiraleye">Home</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeabout">About</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyefanworks">Fanworks</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyeggallery">Gallery</a></li>
|
||||||
|
<li><a href="/shrines/spiraleyelinks">Links</a></li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</headerspiral>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="content"> <!-- Do NOT remove the ID here -->
|
||||||
|
|
||||||
|
<div id="mainspiral" style="padding:1%;">
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- MAIN CONTENT -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
Link to me--
|
||||||
|
(Please do NOT hotlink! Upload to your own site or image hosting platform 🙂)<hr>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<img src="https://file.garden/aJzQmzrHVB4BLKwu/elouisasitebutton.png">
|
||||||
|
<hr>
|
||||||
|
Links out---<hr>
|
||||||
|
<a href="https://emotion.oubliette.nu/"><img src="https://file.garden/aJzQmzrHVB4BLKwu/emotion.png"></a>
|
||||||
|
<hr>
|
||||||
|
<a href="https://toothpaste.atabook.org">Guestbook</a>
|
||||||
|
<hr>
|
||||||
|
<small>Leave me a message!</small>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =============================================== -->
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<!-- =============================================== -->
|
||||||
|
|
||||||
|
<div class="footerspiral">
|
||||||
|
<a href="https://teeth.kissing.computer/shrines">this is a project of agnes the alien.</a></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
22
app/views/shrines/spirit.html.erb
Normal file
22
app/views/shrines/spirit.html.erb
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<div class="bodyspirit">
|
||||||
|
<h1><center><img src="https://file.garden/aJzQmzrHVB4BLKwu/home.gif" width="290px"></center></h1>
|
||||||
|
<h3><a href="/shrines/spirit">HOME</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritquiz">QUIZZES</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirittimeline">EXPLANATION</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritsc">SCREENSHOTS & VIDEOS</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirithc">WORLDBUILDING</a></h3>
|
||||||
|
|
||||||
|
|
||||||
|
Welcome to the homepage of the Negative Spirits! This is a fanpage for the fictional species known as the Negative Spirits from the show Doom Patrol on DC Universe (then, later, HBO Max).<br><br>
|
||||||
|
<center><h3>WHY NOT THE COMICS?</h3>
|
||||||
|
I originally fell in love with the Negative Spirit and their species while watching the Doom Patrol show in 2019. While I respect the comics as the basis of the media I love, I don't really care about them as much as the show. However, much of the lore in the <i><a href="/hc">headcanons</a></i> zone is based tangentially on Gerard Way's run of Doom Patrol.
|
||||||
|
<center><h3>WHAT ARE NEGATIVE SPIRITS?</h3></center>
|
||||||
|
Negative Spirits are a fictional alien race from the show Doom Patrol, based on the comic series by DC Comics. While the show they come from is based largely on a specific run from the comics (Morrison's), I consider TV Negative Spirits to be divergent from comic Negative Spirits. Negative Spirits are noncorporeal aliens formed of pure energy that have a variety of powers, such as immortality, psychic ability, interdimensional travel, electrocution and electricity manipulation, flight, and possession of human hosts (though it makes them radioactive, but this can be contained via emotional bonding with host).
|
||||||
|
<center><h3>WHY DO THEY FASCINATE ME SO MUCH?</h3></center>
|
||||||
|
The Negative Spirits have been shown to be extremely emotionally sensitive, at least according to Niles Caulder. Larry's Negative Spirit, specifically, is my favorite character in the whole universe, and I find its heroic nature to be very beautiful. Larry doesn't want to intervene, doesn't want to help, only wants to hide and shrivel away, but his Negative Spirit forces him to help. Larry doesn't want to heal from his trauma and his Negative Spirit helps him confront his demons. His Negative Spirit helps save the world and fight evil and wants to be in the center of it all, and it has no reason to do this as the world has only been unkind and cruel to it during 95% of its time on earth. <br><br>
|
||||||
|
Now, the Negative Spirit is still an asshole, but that's also part of why I love it. It puts Larry on the ceiling when he tries to communicate with it. It repeatedly leaves Larry's body (which knocks him unconscious). It crashes Larry's bus after he calls it a parasite. But there are also so many tender moments between them--Larry saying they have an untraditional connection in Frances Patrol, Larry saying he believes in it, their general closeness during the 6 month time skip between 1x14 and 1x15, Larry falling asleep with his hand over his chest as it emitted its glow, Larry's attempted sacrifice and it choosing to stay, and of course the face touch. Negative Spirits are, well, aliens with complex emotional states! Larry's NS only seems to lash out when Larry is cruel to it, as Larry's self hatred is extremely torturous to live with---Negative spirits are merged with their host's mind and thoughts on top of being incredibly emotionally sensitive already.<br><br>
|
||||||
|
And then there's Valentina Vostok, who is Larry's narrative parallel. A glimpse into what could be for him. Her Negative Spirit has bonded with her entirely, they "fell in love", and they have perfect communication. Larry thinks this has made her entirely alien, but her and her Spirit were alone in a ship with two corpses for Fifty Years, so that kind of isolation rusts the connection to humanity a bit. Valentina acts detached from Earth, more serene, and so does her Spirit; this indicates to me that merging with a Negative Spirit can be a beautiful thing under the right circumstances. Or maybe the most desperate circumstances.
|
||||||
|
<br><br>I love the Negative Spirits so much. <br><h3>LINK TO ME!!</h3>
|
||||||
|
<a href="https://teeth.kissing.computer/fun/spirit"><img src="https://i.ibb.co/Pv0Jd5XR/button.png" alt="button" border="0"></a><br><textarea>< <a href="https://teeth.kissing.computer"><img src="https://i.ibb.co/Pv0Jd5XR/button.png" alt="button" border="0"></a></textarea><hr>
|
||||||
|
<small><small>Contact<br>
|
||||||
|
Agnes the Alien<br>
|
||||||
|
alienhospitals@gmail.com<br>
|
||||||
|
<a href="https://kissing.computer">kissing.computer</a></small></small>
|
||||||
|
|
||||||
|
|
||||||
17
app/views/shrines/spirithc.html.erb
Normal file
17
app/views/shrines/spirithc.html.erb
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<div class="bodyspirit"><h1><center><img src="https://file.garden/aJzQmzrHVB4BLKwu/home.gif" width="290px"></center></h1>
|
||||||
|
<h3><a href="/shrines/spirit">HOME</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritquiz">QUIZZES</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirittimeline">EXPLANATION</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritsc">SCREENSHOTS & VIDEOS</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirithc">WORLDBUILDING</a></h3>
|
||||||
|
|
||||||
|
<center><h1>HEADCANONS/WORLDBUILDING</h1></center>
|
||||||
|
Most of this is based on the Gerard Way comics.<hr>
|
||||||
|
<br>
|
||||||
|
<b>negative spirits experience everything more intensely than we do.</b> emotions especially, but also sensations and experiences. it's almost unbearable for them, and a major emphasis in spirit society is on dealing with these emotions. hence, creativity is encouraged as an outlet for them, and those particularly good at expressing themselves are highly respected - an example of this is the great poets.<br><br>
|
||||||
|
<b>the naming rule. </b>in the comics, the only named spirits have four letter first and last names; hence, it is my opinion that in the negative space, spirits are usually given names that have only four letters. spirits may choose any name they wish over the course of their existence, but the "default" spirit names when created have four letters only.<br><br>
|
||||||
|
<b>speaking of creation... </b>negative spirits do not reproduce sexually, they are incapable of it. instead, to reproduce, they use a system similar to the loom theory from doctor who. the family extracts negative energy from their existence. to create a new being, the spirits need at least one source of negative energy, though there can be more than one source, and the more sources a spirit has, the more powerful they are. the spirit is then woven together and has the knowledge of their source spirits as well, but must adjust to life on their own. when a spirit extracts their energy, they lose an aspect of themselves, whether it is a memory, a trait, a skill, or something else, so many spirits are hesitant to reproduce.</b>
|
||||||
|
<br><br><b>spirits are immortal.</b> they do not die in their own habitat ever. if the dimension gets too populated beyond what it can sustain, the great sun expands it further; therefore the negative space is technically infinite.<br><br><b>negative spirits mate for life.</b> unless they don't. there is the idea of a b'keh, a soul pairing. this can be of any kind; platonic, romantic, undefined. some have more than one. once they find their soulmate, both/all are taken to what is called a realization sequence -- they are briefly transported into a psychic projection involving the two spirits that displays meaningful imagery to them and generally lets them know that they've found their soulmate(s). however, many spirits do not participate in this aspect of spirit culture and those who choose not to do not experience realization sequences. everything in the negative space is voluntary, which is why being involuntarily forced out of the negative space was so hard for larry's spirit.
|
||||||
|
the soulmates are said to be chosen by...
|
||||||
|
<br><br><b>the great sun.</b> this is the negative space's concept of the being that created the negative space, it is not deified in the traditional human sense but rather considered a personal friend of each negative spirit. there are many different views of the great sun, and many different belief systems, but most of them have the great sun as a central figure. the great sun is a being that lives within the fabric that composes the dimension; it created the Hall of N'Hal first before anything else in the dimension, hence N'Hal hall is considered the atelier of the dimension. religion in the negative space is different than on earth; in the negative space the great sun is prayed to, yes, and is considered to be very powerful, but it is considered offensive to the great sun to worship it or give offerings, because the great sun is supposed to be seen as similar to any negative spiritit is a friend, not a deity to them. its warmth embraces everyone.
|
||||||
|
<hr>
|
||||||
|
<small><small>Contact<br>
|
||||||
|
Agnes Alien<br>
|
||||||
|
spirits@alien.hospital</small></small>
|
||||||
|
</div>
|
||||||
10
app/views/shrines/spiritquiz.html.erb
Normal file
10
app/views/shrines/spiritquiz.html.erb
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="bodyspirit"> <h1><center><img src="https://file.garden/aJzQmzrHVB4BLKwu/home.gif" width="290px"></center></h1>
|
||||||
|
<h3><a href="/shrines/spirit">HOME</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritquiz">QUIZZES</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirittimeline">EXPLANATION</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritsc">SCREENSHOTS & VIDEOS</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirithc">WORLDBUILDING</a></h3><h1>QUIZZES!</h1>
|
||||||
|
<h2>Which Negative Spirit are you?</h2>psst..not showing up? refresh the page!<br><br>
|
||||||
|
<div id="uquiz_embed" data-quiz-id="8x3y3h" data-width="30%"></div>
|
||||||
|
<script src="https://uquiz.com/embed.js"></script>
|
||||||
|
Got an idea for a quiz? Email me!</center>
|
||||||
|
<hr>
|
||||||
|
<small><small>Contact<br>
|
||||||
|
Agnes Alien<br>
|
||||||
|
spirits@alien.hospital</small></small></div>
|
||||||
35
app/views/shrines/spiritsc.html.erb
Normal file
35
app/views/shrines/spiritsc.html.erb
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<div class="bodyspirit"
|
||||||
|
|
||||||
|
|
||||||
|
<h1><center><img src="https://file.garden/aJzQmzrHVB4BLKwu/home.gif" width="290px"></center></h1>
|
||||||
|
<h3><a href="/shrines/spirit">HOME</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritquiz">QUIZZES</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirittimeline">EXPLANATION</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritsc">SCREENSHOTS & VIDEOS</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirithc">WORLDBUILDING</a></h3>
|
||||||
|
|
||||||
|
<center><h2>Screenshots & Videos</h2>
|
||||||
|
<h3>Larry Trainor's Negative Spirit Screenshots</h3>
|
||||||
|
<img src="https://i.ibb.co/C5hQy7f9/DP-S01-E01-0164-Copy.jpg" alt="DP-S01-E01-0164-Copy" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/FP38c9S/DP-S01-E02-1390-Copy.jpg" alt="DP-S01-E02-1390-Copy" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/Zz6FnXnP/DP-S01-E04-0130-Copy.jpg" alt="DP-S01-E04-0130-Copy" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/673vyk3n/DP-S01-E04-0765-Copy.jpg" alt="DP-S01-E04-0765-Copy" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/5mSJ96d/DP-S01-E06-1157.jpg" alt="DP-S01-E06-1157" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/hFSQVxWh/DP-S01-E13-0109.jpg" alt="DP-S01-E13-0109" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/My2fWt7P/DP-S01-E13-0124-Copy.jpg" alt="DP-S01-E13-0124-Copy" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/tksWk70/DP-S01-E13-0199.jpg" alt="DP-S01-E13-0199" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/BKrx2G9T/DP-S01-E13-0238.jpg" alt="DP-S01-E13-0238" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/Rp15yQF9/DP-S01-E13-0924.jpg" alt="DP-S01-E13-0924" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/3yZ8yPxw/DP-S01-E13-0936-Copy.jpg" alt="DP-S01-E13-0936-Copy" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/0yzvfR9S/DP-S01-E13-1210.jpg" alt="DP-S01-E13-1210" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/WWF6C4R0/DP-S01-E13-1216-Copy.jpg" alt="DP-S01-E13-1216-Copy" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/Q73bWN4n/DP-S02-E01-0941.jpg" alt="DP-S02-E01-0941" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/ymsqdMyr/maxresdefault-4.jpg" alt="maxresdefault-4" border="0" width="590px"> q
|
||||||
|
<h3>Larry Trainor's Negative Spirit Videos</h3>
|
||||||
|
<iframe width="560" height="315" src="https://www.youtube.com/embed/b3neG8_blWg?si=Zju1kfdesDLg_k0x" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||||
|
|
||||||
|
<h3>Valentina Vostok's Negative Spirit Screencaps</h3>
|
||||||
|
<img src="https://i.ibb.co/84s3pgVG/DP-S02-E06-0539.jpg" alt="DP-S02-E06-0539" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/Ldyh7LSv/DP-S02-E06-0538.jpg" alt="DP-S02-E06-0538" border="0" width="590px">
|
||||||
|
<img src="https://i.ibb.co/BHZx1LCN/DP-S02-E06-0454.jpg" alt="DP-S02-E06-0454" border="0" width="590px">
|
||||||
|
<hr>
|
||||||
|
<small><small>Contact<br>
|
||||||
|
Agnes Alien<br>
|
||||||
|
spirits@alien.hospital</small></small>
|
||||||
|
|
||||||
32
app/views/shrines/spirittimeline.html.erb
Normal file
32
app/views/shrines/spirittimeline.html.erb
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<div class="bodyspirit" style="padding:9px;">
|
||||||
|
|
||||||
|
<h1><center><img src="https://file.garden/aJzQmzrHVB4BLKwu/home.gif" width="290px"></center></h1>
|
||||||
|
<h3><a href="/shrines/spirit">HOME</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritquiz">QUIZZES</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirittimeline">EXPLANATION</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spiritsc">SCREENSHOTS & VIDEOS</a> <img src="https://file.garden/aJzQmzrHVB4BLKwu/stars.gif" width="50px"> <a href="/shrines/spirithc">WORLDBUILDING</a></h3>
|
||||||
|
|
||||||
|
<h3>Timeline</h3>
|
||||||
|
<small><i>Note: As I am primarily a show fan, feel free to email me any corrections. I probably missed something.<br></i><br>
|
||||||
|
<h3>Comics</h3>
|
||||||
|
June 1963. The first Doom Patrol comic story featuring Larry Trainor/Negative Man, an Air Force test pilot fused to an alien negative spirit, is released. (My Greatest Adventure #80)<br>
|
||||||
|
September 1977. After Larry Trainor's death, the Negative Spirit inside of him is transferred to Valentina Vostok of the Soviet Air Force. (Showcase vol 1 #94)<br>
|
||||||
|
February 1989. The Negative Spirit, potentially known as Mercurius, originally thought to be a projection of Larry's consciousness, is revealed to be its own unique being. (Doom Patrol Vol 2 #19)<br>
|
||||||
|
February 1989. The Negative Spirit, Larry Trainor, and Dr. Eleanor Poole merge to become a different being known as Rebis, who is not any of them. (Doom Patrol vol 2 #19)<br>
|
||||||
|
December 2016. In Gerard Way's run of Doom Patrol, Larry's Negative Spirit is known as Keeg Bovo. He is introduced for the first time. This may or may not be the same individual as Mercurius. (Doom Patrol vol 6 #2)
|
||||||
|
January 2017. Keeg Bovo takes Larry Trainor and Cliff Steele to his dimension, the Negative Space. (Doom Patrol vol 6 #4) <br>
|
||||||
|
August 2019. Larry Trainor becomes Positive Man. (Weight of the Worlds #2)<br>
|
||||||
|
July 2023. We learn that Keeg Bovo left Larry Trainor sometime after he became positive man. (Unstoppable Doom Patrol #4)<br><br>
|
||||||
|
|
||||||
|
<h3>Live Action Adaptation</h3>
|
||||||
|
February 2019. The first episode of Doom Patrol, introducing Larry Trainor's live action/cgi Negative Spirit, premieres on DC Universe. <br>
|
||||||
|
July 2020. Space Patrol airs and introduces Valentina Vostok, a one-episode character who has a Spirit of her own.<br>
|
||||||
|
September 2021. The episode Vacay Patrol, season 3 episode 2, writes the Negative Spirit off. It tells Larry it wants to take him to space, but abandons him, leaving for its dimension and sending Larry back home.<br>
|
||||||
|
October 2021. Keeg, the infant son of Larry Trainor and his Negative Spirit, is born.<br>
|
||||||
|
December 2022. In the show, Larry Trainor travels to the future and sees an adult version of his son, Keeg, and his own ghost in a dystopian apocalypse as the final season premieres.<br>
|
||||||
|
April 2023. In an adjacent universe, Titans, another DC show, has its own version of the Negative Spirit. This Spirit is an adult; however, as Larry's negative Spirit carries only signatures of blue, this Spirit carries both blue and red signatures, just as Larry's infant son does. It is unclear if this is the original Negative Spirit of Larry's or an adult version of Larry's son.<br>
|
||||||
|
November 2023. Larry's Negative Spirit makes an appearance once more as Larry time travels back to 1996. The Spirit possesses past!Larry's body and speaks to present!Larry.<br>
|
||||||
|
November 2023. Doom Patrol ends.<br>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<small><small>Contact<br>
|
||||||
|
Agnes Alien<br>
|
||||||
|
spirits@alien.hospital</small></small>
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue