diff --git a/Dockerfile b/Dockerfile index 47f023a1..1c60f01f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,30 +5,62 @@ # manually is the preferred setup for Notebook.ai instances. # The image to build from. -FROM ruby:2.7.6 +FROM ruby:3.2.1 # Properties/labels for the image. LABEL maintainer="Notebook.ai Contributors" +# Development ENV by default, but overridable with RAILS_ENV +ARG RAILS_ENV=development +ENV RAILS_ENV=${RAILS_ENV} + # Copy the current folder into the notebookai user's home directory. COPY . /home/notebookai # Set the notebookai user's home directory as the working directory. WORKDIR /home/notebookai -# Prep the image for runtime, this should all be done in one command -# to minimize image layers. -# See: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#minimize-the-number-of-layers +# Create the notebookai user and group RUN groupadd --system --gid 1000 notebookai && \ - useradd --system --home-dir /home/notebookai --gid notebookai --uid 1000 --shell /bin/bash notebookai && \ + useradd --system --home-dir /home/notebookai --gid notebookai --uid 1000 --shell /bin/bash notebookai + +# Install system dependencies +RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - && \ apt-get update -qq && \ - apt-get install -y build-essential libpq-dev nodejs imagemagick libmagickwand-dev && \ - rm --recursive --force /var/lib/apt/lists/* && \ - bundle install && \ - rails db:setup + apt-get install -y build-essential libpq-dev nodejs imagemagick libmagickwand-dev curl && \ + rm --recursive --force /var/lib/apt/lists/* + +# Install yarn +RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ + echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ + apt-get update && \ + apt-get install -y yarn + +# Install app dependencies, compile assets, and set up the database +RUN bundle install && \ + yarn install && \ + if [ "$RAILS_ENV" = "development" ] ; then \ + echo "Starting webpack-dev-server..." && \ + bin/webpack-dev-server & \ + else \ + echo "Compiling webpack assets..." && \ + rails webpacker:compile ; \ + fi && \ + rails db:setup && \ + rake db:migrate && \ + rm -f tmp/pids/server.pid # This image should expose the port 3000. # This does not actually expose the port, you'll have to expose it yourself by # using `-p 3000:3000/tcp` in Docker's CLI or `- "3000:3000"` in the in docker-compose.yml service's ports[]. # https://docs.docker.com/engine/reference/builder/#expose +#EXPOSE 3000/tcp + +# This image should expose port 3000. EXPOSE 3000/tcp + +# Finally, start the server using Puma! +CMD bundle exec puma -C config/puma.rb -e ${RAILS_ENV} -b tcp://0.0.0.0:3000 + +# And run it with +# docker run --name nb-webserver -p 80:3000 -d notebookai \ No newline at end of file diff --git a/Gemfile b/Gemfile index bd6d85a9..04ff35af 100644 --- a/Gemfile +++ b/Gemfile @@ -115,7 +115,7 @@ group :production do end group :test, :production do - gem 'pg', '~> 1.4' + gem 'pg', '~> 1.5' gem "mini_racer", "~> 0.6.3" # TODO: audit whether we can remove this end diff --git a/Gemfile.lock b/Gemfile.lock index 4049d6e1..a33f5cf8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -133,7 +133,7 @@ GEM autoprefixer-rails (10.4.7.0) execjs (~> 2) aws-eventstream (1.2.0) - aws-partitions (1.746.0) + aws-partitions (1.760.0) aws-sdk (3.1.0) aws-sdk-resources (~> 3) aws-sdk-accessanalyzer (1.23.0) @@ -331,7 +331,7 @@ GEM aws-sdk-connectparticipant (1.14.0) aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-core (3.171.0) + aws-sdk-core (3.171.1) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) @@ -594,7 +594,7 @@ GEM aws-sdk-kinesisvideosignalingchannels (1.13.0) aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-kms (1.63.0) + aws-sdk-kms (1.64.0) aws-sdk-core (~> 3, >= 3.165.0) aws-sigv4 (~> 1.1) aws-sdk-lakeformation (1.17.0) @@ -1102,7 +1102,7 @@ GEM aws-sdk-route53resolver (1.30.0) aws-sdk-core (~> 3, >= 3.120.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.120.1) + aws-sdk-s3 (1.122.0) aws-sdk-core (~> 3, >= 3.165.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.4) @@ -1280,7 +1280,7 @@ GEM bundler-audit (0.9.1) bundler (>= 1.2.0, < 3) thor (~> 1.0) - chartkick (4.1.3) + chartkick (5.0.2) climate_control (0.2.0) cocoon (1.2.15) codeclimate-test-reporter (1.0.9) @@ -1475,7 +1475,7 @@ GEM net-protocol netrc (0.11.0) newrelic_rpm (9.2.0) - nio4r (2.5.8) + nio4r (2.5.9) nokogiri (1.14.3) mini_portile2 (~> 2.8.0) racc (~> 1.4) @@ -1499,7 +1499,7 @@ GEM faraday (~> 1.4) faraday_middleware (~> 1) paypalhttp (1.0.1) - pg (1.4.6) + pg (1.5.3) pkg-config (1.5.1) pry (0.14.2) coderay (~> 1.1) @@ -1543,7 +1543,7 @@ GEM rails (>= 3.2) rails-ujs (0.1.0) railties (>= 3.1) - rails_admin (3.1.1) + rails_admin (3.1.2) activemodel-serializers-xml (>= 1.0) kaminari (>= 0.14, < 2.0) nested_form (~> 0.3) @@ -1598,7 +1598,7 @@ GEM sprockets (> 3.0) sprockets-rails tilt - selenium-webdriver (4.8.0) + selenium-webdriver (4.9.1) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) @@ -1635,7 +1635,7 @@ GEM stripe (>= 2.8, < 9) terrapin (0.6.0) climate_control (>= 0.0.3, < 1.0) - terser (1.1.13) + terser (1.1.15) execjs (>= 0.3.0, < 3) text-hyphen (1.4.1) textstat (0.1.8) @@ -1643,9 +1643,9 @@ GEM thor (1.2.1) tilt (2.0.11) timeago_js (3.0.2.2) - timeout (0.3.1) + timeout (0.3.2) tribute (3.6.0.0) - turbo-rails (1.3.3) + turbo-rails (1.4.0) actionpack (>= 6.0.0) activejob (>= 6.0.0) railties (>= 6.0.0) @@ -1724,7 +1724,7 @@ DEPENDENCIES paranoia paypal-checkout-sdk paypal_client - pg (~> 1.4) + pg (~> 1.5) pry puma (~> 5.6) puma-heroku diff --git a/app/assets/images/basil/character-jam.png b/app/assets/images/basil/character-jam.png new file mode 100644 index 00000000..ca0e3479 Binary files /dev/null and b/app/assets/images/basil/character-jam.png differ diff --git a/app/assets/images/placeholders/commission.png b/app/assets/images/placeholders/commission.png new file mode 100644 index 00000000..34b1d496 Binary files /dev/null and b/app/assets/images/placeholders/commission.png differ diff --git a/app/assets/images/placeholders/loading.gif b/app/assets/images/placeholders/loading.gif new file mode 100644 index 00000000..ca24753f Binary files /dev/null and b/app/assets/images/placeholders/loading.gif differ diff --git a/app/assets/images/placeholders/loading2.gif b/app/assets/images/placeholders/loading2.gif new file mode 100644 index 00000000..9b6e5007 Binary files /dev/null and b/app/assets/images/placeholders/loading2.gif differ diff --git a/app/assets/stylesheets/basil.scss b/app/assets/stylesheets/basil.scss index b3b2dc77..f5b7eed3 100644 --- a/app/assets/stylesheets/basil.scss +++ b/app/assets/stylesheets/basil.scss @@ -1,3 +1,47 @@ -// Place all the styles related to the Basil controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: https://sass-lang.com/ +$small-screen-up: 601px !default; +$medium-screen-up: 993px !default; +$large-screen-up: 1201px !default; +$small-screen: 600px !default; +$medium-screen: 992px !default; +$large-screen: 1200px !default; + +$medium-and-up: "only screen and (min-width : #{$small-screen-up})" !default; +$large-and-up: "only screen and (min-width : #{$medium-screen-up})" !default; +$small-and-down: "only screen and (max-width : #{$small-screen})" !default; +$medium-and-down: "only screen and (max-width : #{$medium-screen})" !default; +$medium-only: "only screen and (min-width : #{$small-screen-up}) and (max-width : #{$medium-screen})" !default; + +@mixin column-count($ct) { + -webkit-column-count: $ct; + -moz-column-count: $ct; + column-count: $ct; +} + +.cards-container { + column-break-inside: avoid; + .card { + display: inline-block; + overflow: visible; + } +} + +@media #{$small-and-down} { + .cards-container { + @include column-count(1); + } +} +@media #{$medium-and-up} { + .cards-container { + @include column-count(2); + } +} +@media #{$large-and-up} { + .cards-container { + @include column-count(3); + } +} + +.text-center { + text-align: center; +} + diff --git a/app/controllers/basil_controller.rb b/app/controllers/basil_controller.rb index 674e06e9..8a70b378 100644 --- a/app/controllers/basil_controller.rb +++ b/app/controllers/basil_controller.rb @@ -1,5 +1,5 @@ class BasilController < ApplicationController - before_action :authenticate_user!, except: [:complete_commission, :about, :stats] + before_action :authenticate_user!, except: [:complete_commission, :about, :stats, :jam, :queue_jam_job, :commission_info] before_action :require_admin_access, only: [:review], unless: -> { Rails.env.development? } @@ -210,6 +210,51 @@ class BasilController < ApplicationController def about end + def jam + @recent_commissions = BasilCommission.where(entity_id: nil).order('id DESC').limit(24) + @total_count = BasilCommission.where(entity_id: nil).count + + # For generating pie charts + @all_commissions = BasilCommission.where(entity_id: nil) + end + + def queue_jam_job + created_prompt = [ + jam_params[:age], + *jam_params[:features] + ].compact.join(', ') + + # Create our commission, then redirect back to preview it + BasilCommission.create!( + user: current_user, + entity: nil, + prompt: created_prompt, + job_id: SecureRandom.uuid, + style: ["realistic", "realistic2", "realistic3"].sample, + final_settings: jam_params + ) + + redirect_back(fallback_location: basil_jam_path, notice: "#{jam_params.fetch(:name, '').presence || 'Your character'} will be visualized shortly. Find them on this page!") + end + + def commission_info + @commission = BasilCommission.find_by(job_id: params[:jobid]) + raise "No BasilCommission with ID #{params[:jobid]}" if @commission.nil? + + render json: { + image_url: @commission.image.url, + user_id: @commission.user_id, + prompt: @commission.prompt, + job_id: @commission.job_id, + created_at: @commission.created_at, + updated_at: @commission.updated_at, + completed_at: @commission.completed_at, + style: @commission.style, + final_settings: @commission.final_settings, + cached_seconds_taken: @commission.cached_seconds_taken, + } + end + def stats @commissions = BasilCommission.all.with_deleted @@ -509,10 +554,11 @@ class BasilController < ApplicationController def complete_commission commission = BasilCommission.find_by(job_id: params[:jobid]) - return if commission.nil? - - commission.update(completed_at: DateTime.current, - final_settings: JSON.parse(params[:settings])) + raise "Tried to complete commission with invalid job ID #{params[:jobid]}" if commission.nil? + + merged_settings = commission.final_settings || {} + commission.update!(completed_at: DateTime.current, + final_settings: merged_settings.merge(JSON.parse(params.fetch(:settings, "{}")))) # Attach the image in S3 to our `image` ActiveStorage relation key = "job-#{params[:jobid]}.png" @@ -554,6 +600,7 @@ class BasilController < ApplicationController @commissions = BasilCommission.where(id: @reviewed_commission_ids.pluck(:basil_commission_id)) .where.not(completed_at: nil) .where(user: current_user) + .where.not(entity_type: nil, entity_id: nil) .order(created_at: :desc) .limit(50) .includes(:entity) @@ -565,6 +612,7 @@ class BasilController < ApplicationController @commissions = BasilCommission.where.not(id: @reviewed_commission_ids) .where.not(completed_at: nil) .where(user: current_user) + .where.not(entity_type: nil, entity_id: nil) .order(created_at: :desc) .limit(50) .includes(:entity) @@ -595,4 +643,8 @@ class BasilController < ApplicationController def commission_params params.require(:basil_commission).permit(:style, :entity_type, :entity_id, field: {}) end + + def jam_params + params.require(:commission).permit(:name, :age, features: []) + end end diff --git a/app/controllers/content_controller.rb b/app/controllers/content_controller.rb index 572471ca..a28b80cc 100644 --- a/app/controllers/content_controller.rb +++ b/app/controllers/content_controller.rb @@ -168,6 +168,12 @@ class ContentController < ApplicationController if user_signed_in? && current_user.can_create?(@content.class) \ || PermissionService.user_has_active_promotion_for_this_content_type(user: current_user, content_type: @content.class.name) + # For users who are creating premium content in a collaborated universe without premium of their own + # we want to default that content into one of their collaborated unvierses. + if !current_user.on_premium_plan? && Rails.application.config.content_types[:premium].map(&:name).include?(@content.class.name) + @content.universe_id = current_user.contributable_universes.first.try(:id) + end + if params.key?(:document_entity) entity = DocumentEntity.find_by(id: params.fetch(:document_entity).to_i) if entity.document_owner == current_user diff --git a/app/mailers/collaboration_mailer.rb b/app/mailers/collaboration_mailer.rb index 5bc1c99d..164ae705 100644 --- a/app/mailers/collaboration_mailer.rb +++ b/app/mailers/collaboration_mailer.rb @@ -1,7 +1,7 @@ class CollaborationMailer < ApplicationMailer default from: "collaboration@notebook.ai" - def contributor_invitation(inviter:, invite_email:, universe:) + def contributor_invitation(inviter, invite_email, universe) @inviter = inviter @universe = universe diff --git a/app/models/basil_commission.rb b/app/models/basil_commission.rb index cb6b08a6..22c137de 100644 --- a/app/models/basil_commission.rb +++ b/app/models/basil_commission.rb @@ -1,8 +1,8 @@ class BasilCommission < ApplicationRecord acts_as_paranoid - belongs_to :user - belongs_to :entity, polymorphic: true + belongs_to :user, optional: true + belongs_to :entity, polymorphic: true, optional: true has_one_attached :image, service: :amazon_basil, @@ -18,8 +18,7 @@ class BasilCommission < ApplicationRecord # TODO clean this up and put it in a service sts_client = Aws::STS::Client.new(region: region) - queue_url = 'https://sqs.' + region + '.amazonaws.com/' + - sts_client.get_caller_identity.account + '/' + queue_name + queue_url = 'https://sqs.' + region + '.amazonaws.com/' + sts_client.get_caller_identity.account + '/' + queue_name sqs_client = Aws::SQS::Client.new(region: region) message_body = { diff --git a/app/services/contributor_service.rb b/app/services/contributor_service.rb index 97007a1f..d33f9120 100644 --- a/app/services/contributor_service.rb +++ b/app/services/contributor_service.rb @@ -1,5 +1,5 @@ class ContributorService < Service - def self.invite_contributor_to_universe universe:, email: + def self.invite_contributor_to_universe(universe:, email:) # First, look up whether a user already exists for this invite related_user = User.find_by(email: email.downcase) @@ -31,19 +31,19 @@ class ContributorService < Service end end - def self.send_invite_email_to inviter:, email:, universe: + def self.send_invite_email_to(inviter:, email:, universe:) CollaborationMailer.contributor_invitation( - inviter: inviter, - invite_email: email, - universe: universe + inviter, + email, + universe ).deliver_now! if Rails.env.production? end - def self.send_contributor_notice_to inviter:, email:, universe: + def self.send_contributor_notice_to(inviter:, email:, universe:) CollaborationMailer.contributor_invitation( - inviter: inviter, - invite_email: email, - universe: universe + inviter, + email, + universe ).deliver_now! if Rails.env.production? end end diff --git a/app/services/export_service.rb b/app/services/export_service.rb index f9b16ebd..21f8954f 100644 --- a/app/services/export_service.rb +++ b/app/services/export_service.rb @@ -61,8 +61,8 @@ class ExportService < Service if value_for_this_field.present? json_list = JSON.parse(value_for_this_field.value) formatted_names = json_list.map do |link_code| - query_link_code_with_cache(*link_code.split('-')).name - end + query_link_code_with_cache(*link_code.split('-')).try(:name) + end.compact export_text << " #{field.label}: #{formatted_names.to_sentence}\n" else @@ -310,8 +310,8 @@ class ExportService < Service # TODO: we should probably whitelist content_type from valid page types here # If there's no cache, we unfortunately need to do a query to resolve the link code - content = class_from_name(content_type_name).find(content_id) - @content_cache[cache_key] = content + content = class_from_name(content_type_name).find_by(id: content_id) + @content_cache[cache_key] = content if content.present? return content end diff --git a/app/views/basil/_character_jam.html.erb b/app/views/basil/_character_jam.html.erb new file mode 100644 index 00000000..03b31782 --- /dev/null +++ b/app/views/basil/_character_jam.html.erb @@ -0,0 +1,478 @@ + + + + + +<%= content_for :full_width_page_header do %> +
+
+

