i18n_proofreading 0.10.3 → 0.10.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: c234f2ba39e1d73f3ed201be05d4cce85a6550977b956943158fe353c9f13b50
4
- data.tar.gz: 72a56aba394043a0300cb1500c877b9dd0b2eb6e33de2085b83948cdc5a1d9a8
3
+ metadata.gz: 92f93f3b80d87b86e0ec3b0ce33dc7c89cdd5ba9c2e7fc8b40bcb26b92a1a224
4
+ data.tar.gz: f994d6b60277c364a23eb299e344ab0af2f1fcdaa567966d07bd97352ac7f2fe
5
5
  SHA512:
6
- metadata.gz: 5c520423553945e1c7e00699a143e37c1223658c11dfb9403c2998d67d1e0ca4ff7a6f9f874157aa2c2829ba62f681c89a11920ae0ef827dac20a160e0e32a71
7
- data.tar.gz: 007dceabf146ee93b69a2e3940a16b6d47beb68fab0db80b5a51fc35a325e776ab4544c6e96b104fe4158e7b67845c296776a027e7a42935eafa6d01973951b3
6
+ metadata.gz: d99a83e338ef612aa9a519eb115cb55f55a35978c514171070848e4dca9ea470dbb9055d29f04e5b950107611181a8719d8eff9d976272d7f4c67f8694186ff3
7
+ data.tar.gz: 3a296e3305dcce5f55b22d01843f5a885a34d805f30f4dcfa794a08138948f06125a438d550c03e142409d1a3e60e6ebf32259dd470f930b42c945b0e892340b
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.10.5]
6
+
7
+ - Added `config.admin_layout`, letting host apps render the proofreading
8
+ dashboard inside their own admin layout while keeping the standalone gem
9
+ layout as the default.
10
+
11
+ ## [0.10.4]
12
+
13
+ - Redesigned the proofreading admin dashboard into a two-column review layout
14
+ with status filters, a selected-suggestion pane, and refreshed before/after
15
+ screenshots.
16
+ - Added independent suggestion show pages while keeping long detail content
17
+ scrollable on narrow screens.
18
+
5
19
  ## [0.10.3]
6
20
 
7
21
  - Added `mount_i18n_proofreading at: "/i18n_proofreading"` as the install-time
@@ -6,6 +6,10 @@ module I18nProofreading
6
6
 
7
7
  private
8
8
 
9
+ def i18n_proofreading_admin_layout
10
+ I18nProofreading.config.admin_layout
11
+ end
12
+
9
13
  # Gate for the widget's public API (submit a suggestion, read prior context).
10
14
  # The client can set the cookie, but it can never reach the endpoints unless
11
15
  # the app itself says the tool is available for this request.
@@ -6,9 +6,10 @@ module I18nProofreading
6
6
 
7
7
  # Public widget API is available-gated; the read-only dashboard is admin-gated.
8
8
  before_action :require_available, only: %i[context create]
9
- before_action :require_admin, only: :index
9
+ before_action :require_admin, only: %i[index show]
10
+ before_action :set_suggestion, only: :show
10
11
 
11
- layout 'i18n_proofreading/application', only: :index
12
+ layout :i18n_proofreading_admin_layout, only: %i[index show]
12
13
 
13
14
  # Throttle the public submission endpoint per IP so one user or bot can't
14
15
  # flood the table. Uses the rate limiter built into Rails 7.2+ (backed by
@@ -37,8 +38,12 @@ module I18nProofreading
37
38
  rows = scope.newest_first.offset((@page - 1) * PER_PAGE).limit(PER_PAGE + 1).to_a
38
39
  @more = rows.size > PER_PAGE
39
40
  @suggestions = rows.first(PER_PAGE)
41
+
42
+ @selected_suggestion = Suggestion.find_by(id: params[:suggestion_id]) if params[:suggestion_id].present?
40
43
  end
41
44
 
45
+ def show; end
46
+
42
47
  # --- widget API (public) -------------------------------------------------
43
48
 
44
49
  # Pending suggestions for one key/locale, shown as read-only context when the
@@ -67,6 +72,10 @@ module I18nProofreading
67
72
 
68
73
  private
69
74
 
75
+ def set_suggestion
76
+ @suggestion = Suggestion.find(params[:id])
77
+ end
78
+
70
79
  def attribute_author(suggestion)
71
80
  author = current_author
72
81
  return unless author
