robin_cms 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7496d517d8e733bc0e0876e806347a0c3670efc489d974b16718e3b015d0a8e1
4
- data.tar.gz: cc6df56fde6950e75fddf3fef947e5363cdc67f506bd07c16f5bbe0f1940d629
3
+ metadata.gz: 667498a061c442f90e57b20fdd7a12db60e76f447e0a948be22276620c5532ef
4
+ data.tar.gz: 4e6317eee498285fc39f939cb86b80847ab9e5c629ad7864efe8c2761ff17bb7
5
5
  SHA512:
6
- metadata.gz: 6f4c01cfe9c26b0c7e864af98d405dfe55fbe986f78a3ee9295cb20e9c6bd808bbd9473685ddd17c158574540a63535c16a783a650a937f3e6419f215a81c51f
7
- data.tar.gz: 36e240374f3bcf0c564d15347b4847a6b781d7b6192b3b3214fbf35f297748415d1729773039dca5f61b00ce088d681d7b3b02ac340fc3aa1f981c2fa4dd63dc
6
+ metadata.gz: a5ae9452f1da1b65f19f21798d5036109e88114af97bdf1c3d10d453992034361cedf22805e660b380b0b2cbcd5083b2ec005ea900b4666b436e58a78078395f
7
+ data.tar.gz: 15395c440febd9fd0dea69bc7cf99c9338a3d97eef5a039662e46670e1d1dfe0b259a4004c3378d595d2b1398a23a860980a8476c79959696fd69edbe5471942
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  > This software is currently in beta. There may be bugs and breaking changes.
5
5
  > If you find a bug, I'd love to hear about it.
6
6
 
7
- ![Robin CMS logo](./assets/robin-logo.png)
7
+ ![Robin CMS logo](./doc/assets/robin-logo.png)
8
8
 
9
9
  Robin CMS is a minimalist flat-file CMS built with Ruby and Sinatra. It is
10
10
  designed to be used by developers for creating custom built websites where the
@@ -61,125 +61,53 @@ that are commonly found in CMS software have been omitted.
61
61
  content. You can however add richtext fields to your content models (see
62
62
  example below).
63
63
 
64
- ## Installation
65
-
66
- Just the usual incantation:
67
-
68
- ```sh
69
- gem install robin_cms
70
- ```
71
-
72
- ## Configuring
73
-
74
- You can define your content model in a `_cms.yml` file like this:
75
-
76
- ```yml
77
- url: https://example.com
78
- title: Example
79
- libraries:
80
- - id: poem
81
- type: collection
82
- label: Poem
83
- location: poems
84
- filetype: html
85
- fields:
86
- - { label: Title, id: title, type: input }
87
- - { label: Author, id: author_name, type: input }
88
- - { label: Content, id: content, type: richtext }
89
- - id: book
90
- type: data
91
- label: Book
92
- location: books
93
- filetype: yml
94
- fields:
95
- - { label: Title, id: title, type: input }
96
- - { label: Author, id: author_name, type: input }
97
- ```
98
-
99
- The admin username and password needs to be set in a `.htpasswd` file in the
100
- root directory of the project. Obviously make sure you `.gitignore` that file.
101
- Also make sure your static site generator is ignoring it because you don't want
102
- it in your public directory! Each line of the `.htpasswd` file should follow
103
- the format `<username>:<password>`, but note that only a single
104
- username/password is supported for now. The password needs to be encrypted with
105
- bcrypt. You can do this in Ruby with the `bcrypt` gem:
106
-
107
- ```sh
108
- ruby -r bcrypt -e "puts BCrypt::Password.create('mypassword')"
109
- ```
110
-
111
- Another thing to note is that if no `.htpasswd` file is found, it will
112
- automatically create one with username "admin" and password "admin". This lets
113
- you play around with it locally without configuring a password. So make sure
114
- you create a `.htpasswd` file before running it in production!
115
-
116
- You'll also need to expose a `SESSION_SECRET` environment variable. If you
117
- don't, it will create one for you, but it creates a new secret each time
118
- the server starts, meaning you will have to log in again whenever you restart
119
- the server. It is recommended to create one via Ruby's SecureRandom package.
120
-
121
- ```sh
122
- ruby -r securerandom -e "puts SecureRandom.hex(64)"
123
- ```
124
-
125
- See the [examples](./examples) folder for a full example. I haven't written any
126
- documentation yet, but the example `_cms.yml` file is thoroughly commented to
127
- explain each of the fields.
128
-
129
64
  ## Usage