+ <%= Character.icon %> + Welcome to our Character VizJam! +

+ + +
+ +
+

+ Recent visualizations (click one to see their traits, refresh for more) + + <%= number_with_delimiter @total_count %> + +

+ +
+
+ <% @recent_commissions.each do |commission| %> + +
+
+ <%= link_to "#details-#{commission.job_id}", class: 'modal-trigger waves-effect waves-light' do %> + <% if commission.complete? %> + <%= image_tag commission.image, class: 'commission-image' %> + <% else %> + <%= image_tag image_path("placeholders/loading.gif"), class: 'commission-image', style: 'background: #2196F3' %> + <% end %> + + <%= commission.final_settings&.fetch('name', '').presence || 'No name' %> + + <% end %> +
+
+ + <% if commission.completed_at.nil? %> + + <% end %> + + + <% end %> +
+
+ +
+
+<% end %> + + + diff --git a/app/views/basil/character.html.erb b/app/views/basil/character.html.erb deleted file mode 100644 index a288d31e..00000000 --- a/app/views/basil/character.html.erb +++ /dev/null @@ -1,224 +0,0 @@ - - - -
- <%= form_for BasilCommission.new, url: basil_character_path(@character) do |f| %> - <%= f.hidden_field :style, value: 'realistic' %> - <%= f.hidden_field :entity_type, value: 'Character' %> - <%= f.hidden_field :entity_id, value: @character.id %> -
-
- <%= link_to basil_path, class: 'grey-text text-darken-2' do %> - chevron_left - Back to my character list - <% end %> -
-
- <%= image_tag @character.random_image_including_private(format: :medium), style: 'width: 100%' %> -