@@ -0,0 +1,42 @@
1
+ <section class="suggestion-panel">
2
+ <div class="panel-head">
3
+ <h1>
4
+ <code class="key"><%= suggestion.translation_key %></code>
5
+ <span class="badge locale"><%= suggestion.locale %></span>
6
+ <span class="badge status-<%= suggestion.status %>">
7
+ <%= t("i18n_proofreading.statuses.#{suggestion.status}", default: suggestion.status.humanize) %>
8
+ </span>
9
+ <span class="case-id">#<%= suggestion.id %></span>
10
+ </h1>
11
+ </div>
12
+
13
+ <div class="detail-scroll">
14
+ <article class="card pad">
15
+ <dl class="meta-list">
16
+ <% if suggestion.author_label.present? || suggestion.author_id.present? %>
17
+ <dt><%= t('i18n_proofreading.dashboard.from', default: 'From') %></dt>
18
+ <dd><%= suggestion.author_label.presence || "##{suggestion.author_id}" %></dd>
19
+ <% end %>
20
+ <% if suggestion.page_url.present? %>
21
+ <dt><%= t('i18n_proofreading.dashboard.page', default: 'Page') %></dt>
22
+ <dd><%= link_to suggestion.page_url, suggestion.page_url, target: '_blank', rel: 'noopener' %></dd>
23
+ <% end %>
24
+ <dt><%= t('i18n_proofreading.dashboard.received', default: 'Received') %></dt>
25
+ <dd><%= suggestion.created_at %></dd>
26
+ </dl>
27
+ </article>
28
+
29
+ <article class="card pad">
30
+ <dl class="diff">
31
+ <dt><%= t('i18n_proofreading.dashboard.current', default: 'Current') %></dt>
32
+ <dd><span class="old" dir="auto"><%= suggestion.old_value.presence || '—' %></span></dd>
33
+ <dt><%= t('i18n_proofreading.dashboard.suggested', default: 'Suggested') %></dt>
34
+ <dd><span class="new" dir="auto"><%= suggestion.proposed_value %></span></dd>
35
+ </dl>
36
+
37
+ <% if suggestion.comment.present? %>
38
+ <div class="comment" dir="auto"><%= suggestion.comment %></div>
39
+ <% end %>
40
+ </article>
41
+ </div>
42
+ </section>
@@ -1,77 +1,99 @@
1
+ <% content_for :body_class, 'ip-index' %>
2
+
1
3
  <header class="page">
2
4
  <h1><%= t('i18n_proofreading.dashboard.title', default: 'I18nProofreading') %></h1>
3
5
  <p class="lede"><%= t('i18n_proofreading.dashboard.subtitle', default: 'Review what reviewers proposed, then apply it to your locale files.') %></p>
4
6
  </header>
5
7
 
6
- <nav class="tabs">
7
- <% I18nProofreading::Suggestion::STATUSES.each do |status| %>
8
- <%= link_to suggestions_path(status: status, locale: @locale),
9
- class: ('active' if status == @status) do %>
10
- <%= t("i18n_proofreading.statuses.#{status}", default: status.humanize) %>
11
- <span class="count"><%= @counts.fetch(status, 0) %></span>
12
- <% end %>
13
- <% end %>
14
- </nav>
15
-
16
- <% if @locales.many? %>
17
- <form class="filters" method="get">
18
- <input type="hidden" name="status" value="<%= @status %>">
19
- <label for="i18np-locale"><%= t('i18n_proofreading.dashboard.locale', default: 'Locale') %></label>
20
- <select id="i18np-locale" name="locale" onchange="this.form.submit()">
21
- <option value=""><%= t('i18n_proofreading.dashboard.all_locales', default: 'All locales') %></option>
22
- <% @locales.each do |locale| %>
23
- <option value="<%= locale %>" <%= 'selected' if locale == @locale %>><%= locale %></option>
8
+ <div class="review-shell <%= 'has-selected' if @selected_suggestion %>">
9
+ <aside class="review-sidebar" aria-label="<%= t('i18n_proofreading.dashboard.suggestions', default: 'Suggestions') %>">
10
+ <nav class="tabs">
11
+ <% I18nProofreading::Suggestion::STATUSES.each do |status| %>
12
+ <%= link_to suggestions_path(status: status, locale: @locale),
13
+ class: ('active' if status == @status) do %>
14
+ <%= t("i18n_proofreading.statuses.#{status}", default: status.humanize) %>
15
+ <span class="count"><%= @counts.fetch(status, 0) %></span>
16
+ <% end %>
24
17
  <% end %>