130
65
 
131
- You have a few options for using this gem in your project. Firstly, you can use
132
- it directly as a CMS for any Static Site Generator with the following
133
- `config.ru`:
134
-
135
- ```ruby
136
- require 'robin_cms'
137
- map "/admin" { run RobinCMS::CMS.new }
138
- ```
66
+ Robin CMS is packaged as a Ruby gem, so installation is as simple as
139
67
 
140
- Now you should be able to run `rackup`, and go to `http://localhost:9292/admin`
141
- in your browser.
68
+ gem install robin_cms
142
69
 
143
- Alternatively, you can embed it into your own Sinatra project like this:
70
+ It's possible to use the CMS in a few different ways. You can use it as a
71
+ standalone CMS for any Static Site Generator, you can embed it in a dynamic
72
+ Sinatra app, or you can use it as a Jekyll plugin. If you're already using
73
+ Jekyll as your SSG, this is the simplest approach.
144
74
 
145
- ```ruby
146
- require 'robin_cms'
147
- require 'sinatra'
75
+ See the [documentation](./docs/index.adoc) for further information on usage.
148
76
 
149
- use RobinCMS::CMS
150
-
151
- get '/' do
152
- 'Hello, world!'
153
- end
154
-
155
- run Sinatra::Application.new
156
- ```
157
-
158
- Yet another option is to use it as a [Jekyll](https://jekyllrb.com/) plugin. To
159
- do this, you just need to pop `robin_cms` in the `:jekyll_plugins` group of
160
- your `Gemfile` like so:
161
-
162
- ```
163
- gem "jekyll"
164
-
165
- group :jekyll_plugins do
166
- gem "robin_cms"
167
- end
168
- ```
77
+ ## Configuring
169
78
 
170
- After running `bundle exec jekyll serve`, the CMS should be available on your
171
- website under `/admin`. Note that if using it as a Jekyll plugin, you can put
172
- your config in Jekyll's `_config.yml` file under the `cms` field. You also
173
- don't need to specify the `url` and `title` fields, as these are taken from the
174
- Jekyll config.
79
+ Your content model is defined in a single YAML file. It acts as a sort of
80
+ schema for your content. An example YAML file looks like this:
81
+
82
+ url: https://example.com
83
+ title: Example
84
+ libraries:
85
+ - id: poem
86
+ type: collection
87
+ label: Poem
88
+ location: poems
89
+ filetype: html
90
+ fields:
91
+ - { label: Title, id: title, type: input }
92
+ - { label: Author, id: author_name, type: input }
93
+ - { label: Content, id: content, type: richtext }
94
+ - id: book
95
+ type: data
96
+ label: Book
97
+ location: books
98
+ filetype: yml
99
+ fields:
100
+ - { label: Title, id: title, type: input }
101
+ - { label: Author, id: author_name, type: input }
102
+
103
+ See the [docs](./docs/index.adoc) for more details on configuration. You can
104
+ also find some examples [here](./examples).
175
105
 
176
106
  ## Testing
177
107
 
178
108
  Unit test are written in RSpec. To run them:
179
109
 
180
- ```
181
- rspec
182
- ```
110
+ rake test
183
111
 
184
112
  ## Roadmap
185
113
 
data/lib/robin_cms/cms.rb CHANGED
@@ -53,7 +53,6 @@ module RobinCMS
53
53
 
54
54
  post "/login" do
55
55
  authenticate!(params[:username], params[:password])
56
-
57
56
  session[:auth_user] = params[:username]
58
57
  redirect to(home_page)
59
58
  rescue AuthenticationError
@@ -83,11 +82,9 @@ module RobinCMS
83
82
  end
84
83
 
85
84
  authenticate!(session[:auth_user], params[:old_password])
86
-
87
85
  update_credentials(params[:new_password])
88
86
  session[:auth_user] = nil
89
87
  flash[:success] = "Password updated succesfully. Please log in with your new credentials."
90
-
91
88
  redirect to("/login")
92
89
  rescue AuthenticationError
93
90
  flash[:error] = "Incorrect username or password"
@@ -100,69 +97,67 @@ module RobinCMS
100
97
  published: params[:published],
101
98
  q: params[:q]
102
99
  )