<%= link_to @character.name, @character %>

- <%= link_to 'Edit this character page', edit_polymorphic_path(@character), class: 'grey-text text-darken-2' %> -
- - - - <% if !shown_any_value %> -
- Basil works best with guidance! Please fill out some fields in the "Looks" category for this character - before requesting an image. -
- <% end %> - -
- <% if @can_request_another %> - <%= link_to 'Customize per-field importance', "javascript:var sliders = document.getElementsByClassName('js-importance-slider'); for(var i = 0; i < sliders.length; i++) sliders.item(i).classList.remove('hide')" %> - <% end %> - -
- How to customize per-field importance -

- - This allows you to tell Basil which fields are more or less important to you. For example, if Basil isn't - getting a character's hair color right, you can increase the importance of the "Hair Color" field. -

- You can also tell Basil to ignore a field entirely by dragging the slider all the way to the left. -
-
- -
-

- This is still a work in progress and very much a beta that will change a lot before releasing publicly, - but feel free to use it as much as you'd like to provide feedback! -

-

- If you run into any issues, please let me know <%# in the %> - <%# link_to 'Site Support forums', 'https://www.notebook.ai/forum/site-support' %> - <%# or %> <%= link_to 'on Discord', 'https://discord.gg/7WCuGxY3AW' %>. -