25
- </select>
26
- <%# Inline onchange auto-submits where allowed; this button is the reliable
27
- path where a strict CSP blocks inline handlers, or with JS off. %>
28
- <button><%= t('i18n_proofreading.dashboard.filter', default: 'Filter') %></button>
29
- </form>
30
- <% end %>
18
+ </nav>
31
19
 
32
- <% if @suggestions.any? %>
33
- <% @suggestions.each do |suggestion| %>
34
- <article class="card">
35
- <div class="row-top">
36
- <code class="key"><%= suggestion.translation_key %></code>
37
- <span class="badge locale"><%= suggestion.locale %></span>
38
- <span class="badge status-<%= suggestion.status %>">
39
- <%= t("i18n_proofreading.statuses.#{suggestion.status}", default: suggestion.status.humanize) %>
40
- </span>
41
- <span class="spacer"></span>
42
- <span class="meta">
43
- <% if suggestion.author_label.present? %><%= suggestion.author_label %> · <% end %>
44
- <span title="<%= suggestion.created_at %>"><%= time_ago_in_words(suggestion.created_at) %> <%= t('i18n_proofreading.dashboard.ago', default: 'ago') %></span>
45
- </span>
46
- </div>
47
-
48
- <dl class="diff">
49
- <dt><%= t('i18n_proofreading.dashboard.current', default: 'Current') %></dt>
50
- <dd><span class="old" dir="auto"><%= suggestion.old_value.presence || '—' %></span></dd>
51
- <dt><%= t('i18n_proofreading.dashboard.suggested', default: 'Suggested') %></dt>
52
- <dd><span class="new" dir="auto"><%= suggestion.proposed_value %></span></dd>
53
- </dl>
20
+ <% if @locales.many? %>
21
+ <form class="filters" method="get">
22
+ <input type="hidden" name="status" value="<%= @status %>">
23
+ <label for="i18np-locale"><%= t('i18n_proofreading.dashboard.locale', default: 'Locale') %></label>
24
+ <select id="i18np-locale" name="locale" onchange="this.form.submit()">
25
+ <option value=""><%= t('i18n_proofreading.dashboard.all_locales', default: 'All locales') %></option>
26
+ <% @locales.each do |locale| %>
27
+ <option value="<%= locale %>" <%= 'selected' if locale == @locale %>><%= locale %></option>
28
+ <% end %>
29
+ </select>
30
+ <%# Inline onchange auto-submits where allowed; this button is the reliable
31
+ path where a strict CSP blocks inline handlers, or with JS off. %>
32
+ <button><%= t('i18n_proofreading.dashboard.filter', default: 'Filter') %></button>
33
+ </form>
34
+ <% end %>
54
35
 
55
- <% if suggestion.comment.present? %>
56
- <div class="comment" dir="auto"><%= suggestion.comment %></div>
36
+ <div class="suggestion-list">
37
+ <% if @suggestions.any? %>
38
+ <% @suggestions.each do |suggestion| %>
39
+ <%= link_to suggestions_path(status: @status, locale: @locale, page: @page,
40
+ suggestion_id: suggestion.id),
41
+ class: ['suggestion-row', ('active' if @selected_suggestion&.id == suggestion.id)].compact do %>
42
+ <span class="suggestion-main">
43
+ <span class="suggestion-topline">
44
+ <code class="key"><%= suggestion.translation_key %></code>
45
+ <span class="badge locale"><%= suggestion.locale %></span>
46
+ <span class="muted suggestion-time" title="<%= suggestion.created_at %>">
47
+ <%= time_ago_in_words(suggestion.created_at) %> <%= t('i18n_proofreading.dashboard.ago', default: 'ago') %>
48
+ </span>
49
+ </span>
50
+ <span class="suggestion-copy" dir="auto"><%= truncate(suggestion.proposed_value, length: 110) %></span>
51
+ <span class="muted suggestion-meta">
52
+ <span>#<%= suggestion.id %></span>
53
+ <% if suggestion.author_label.present? %>
54
+ <span><%= suggestion.author_label %></span>
55
+ <% end %>
56
+ </span>
57
+ </span>
58
+ <span class="badge status-<%= suggestion.status %>">
59
+ <%= t("i18n_proofreading.statuses.#{suggestion.status}", default: suggestion.status.humanize) %>
60
+ </span>
61
+ <% end %>
62
+ <% end %>
63
+ <% else %>
64
+ <div class="empty"><%= t('i18n_proofreading.dashboard.empty', default: 'Nothing here yet.') %></div>
57
65
  <% end %>