103
-
104
100
  erb :library
105
101
  end
106
102
 
107
103
  get "/libraries/:kind/item" do
108
- if params[:id]
109
- @item = @library.find_one(params[:id])
110
- halt 404 unless @item
111
- else
112
- @item = @library.blank
113
- end
104
+ @item = @library.blank
105
+ erb :library_item
106
+ end
114
107
 
108
+ get "/libraries/:kind/item/:id" do
109
+ @item = @library.find_one(params[:id])
110
+ halt 404 unless @item
115
111
  erb :library_item
116
112
  end
117
113
 
118
114
  post "/libraries/:kind/item" do
119
- if params[:id]
120
- @item = @library.find_one(params[:id])
121
- halt 404 unless @item
122
-
123
- if params[:image]
124
- filename = params[:image][:filename]
125
- tempfile = params[:image][:tempfile].to_path
126
-
127
- @library.delete_static(@item.attributes[:image_src])
128
- static_path = @library.create_static(filename, tempfile)
129
- params[:image_src] = "/" + static_path
130
- end
131
-
132
- @item.attributes = params
133
- @library.write(@item)
134
- else
135
- if params[:image]
136
- filename = params[:image][:filename]
137
- tempfile = params[:image][:tempfile].to_path
138
-
139
- static_path = @library.create_static(filename, tempfile)
140
- params[:image_src] = "/" + static_path
141
- end
142
-
143
- @library.create(params)
115
+ if params[:image]
116
+ static_path = @library.create_static(
117
+ params[:image][:filename],
118
+ params[:image][:tempfile].to_path
119
+ )
120
+ params[:image_src] = "/" + static_path
144
121
  end
145
122
 
123
+ @library.create(params)
146
124
  redirect to("/libraries/#{params[:kind]}")
147
125
  rescue ItemExistsError
148
126
  flash[:error] = "An item with the same name already exists"
149
127
  redirect to("/libraries/#{params[:kind]}/item")
150
128
  end
151
129
 
152
- post "/libraries/:kind/item/delete" do
153
- if params[:id]
154
- @item = @library.find_one(params[:id])
155
- halt 404 unless @item
130
+ post "/libraries/:kind/item/:id" do
131
+ @item = @library.find_one(params[:id])
132
+ halt 404 unless @item
156
133
 
157
- if @item.attributes[:image_src]
134
+ if params[:image]
135
+ # If there is an existing image for this item, delete it to prevent
136
+ # accumulation of old files.
137
+ if @item.attributes[:image_src] && !@item.attributes[:image_src].empty?
158
138
  @library.delete_static(@item.attributes[:image_src])
159
139
  end
140
+ static_path = @library.create_static(
141
+ params[:image][:filename],
142
+ params[:image][:tempfile].to_path
143
+ )
144
+ params[:image_src] = "/" + static_path
145
+ end
160
146
 
161
- @library.delete(@item.id)
162
- else
163
- halt 404
147
+ @item.attributes = params
148
+ @library.write(@item)
149
+ redirect to("/libraries/#{params[:kind]}")
150
+ end
151
+
152
+ post "/libraries/:kind/item/:id/delete" do
153
+ @item = @library.find_one(params[:id])
154
+ halt 404 unless @item
155
+
156
+ if @item.attributes[:image_src]
157
+ @library.delete_static(@item.attributes[:image_src])
164
158
  end
165
159
 
160
+ @library.delete(@item.id)
166
161
  redirect to("/libraries/#{params[:kind]}")
167
162
  end
168
163
 