-
-
- -
- <% if @can_request_another && shown_any_value %> -
Available styles
- <% %w(realistic painting sketch digital anime abstract).each do |style| %> - <%= link_to "javascript:commission_basil('#{style}')" do %> -
- <%= style.humanize %> - chevron_right -
- <% end %> - <% end %> - <% end %> - - <% if @can_request_another && shown_any_value %> -
Experimental styles
- <% %w(painting2 horror watercolor).each do |style| %> - <%= link_to "javascript:commission_basil('#{style}')" do %> -
- <%= style.humanize %> - chevron_right -
- <% end %> - <% end %> - <% end %> - - <% if !@can_request_another %> -
- Basil is working on your <%= pluralize @in_progress_commissions.count, 'requested commission' %>. -

- As soon as he completes one, you'll be able to request another. -
- <% end %> -
- <% end %> - -
- <% @commissions.each do |commission| %> -
- <% if commission.complete? %> - <%# image_tag commission.image, style: 'width: 100%' %> - <% - s3 = Aws::S3::Resource.new(region: "us-east-1") - obj = s3.bucket(commission.s3_bucket).object("job-#{commission.job_id}.png") - %> -
-
- <%= link_to obj.presigned_url(:get) do %> - <%= image_tag obj.presigned_url(:get) %> - <% end %> -
-
-
-
- <%= @character.name %> - <% if commission.style? %> - (<%= commission.style.humanize %>) - <% end %> -
-
- Completed <%= time_ago_in_words commission.completed_at %> ago - ·
- Took <%= distance_of_time_in_words commission.completed_at - commission.created_at %> -
-
-
Feedback for Basil
-
- <%= form_for commission.basil_feedbacks.find_or_initialize_by(user: current_user), url: basil_feedback_path(commission.job_id), method: :POST, remote: true do |f| %> -
- -
-
- -
-
- -
-
- -
- <% end %> -
-
-
- -
-
- <% else %> -
- Basil is still working on this commission... (style: <%= commission.style %>) -
- (Requested <%= time_ago_in_words(commission.created_at) %> ago) -
-
- <% end %> -
- <% end %> -
-
diff --git a/app/views/basil/jam.html.erb b/app/views/basil/jam.html.erb new file mode 100644 index 00000000..4775d7fc --- /dev/null +++ b/app/views/basil/jam.html.erb @@ -0,0 +1,444 @@ +
+
+