58
- </article>
59
- <% end %>
66
+ </div>
60
67
 
61
- <nav class="pager">
62
- <span>
63
- <% if @page > 1 %>
64
- <%= link_to t('i18n_proofreading.dashboard.newer', default: '← Newer'),
65
- suggestions_path(status: @status, locale: @locale, page: @page - 1) %>
66
- <% end %>
67
- </span>
68
- <span>
69
- <% if @more %>
70
- <%= link_to t('i18n_proofreading.dashboard.older', default: 'Older →'),
71
- suggestions_path(status: @status, locale: @locale, page: @page + 1) %>
72
- <% end %>
73
- </span>
74
- </nav>
75
- <% else %>
76
- <div class="empty"><%= t('i18n_proofreading.dashboard.empty', default: 'Nothing here yet.') %></div>
77
- <% end %>
68
+ <nav class="pager">
69
+ <span>
70
+ <% if @page > 1 %>
71
+ <%= link_to t('i18n_proofreading.dashboard.newer', default: '← Newer'),
72
+ suggestions_path(status: @status, locale: @locale, page: @page - 1) %>
73
+ <% end %>
74
+ </span>
75
+ <span>
76
+ <% if @more %>
77
+ <%= link_to t('i18n_proofreading.dashboard.older', default: 'Older →'),
78
+ suggestions_path(status: @status, locale: @locale, page: @page + 1) %>
79
+ <% end %>
80
+ </span>
81
+ </nav>
82
+ </aside>
83
+
84
+ <main class="review-detail" aria-label="<%= t('i18n_proofreading.dashboard.current_suggestion', default: 'Current suggestion') %>">
85
+ <% if @selected_suggestion %>
86
+ <p class="mobile-back">
87
+ <%= link_to t('i18n_proofreading.dashboard.back', default: '← All suggestions'),
88
+ suggestions_path(status: @status, locale: @locale, page: @page) %>
89
+ </p>
90
+ <%= render 'suggestion_panel', suggestion: @selected_suggestion %>
91
+ <% else %>
92
+ <div class="empty-state">
93
+ <h2><%= t('i18n_proofreading.dashboard.select_suggestion', default: 'Select a suggestion') %></h2>
94
+ <p class="muted"><%= t('i18n_proofreading.dashboard.select_suggestion_hint',
95
+ default: 'Open a row to review the proposed wording beside the queue.') %></p>
96
+ </div>
97
+ <% end %>
98
+ </main>
99
+ </div>
@@ -0,0 +1,8 @@
1
+ <% content_for :body_class, 'ip-show' %>
2
+
3
+ <p class="breadcrumb">
4
+ <%= link_to t('i18n_proofreading.dashboard.title', default: 'I18nProofreading'), suggestions_path %>
5
+ / #<%= @suggestion.id %>
6
+ </p>
7
+
8
+ <%= render 'suggestion_panel', suggestion: @suggestion %>
@@ -9,7 +9,7 @@
9
9
  <%= stylesheet_link_tag "#{dashboard_stylesheet_path}?v=#{I18nProofreading::Widget.dashboard_stylesheet_fingerprint}",
10
10
  'data-turbo-track': 'reload' %>
11
11
  </head>
12
- <body>
12
+ <body class="<%= content_for?(:body_class) ? yield(:body_class) : '' %>">
13
13
  <div class="container">
14
14
  <%= yield %>
15
15
  </div>
data/config/routes.rb CHANGED
@@ -10,7 +10,7 @@ I18nProofreading::Engine.routes.draw do
10
10
  # route are the widget's public API (see SuggestionsController). There is
11
11
  # deliberately no update/destroy — the tool never mutates suggestions from the
12
12
  # UI, since it can't write to the host's locale files.
13
- resources :suggestions, only: %i[index create] do
13
+ resources :suggestions, only: %i[index show create] do
14
14
  get :context, on: :collection
15
15
  end
16
16
 
@@ -16,6 +16,11 @@ I18nProofreading.configure do |config|
16
16
  # development only; wire it to your own admin check to open it elsewhere.
17
17
  #
18
18
  # config.authorize_admin = ->(request) { request.env["warden"]&.user&.admin? }
19
+ #
20
+ # Render the dashboard inside your app's admin layout. Default: the gem's
21
+ # standalone dashboard layout.
22
+ #
23
+ # config.admin_layout = "admin/application"
19
24
 