@@ -69,6 +69,7 @@
69
69
  "options": { "$ref": "#/$defs/options_schema" },
70
70
  "dimensions": { "$ref": "#/$defs/image_dimensions_schema" },
71
71
  "filetype": { "$ref": "#/$defs/image_filetype_schema" },
72
+ "description": { "type": "string" },
72
73
  "order": { "type": "number" }
73
74
  }
74
75
  }
@@ -108,7 +109,8 @@
108
109
  "readonly": { "type": "boolean" },
109
110
  "options": { "$ref": "#/$defs/options_schema" },
110
111
  "dimensions": { "$ref": "#/$defs/image_dimensions_schema" },
111
- "filetype": { "$ref": "#/$defs/image_filetype_schema" }
112
+ "filetype": { "$ref": "#/$defs/image_filetype_schema" },
113
+ "description": { "type": "string" }
112
114
  }
113
115
  }
114
116
  }
@@ -42,6 +42,7 @@ module RobinCMS
42
42
  id: "title",
43
43
  type: "text",
44
44
  required: true,
45
+ description: "Provide a short, descriptive title.",
45
46
  order: 1
46
47
  }, {
47
48
  label: "Published date",
@@ -65,6 +66,9 @@ module RobinCMS
65
66
  label: "Published",
66
67
  value: true
67
68
  }],
69
+ description: "Set this to draft if you're not ready to publish this item
70
+ just yet. Draft items won't appear on your website, even after clicking
71
+ 'Publish site'.",
68
72
  order: 2
69
73
  }].freeze
70
74
 
@@ -74,7 +78,9 @@ module RobinCMS
74
78
  }, {
75
79
  id: "image_alt",
76
80
  type: "text",
77
- label: "Alt text"
81
+ label: "Alt text",
82
+ description: "Provide a descriptive and concise description of your
83
+ image. This helps to improve the accessibility of your website."
78
84
  }].freeze
79
85
 
80
86
  def self.parse(config_file: "_cms.yml", jekyll_plugin: false)
@@ -4,31 +4,37 @@ module RobinCMS
4
4
  # This module provides methods for editing image files.
5
5
  # No methods are required in the base class in order to use this mixin.
6
6
  module Editable
7
- def resize_image(filepath, dimensions)
7
+ def resize_image(filepath, to:)
8
8
  # The mogrify command edits images in place. For more info, see
9
9
  # mogrify(1).
10
-
11
- system("mogrify -resize #{dimensions} #{filepath}")
10
+ system("mogrify -resize #{to} #{filepath}")
12
11
  if $?.exitstatus != 0
13
12
  raise ConversionError, "Could not resize image #{filepath}"
14
13
  end
15
14
  end
16
15
 
17
- def format_image(filepath, filetype)
18
- system("mogrify -format #{filetype} #{filepath}")
16
+ def convert_image(filepath, to:)
17
+ system("mogrify -format #{to} #{filepath}")
19
18
  if $?.exitstatus != 0
20
19
  raise ConversionError, "Could not format image #{filepath}"
21
20
  end
22
21
 