+ <%= Character.icon %> + The Character VizJam has concluded! +

+ +
+ <%= link_to asset_path('basil/character-jam.png') do %> + <%= image_tag 'basil/character-jam.png', style: 'width: 100%' %> + <% end %> +
+ Thank you for participating! +
+
+ +
+

Together, we visualized 1,611 characters!

+

+ In the spirit of AI transparency, I've compiled some aggregate visualizations of the kinds of characters + that were most — and least — visualized during this VizJam. +

+ + + +

+ If you noticed that any particular age, gender, race, or other trait produced + lower-quality images than others, please <%= link_to 'let me know', 'https://discord.gg/bDE8g5YRzp' %> + so I can continue to make our AI models work better for all kinds of characters! +

+ +
+ +
+ <%= area_chart({ + "2023-06-08"=>208, + "2023-06-09"=>515, + "2023-06-10"=>255, + "2023-06-11"=>128, + "2023-06-12"=>167, + "2023-06-13"=>338}, title: 'New visualizations per day', colors: ['#E91E63'], max: 550, height: '200px') + %> +
+
+
+ +<% + @content_type = Character + singular_class_name = @content_type.name + pluralized_class_name = @content_type.name.pluralize + premium_page = !User.new.can_create?(@content_type) +%> + +
+
+