20
25
  # Attribute a suggestion to a user (optional). Return an object responding to
21
26
  # #id (ideally #email too), or nil. Receives the Rack::Request. You resolve the
@@ -21,6 +21,10 @@ module I18nProofreading
21
21
  # the dashboard until you wire it to your own admin check.
22
22
  attr_accessor :authorize_admin
23
23
 
24
+ # Layout used by the triage dashboard. Override this to render it inside
25
+ # your app's admin shell, e.g. "admin/application".
26
+ attr_accessor :admin_layout
27
+
24
28
  # Resolve the current user for attribution (optional). Return an object
25
29
  # responding to #id, or nil. Receives the Rack::Request.
26
30
  attr_accessor :current_user
@@ -68,6 +72,7 @@ module I18nProofreading
68
72
  @enabled_environments = %w[development staging]
69
73
  @enabled = ->(_request) { true }
70
74
  @authorize_admin = ->(_request) { Rails.env.development? }
75
+ @admin_layout = 'i18n_proofreading/application'
71
76
  @current_user = ->(_request) {}
72
77
  @author_label = ->(user) { user.respond_to?(:email) ? user.email : user&.to_s }
73
78
  @available_locales = -> { I18n.available_locales.map(&:to_s) }
@@ -45,6 +45,7 @@
45
45
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
46
46
  padding: 14px 16px; margin-bottom: 12px;
47
47
  }
48
+ .card.pad { margin: 0; }
48
49
  .row-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