23
- # The name of the converted file will be the same as the original but
24
- # with a new file extension.
25
- converted = filepath.sub(/#{File.extname(filepath)}$/, ".#{filetype}")
22
+ # The file created by the mogrify command will be the same as the
23
+ # original but with the new file extension.
24
+ converted = filepath.sub(/#{File.extname(filepath)}$/, ".#{to}")
26
25
 
27
- # Mogrify's format command creates a new file with the new extension.
28
- # Copy the contents of this file to the original, then delete the newly
29
- # created file.
30
- IO.copy_stream(converted, filepath)
31
- File.delete(converted)
26
+ if converted == filepath
27
+ # If the converted file has the same filename as the original (i.e. no
28
+ # conversion happened), there is nothing more to do.
29
+ filepath
30
+ else
31
+ # Otherwise, copy the contents of the mogrify output file to a new
32
+ # tempfile and delete the mogrify output.
33
+ tempfile = Tempfile.new(["robin_cms" ".#{to}"])
34
+ IO.copy_stream(converted, tempfile)
35
+ File.delete(converted)
36
+ tempfile
37
+ end
32
38
  end
33
39
  end
34
40
  end
@@ -100,5 +100,9 @@ module RobinCMS
100
100
  value = @attributes[field[:id].to_sym] || field[:default]
101
101
  value.to_s
102
102
  end
103
+
104
+ def can_delete?
105
+ @id && @opts[:can_delete]
106
+ end
103
107
  end
104
108
  end
@@ -42,6 +42,7 @@ module RobinCMS
42
42
 
43
43
  def create_static(filename, tempfile)
44
44
  path = File.join(@schema[:static_location], File.basename(make_slug(filename)))
45
+ FileUtils.mkdir_p(File.dirname(path))
45
46
 
46
47
  if File.exist?(path)
47
48
  raise ItemExistsError, "An item with the same name already exists"
@@ -51,18 +52,24 @@ module RobinCMS
51
52
  dimensions = image_field&.dig(:dimensions)
52
53
  filetype = image_field&.dig(:filetype)
53
54
 
54
- resize_image(tempfile, dimensions) if dimensions
55
- format_image(tempfile, filetype) if filetype
55
+ resize_image(tempfile, to: dimensions) if dimensions
56
56
 
57
- FileUtils.mkdir_p(File.dirname(path))
58
- FileUtils.cp(tempfile, path)
57
+ # Only convert if the specified filetype is different to the actual
58
+ # filetype. Unlike resize_image which modifies the image in-pace, this
59
+ # function necessarily creates a new file, so we need to ensure we
60
+ # update the path with the new file extension.
61
+ if filetype && File.extname(tempfile) != ".#{filetype}"
62
+ tempfile = convert_image(tempfile, to: filetype)
63
+ path = File.join(File.dirname(path), "#{File.basename(path, '.*')}.#{filetype}")
64
+ end
59
65
 
66
+ FileUtils.cp(tempfile, path)
60
67
  path
61
68
  end
62
69
 
63
70
  def delete_static(filename)
71
+ return if filename.nil? || filename.empty?
64
72
  path = File.join(@schema[:static_location], File.basename(filename))
65
-
66
73
  return unless File.exist?(path)
67
74
 
68
75
  File.delete(path)
@@ -79,5 +86,9 @@ module RobinCMS
79
86
  def [](key)
80
87
  @schema[key]
81
88
  end
89
+
90
+ def kind
91
+ @schema[:id]
92
+ end
82
93
  end
83
94
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RobinCMS
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.5"
5
5
  end
@@ -9,7 +9,7 @@
9
9
  <button type="submit">Close</button>
10
10
  </form>
11
11
  <form
12
- action='<%= url("/libraries/#{@library[:id]}/item/delete?id=#{@item.id}") %>'
12
+ action='<%= url("/libraries/#{@library.kind}/item/#{@item.id}/delete") %>'
13
13
  method="post"
14
14
  >
15
15
  <button type="submit" class="--danger">Delete</button>
@@ -33,5 +33,5 @@
33
33
  placeholder="Search <%= @library[:label].downcase %>..."
34
34
  />
35
35
  <button type="submit">Apply</button>
36
- <a href="/admin/libraries/<%= @library[:id] %>">Clear filters</a>
36
+ <a href="/admin/libraries/<%= @library.kind %>">Clear filters</a>
37
37
  </form>
@@ -1,4 +1,7 @@
1
1
  <label for="<%= safe_id('image', 'field') %>"><%= field[:label] %></label>
2
+ <% if field[:description] %>
3
+ <small><%= field[:description] %></small>
4
+ <% end %>
2
5
  <%#
3
6
  There is a bug here where if you upload a different image with the
4
7
  same name, it shows the old image due to caching. This is a very
@@ -1,4 +1,7 @@
1
1
  <label for="<%= safe_id(field[:id], 'field') %>"><%= field[:label] %></label>
2
+ <% if field[:description] %>
3
+ <small><%= field[:description] %></small>
4
+ <% end %>
2
5
  <input
3
6
  id="<%= safe_id(field[:id], 'field') %>"
4
7
  type="<%= field[:type] %>"
@@ -6,7 +6,7 @@
6
6
  <p><%= @library[:description] %></p>
7
7
  <% end %>
8
8
  <% if @library[:can_create] %>
9
- <form action='<%= url("/libraries/#{@library[:id]}/item") %>'>
9
+ <form action='<%= url("/libraries/#{@library.kind}/item") %>'>
10
10
  <button type="submit">
11
11
  New <%= @library[:label_singular].downcase %>
12
12
  </button>
@@ -20,8 +20,8 @@
20
20
  <br />
21
21
  <br />
22
22
  <% if @library[:can_create] %>
23
- Create a <a href='<%= url("/libraries/#{@library[:id]}/item") %>'>new <%= @library[:label_singular].downcase %></a>
24
- or try <a href='<%= url("/libraries/#{@library[:id]}") %>'>clearing</a> your search filters.
23
+ Create a <a href='<%= url("/libraries/#{@library.kind}/item") %>'>new <%= @library[:label_singular].downcase %></a>
24
+ or try <a href='<%= url("/libraries/#{@library.kind}") %>'>clearing</a> your search filters.
25
25
  <% end %>
26
26
  </div>
27
27
  <% else %>
@@ -40,13 +40,13 @@
40
40
  <% for item in @items %>
41
41
  <tr>
42
42
  <td>
43
- <a href='<%= url("/libraries/#{@library[:id]}/item?id=#{item.id}") %>'>
43
+ <a href='<%= url("/libraries/#{@library.kind}/item/#{item.id}") %>'>
44
44
  <%= item.display_name %>
45
45
  </a>
46
46
  </td>
47
47
  <% if @library.drafts_enabled? %>
48
48
  <td>
49
- <a href='<%= url("/libraries/#{@library[:id]}/item?id=#{item.id}") %>'>
49
+ <a href='<%= url("/libraries/#{@library.kind}/item/#{item.id}") %>'>
50
50
  <span class="badge --<%= item.published? ? 'published' : 'draft' %>">
51
51
  <%= item.published_label %>
52
52
  </span>
@@ -54,12 +54,12 @@
54
54
  </td>
55
55
  <% end %>
56
56
  <td>
57
- <a href='<%= url("/libraries/#{@library[:id]}/item?id=#{item.id}") %>'>
57
+ <a href='<%= url("/libraries/#{@library.kind}/item/#{item.id}") %>'>
58
58
  <%= item.created_at.strftime("%b %d, %Y") %>
59
59
  </a>
60
60
  </td>
61
61
  <td>
62
- <a href='<%= url("/libraries/#{@library[:id]}/item?id=#{item.id}") %>'>
62
+ <a href='<%= url("/libraries/#{@library.kind}/item/#{item.id}") %>'>
63
63
  <%= item.updated_at.strftime("%b %d, %Y") %>
64
64
  </a>
65
65
  </td>
@@ -1,5 +1,5 @@
1
1
  <span class="controls">
2
- <a href='<%= url("/libraries/#{@library[:id]}") %>'>Back</a>
2
+ <a href='<%= url("/libraries/#{@library.kind}") %>'>Back</a>
3
3
  <button type="submit">Save</button>
4
4
  <% if has_delete %>
5
5
  <%= erb :delete_dialog %>
@@ -2,14 +2,14 @@
2
2
  <h2>
3
3
  <% if @item.id %>Edit<% else %>New<% end %> <%= @library[:label_singular].downcase %>
4
4
  </h2>
5
- <%= erb :library_actions, locals: { has_delete: @library[:can_delete] && @item.id } %>
5
+ <%= erb :library_actions, locals: { has_delete: @item.can_delete? } %>
6
6
  </header>
7
7
  <form
8
8
  class="card"
9
9
  <% if @item.id %>
10
- action='<%= url("/libraries/#{@library[:id]}/item?id=#{@item.id}") %>'
10
+ action='<%= url("/libraries/#{@library.kind}/item/#{@item.id}") %>'
11
11
  <% else %>
12
- action='<%= url("/libraries/#{@library[:id]}/item") %>'
12
+ action='<%= url("/libraries/#{@library.kind}/item") %>'
13
13
  <% end %>
14
14
  method="post"
15
15
  enctype="multipart/form-data"
@@ -1,6 +1,9 @@
1
1
  <link rel="stylesheet" type="text/css" href="https://unpkg.com/trix@2.0.8/dist/trix.css">
2
2
  <script type="text/javascript" src="https://unpkg.com/trix@2.0.8/dist/trix.umd.min.js"></script>
3
3
  <label for="<%= safe_id(field[:id], 'field') %>"><%= field[:label] %></label>
4
+ <% if field[:description] %>
5
+ <small><%= field[:description] %></small>
6
+ <% end %>
4
7
  <input
5
8
  id="richtext-content"
6
9
  type="hidden"
@@ -1,4 +1,7 @@
1
1
  <label for="<%= safe_id(field[:id], 'field') %>"><%= field[:label] %></label>
2
+ <% if field[:description] %>
3
+ <small><%= field[:description] %></small>
4
+ <% end %>
2
5
  <select
3
6
  id="<%= safe_id(field[:id], 'field') %>"
4
7
  name="<%= field[:id] %>"
@@ -3,6 +3,7 @@
3
3
  --border-radius: 8px;
4
4
  --bg-color: rgb(246, 246, 247);
5
5
  --font-color: #141414;
6
+ --font-color-light: #44474a;
6
7
  --link-color: rgb(71, 95, 145);
7
8
  --accent-color: <%= @config[:accent_color] %>;
8
9
  --accent-color-light: <%= @config[:accent_color] %>1e;
@@ -24,10 +25,6 @@
24
25
  --box-shadow-input: inset rgba(0, 0, 0, 0.1) 0px 1px 0px 0px;
25
26
  }
26
27
 
27
- test {
28
- te
29
- }
30
-
31
28
  @media only screen and (max-width: 1250px) {
32
29
  :root {
33
30
  --content-left-margin: 3rem;
@@ -153,7 +150,10 @@ tbody tr:hover td {
153
150
 
154
151
  label {
155
152
  display: block;
156
- margin-bottom: var(--padding-xs);
153
+ }
154
+
155
+ small {
156
+ color: var(--font-color-light);
157
157
  }
158
158
 
159
159
  input {
@@ -190,6 +190,13 @@ select {
190
190
  box-shadow: var(--box-shadow-input);
191
191
  }
192
192
 
193
+ input,
194
+ select,
195
+ trix-toolbar {
196
+ display: block;
197
+ margin-top: var(--padding-xs);
198
+ }
199
+
193
200
  button {
194
201
  padding: var(--padding-xxs) var(--padding-xs);
195
202
  color: var(--accent-color);
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: robin_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aron Lebani
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-09-22 00:00:00.000000000 Z
11
+ date: 2025-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt
@@ -66,7 +66,7 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '2.2'
69
- description:
69
+ description: Robin CMS is a simple, flat-file CMS for Static Site Generators.
70
70
  email:
71
71
  - aron@lebani.dev
72
72
  executables: []
@@ -111,11 +111,11 @@ files:
111
111
  - lib/robin_cms/views/richtext_field.erb
112
112
  - lib/robin_cms/views/select_field.erb
113
113
  - lib/robin_cms/views/stylesheet.css.erb
114
- homepage: https://codeberg.org/evencuriouser/robin_cms
114
+ homepage: https://robincms.org
115
115
  licenses:
116
116
  - MIT
117
117
  metadata:
118
- homepage_uri: https://codeberg.org/evencuriouser/robin_cms
118
+ homepage_uri: https://robincms.org
119
119
  source_code_uri: https://codeberg.org/evencuriouser/robin_cms
120
120
  post_install_message:
121
121
  rdoc_options: []
@@ -135,5 +135,5 @@ requirements: []
135
135
  rubygems_version: 3.5.16
136
136
  signing_key:
137
137
  specification_version: 4
138
- summary: A minimalist, headless, rack-based flat-file CMS.
138
+ summary: A simple, flat-file CMS for Static Site Generators.
139
139
  test_files: []