+ You can still create characters and visualize them on Notebook.ai! +

+
+ +
+
+
+ <%= image_tag "card-headers/#{pluralized_class_name.downcase}.webp", class: 'materialboxed tooltipped', alt: "The default image used for all #{pluralized_class_name.downcase} on Notebook.ai, but you can replace it with your own uploads.", data: { tooltip: "The default image used for all #{pluralized_class_name.downcase} on Notebook.ai, but you can replace it with your own uploads."} %> +
+
+
+

Creating <%= pluralized_class_name %> on Notebook.ai

+

+ <%= t("content_descriptions.#{singular_class_name.downcase}") %> +

+

+ Creating <%= pluralized_class_name.downcase %> on Notebook.ai is easy. +

+

+ To get started, just click <%= pluralized_class_name %> under the "Worldbuilding" header in the site sidebar. + You'll be able to see or edit all of your existing <%= singular_class_name.downcase %> pages and create new ones at any time. +

+
+
+
+
+ +
+
+
+

+ + <%= @content_type.icon %> + + + Get a head start with a rich + <%= singular_class_name.downcase %> + template +

+
+ +
+

+ Templates on Notebook.ai are what help our unique worldbuilding system better understand your world. +

+

+ You can fill out as little or as much as you'd like on every new <%= singular_class_name.downcase %>. You'll see + progress indicators every time you edit it to show where you can make progress on, and our system will + intelligently generate questions for you around the site that will automatically save your answers + to the proper place on your <%= singular_class_name.downcase %> page. +