49
50
  code.key {
50
51
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
@@ -78,3 +79,83 @@
78
79
  .empty { padding: 48px 16px; text-align: center; color: var(--muted); border: 1px dashed var(--border); border-radius: 12px; }
79
80
  .pager { margin-top: 16px; display: flex; justify-content: space-between; }
80
81
  .pager .muted { color: var(--muted); }
82
+ .breadcrumb { margin: 0 0 12px; font-size: 13px; }
83
+ .case-id { color: var(--muted); font-weight: 400; font-size: 15px; }
84
+ .panel-head { flex: 0 0 auto; }
85
+ .panel-head h1 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
86
+ .detail-scroll { min-height: 0; display: flex; flex-direction: column; gap: 12px; }
87
+ .suggestion-panel { min-width: 0; display: flex; flex-direction: column; }
88
+ .meta-list { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; margin: 0; }
89
+ .meta-list dt { color: var(--muted); font-size: 13px; }
90
+ .meta-list dd { margin: 0; overflow-wrap: anywhere; }
91
+
92
+ /* Desktop review: queue on the left, selected suggestion on the right. */
93
+ .ip-index, .ip-index .container { height: 100vh; height: 100dvh; }
94
+ .ip-index { overflow: hidden; }
95
+ .ip-index .container { max-width: 1280px; display: flex; flex-direction: column; padding-bottom: 16px; }
96
+ .ip-index header.page { flex: 0 0 auto; margin-bottom: 14px; }
97
+ .review-shell { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: minmax(330px, 430px) 1fr;
98
+ gap: 16px; }
99
+ .review-sidebar { min-width: 0; min-height: 0; display: flex; flex-direction: column;
100
+ background: var(--surface); border: 1px solid var(--border);
101
+ border-radius: 8px; overflow: hidden; }
102
+ .review-sidebar .tabs { flex: 0 0 auto; margin: 12px 12px 0; padding: 0; background: transparent;
103
+ border: 0; border-radius: 0; gap: 6px; flex-wrap: nowrap; }
104
+ .review-sidebar .tabs a { flex: 1 1 0; position: relative; display: flex; align-items: center;
105
+ justify-content: center; gap: 6px; min-height: 34px; padding: 5px 10px 8px;
106
+ border-radius: 8px; color: var(--muted); font-weight: 600; }
107
+ .review-sidebar .tabs a.active { color: var(--text); border: 0; background: transparent; box-shadow: none; margin: 0; }
108
+ .review-sidebar .tabs a.active::after { content: ""; position: absolute; left: 20px; right: 20px; bottom: 0;
109
+ height: 2px; border-radius: 999px; background: var(--accent); }
110
+ .review-sidebar .tabs a:not(.active):hover { text-decoration: none; color: var(--text); background: var(--bg); }
111
+ .review-sidebar .tabs .count { margin-left: 0; background: color-mix(in srgb, var(--muted) 14%, transparent); }
112
+ .review-sidebar .tabs a.active .count { background: var(--border); }
113
+ .review-sidebar .filters { flex: 0 0 auto; margin: 0; padding: 12px; border-bottom: 1px solid var(--border); }
114
+ .suggestion-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
115
+ .suggestion-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; padding: 13px 14px;
116
+ color: var(--text); border-bottom: 1px solid var(--border); }
117
+ .suggestion-row > .badge { align-self: start; }
118
+ .suggestion-row:hover { text-decoration: none; background: var(--bg); }
119
+ .suggestion-row.active { background: color-mix(in srgb, var(--accent) 9%, var(--surface));
120
+ box-shadow: inset 3px 0 0 var(--accent); }
121
+ .suggestion-main { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
122
+ .suggestion-topline, .suggestion-meta { display: flex; align-items: center; gap: 8px; min-width: 0; }
123
+ .suggestion-topline .key { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
124
+ .suggestion-copy, .suggestion-meta span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
125
+ .suggestion-copy { display: block; font-weight: 600; }
126
+ .suggestion-time { margin-left: auto; white-space: nowrap; }
127
+ .review-sidebar .pager { flex: 0 0 auto; margin: 0; padding: 10px 12px; border-top: 1px solid var(--border); }
128
+ .review-detail { min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
129
+ .review-detail .suggestion-panel { flex: 1 1 auto; min-height: 0; }
130
+ .review-detail .detail-scroll { flex: 1 1 auto; overflow-y: auto; padding-right: 2px; }
131
+ .empty-state { margin: auto; max-width: 320px; padding: 24px; text-align: center; }
132
+ .empty-state h2 { margin: 0 0 6px; font-size: 18px; }
133
+ .empty-state p { margin: 0; }
134
+ .mobile-back { display: none; }
135
+
136
+ /* Standalone show pages keep normal page scrolling. */
137
+ .ip-show { min-height: 100vh; overflow: auto; }
138
+ .ip-show .detail-scroll { overflow: visible; }
139
+ @media (max-width: 760px) {
140
+ body { font-size: 14px; }
141
+ .container { padding: 14px 10px 24px; }
142
+ .ip-index, .ip-show { overflow-x: hidden; }
143
+ .review-detail, .suggestion-panel, .detail-scroll, .card, dl, dd { min-width: 0; max-width: 100%; }
144
+ .suggestion-panel .card { width: calc(100vw - 20px); }
145
+ .lede, dd, .comment { max-width: calc(100vw - 54px); white-space: normal; overflow-wrap: anywhere; }
146
+ .ip-index { overflow-y: auto; overflow-x: hidden; }
147
+ .ip-index, .ip-index .container { min-height: 100vh; height: auto; }
148
+ .ip-index .container { padding-bottom: 10px; }
149
+ .review-shell { display: block; min-height: calc(100vh - 76px); }
150
+ .review-sidebar, .review-detail { min-height: calc(100vh - 76px); }
151
+ .review-shell.has-selected .review-sidebar { display: none; }
152
+ .review-shell:not(.has-selected) .review-detail { display: none; }
153
+ .suggestion-list { overflow: visible; }
154
+ .suggestion-row { padding: 12px; }
155
+ .suggestion-meta { display: none; }
156
+ .review-detail .suggestion-panel { min-height: calc(100vh - 64px); }
157
+ .mobile-back { display: block; flex: 0 0 auto; margin: 10px 10px 0; font-size: 13px; }
158
+ .panel-head h1 { font-size: 18px; }
159
+ .panel-head .key { min-width: 0; white-space: normal; overflow-wrap: anywhere; }
160
+ dl.diff, .meta-list { grid-template-columns: 1fr; gap: 3px; }
161
+ }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18nProofreading
4
- VERSION = '0.10.3'
4
+ VERSION = '0.10.5'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n_proofreading
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.3
4
+ version: 0.10.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov
@@ -44,7 +44,9 @@ files:
44
44
  - app/helpers/i18n_proofreading/tag_helper.rb
45
45
  - app/models/i18n_proofreading/application_record.rb
46
46
  - app/models/i18n_proofreading/suggestion.rb
47
+ - app/views/i18n_proofreading/suggestions/_suggestion_panel.html.erb
47
48
  - app/views/i18n_proofreading/suggestions/index.html.erb
49
+ - app/views/i18n_proofreading/suggestions/show.html.erb
48
50
  - app/views/layouts/i18n_proofreading/application.html.erb
49
51
  - config/locales/i18n_proofreading.ar.yml
50
52
  - config/locales/i18n_proofreading.bg.yml