+

+ Templates are also fully customizable across every <%= singular_class_name.downcase %> in your notebook. +

+

+ You can browse the default template for <%= pluralized_class_name.downcase %> here; click any category + to see its questions. +

+
+
+
+
+
+ +
+
+
+
+ <%= image_tag 'basil/portrait.png', style: 'width: 420px' %> +
+
+
+
+ palette + Visualize your characters +
+

+ After you've created a character on Notebook.ai, visualizing them is as easy as picking an image style and clicking a button. + Everything you've written about what they look like on their notebook page is automatically included. +

+
+

+ Image visualization is a Premium feature, but you can generate up to 100 images for free to try it out for yourself. +

+
+
+
+
+
+ +<% if user_signed_in? %> +
+
Already logged in? Great!
+ <%= link_to 'Visualize your ideas', basil_path, class: 'btn btn-large hoverable blue white-text' %> +
+<% else %> +
+
Want to keep visualizing your ideas?
+ <%= link_to 'Get started with Notebook.ai', new_registration_path(User), class: 'btn btn-large hoverable blue white-text' %> +
+<% end %> + +<% 10.times do %>
<% end %> diff --git a/app/views/basil/review.html.erb b/app/views/basil/review.html.erb index 182c65f5..3d35d5ed 100644 --- a/app/views/basil/review.html.erb +++ b/app/views/basil/review.html.erb @@ -20,7 +20,7 @@ <% @current_queue_items.each do |commission| %>
  • <%= commission.entity_type %>-<%= commission.entity_id %> (<%= commission.style %>) - for U-<%= commission.user_id %> + for U-<%= commission.user_id %> (#<%= commission.id %>)
  • <% end %> @@ -29,15 +29,10 @@ <% @recent_commissions.each do |commission| %>
    <% if commission.complete? %> - <%# image_tag commission.image, style: 'width: 100%' %> - <% - s3 = Aws::S3::Resource.new(region: "us-east-1") - obj = s3.bucket(commission.s3_bucket).object("job-#{commission.job_id}.png") - %>
    - <%= link_to obj.presigned_url(:get) do %> - <%= image_tag obj.presigned_url(:get) %> + <%= link_to commission.image do %> + <%= image_tag commission.image %> <% end %>
    @@ -45,12 +40,17 @@
    <%= commission.id %>. <% if commission.entity.present? %> + <%= content_class_from_name(commission.entity_type).icon %> <%= link_to commission.entity.name, commission.entity %> <% end %> <% if commission.style? %> (<%= commission.style.humanize %>) <% end %> - by <%= link_to commission.user.name, commission.user %> + <% if commission.user_id %> + by <%= link_to commission.user.name, commission.user %> + <% else %> + anonymous generation + <% end %>
    <%= commission.job_id %>
      diff --git a/app/views/basil/stats.html.erb b/app/views/basil/stats.html.erb index 0dc5ce95..20c7dff7 100644 --- a/app/views/basil/stats.html.erb +++ b/app/views/basil/stats.html.erb @@ -42,7 +42,7 @@
    <%= - area_chart @commissions.group_by_day(:created_at).map { |date, count| [date.to_date, count] }, + area_chart @commissions.where('created_at > ?', 30.days.ago.beginning_of_month).group_by_day(:created_at).map { |date, count| [date.to_date, count] }, colors: ['#9C27B0', '#2196F3'], title: 'Images created per day', suffix: ' images' diff --git a/app/views/content/list/_document_table.html.erb b/app/views/content/list/_document_table.html.erb index 67953b39..71411a28 100644 --- a/app/views/content/list/_document_table.html.erb +++ b/app/views/content/list/_document_table.html.erb @@ -345,6 +345,12 @@