studio-engine 0.22.0 → 0.24.0

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.
@@ -1,13 +1,52 @@
1
1
  <%# Tasks section of the living style guide (admin/style).
2
2
 
3
- A DEMONSTRATOR of the shared board primitive the columns, cards, stage
4
- badges, and drag/rank affordance that every board (McRitchie Studio's
5
- /deployments is the top-of-line) builds from. It is a static sketch, not a
6
- backend-wired sortable board; the point is to show the standard so custom
7
- boards can adopt it. The stage-* badge palette (components/_badge) is the
8
- spine. %>
3
+ The shared board primitive, LIVE. Phase D lifted the columns / cards / drag +
4
+ optimistic move / reorder that the three near-identical McRitchie Studio boards
5
+ (tasks / news / content) each hand-rolled into one engine primitive
6
+ (studio/board/_board + the window.studioBoard factory + vendored SortableJS).
7
+ The first block below renders that real primitive with `demo: true` — you can
8
+ DRAG a card between columns and the move animates + toasts, but nothing POSTs.
9
+ The blocks under it stay a static reference: the stage-* badge palette
10
+ (components/_badge) that is the pipeline spine, and the standard-vs-custom
11
+ split of what a board inherits vs sets. %>
9
12
  <%
10
- # The shared pipeline palette: each column maps to a stage-* badge scheme.
13
+ # LIVE specimen the real studio/board/_board primitive, demo mode. Plain Hash
14
+ # records (the style guide has no models); each card id becomes id="card-<id>".
15
+ # Each demo card carries its current `zone` (the column it sits in) — in a real
16
+ # board the record knows its own stage; here the Hash does.
17
+ live_board_columns = [
18
+ { key: "designed", label: "Designed", badge_class: "stage-fresh",
19
+ cards: [
20
+ { id: "engine-board-primitive", zone: "designed", title: "Engine board primitive", repo: "studio-engine", who: "SH" },
21
+ { id: "coinflow-buy-rail", zone: "designed", title: "Coinflow buy rail", repo: "turf-monster", who: "JA" }
22
+ ] },
23
+ { key: "building", label: "Building", badge_class: "stage-shaping",
24
+ cards: [{ id: "navbar-rpc-preload", zone: "building", title: "Navbar RPC preload", repo: "turf-monster", who: "CA" }] },
25
+ { key: "submitted", label: "Submitted", badge_class: "stage-structured",
26
+ cards: [{ id: "magic-link-ttl-bump", zone: "submitted", title: "Magic-link TTL bump", repo: "studio-engine", who: "SH" }] },
27
+ { key: "shipped", label: "Shipped", badge_class: "stage-shipped",
28
+ cards: [{ id: "ses-email-backend", zone: "shipped", title: "SES email backend", repo: "mcritchie", who: "ST" }] }
29
+ ]
30
+
31
+ # Copy-paste-for-an-agent usage. Built by array-join, NOT a heredoc: a heredoc as
32
+ # the last expression of an ERB scriptlet trips Erubi, and literal ERB tags in the
33
+ # body would close the tag. Plain render() calls read clearly for an agent.
34
+ board_usage_snippet = [
35
+ "# once at layout level (a <script> in the board template never runs):",
36
+ 'render "studio/board_assets"',
37
+ "",
38
+ "# then the board itself:",
39
+ 'render "studio/board/board",',
40
+ " columns: @columns, # [{ key:, label:, cards:, badge_class: }]",
41
+ ' card_partial: "tasks/task_card",',
42
+ " reorder_url: tasks_reorder_path, # POST { slugs: [...], zone: }",
43
+ ' move_url: "/tasks/:id.json", # PATCH { task: { stage: } }',
44
+ ' move_param: { resource: "task", attr: "stage" },',
45
+ ' live_channel: "deployments"'
46
+ ].join("\n")
47
+
48
+ # The static reference palette below (unchanged): each column maps to a stage-*
49
+ # badge scheme.
11
50
  board_columns = [
12
51
  { key: "stage-fresh", label: "Fresh", count: 2 },
13
52
  { key: "stage-shaping", label: "Shaping", count: 1 },
@@ -32,13 +71,45 @@
32
71
  <h2 class="text-2xl font-bold text-heading">Tasks</h2>
33
72
  <p class="text-muted text-sm max-w-2xl">
34
73
  The shared board primitive that every board builds from &mdash; columns, cards, the
35
- <code class="font-mono text-2xs">stage-*</code> badge palette, and the drag / rank affordance.
36
- McRitchie Studio's <code class="font-mono text-2xs">/deployments</code> board is the top-of-line;
37
- this is a static demonstrator (full extraction lands in a later phase).
74
+ <code class="font-mono text-2xs">stage-*</code> badge palette, and the drag / optimistic
75
+ move + reorder. McRitchie Studio's <code class="font-mono text-2xs">/deployments</code> board is
76
+ the top-of-line; the specimen below is the <strong>real</strong>
77
+ <code class="font-mono text-2xs">studio/board/board</code> primitive in
78
+ <code class="font-mono text-2xs">demo: true</code> mode &mdash; drag a card, no save.
38
79
  </p>
39
80
  </div>
40
81
 
41
- <%# ---- The board ------------------------------------------------------- %>
82
+ <%# ---- The LIVE primitive (demo mode) --------------------------------- %>
83
+ <%# Render the factory once (a <script> in the board template would never run), then
84
+ the board. demo: true → drag animates + toasts locally, nothing POSTs. %>
85
+ <%= render "studio/board_assets" %>
86
+ <div class="space-y-3">
87
+ <div class="flex items-center gap-2">
88
+ <span class="badge" style="color: var(--color-success); border-color: var(--color-success)">Live</span>
89
+ <p class="label-upper"><code class="font-mono text-2xs">studio/board/board</code> &middot; demo &mdash; drag a card between columns</p>
90
+ </div>
91
+ <%= render "studio/board/board",
92
+ demo: true,
93
+ columns: live_board_columns,
94
+ card_partial: "style/board/demo_card",
95
+ card_as: :card,
96
+ reorder_url: "#",
97
+ move_url: "#",
98
+ move_param: { resource: "task", attr: "stage" },
99
+ group: "studio-board-demo",
100
+ empty_label: "Drop tasks here" %>
101
+ <div class="relative" x-data="{ copied: false }">
102
+ <pre class="bg-inset text-body text-2xs rounded-lg px-3 py-2 pr-16 overflow-x-auto"><code x-ref="boardUsage"><%= board_usage_snippet %></code></pre>
103
+ <button type="button"
104
+ class="btn btn-neutral btn-sm absolute top-2 right-2"
105
+ aria-label="Copy usage snippet"
106
+ @click="navigator.clipboard?.writeText($refs.boardUsage.innerText); copied = true; setTimeout(() => copied = false, 1200)">
107
+ <span x-text="copied ? 'Copied' : 'Copy'">Copy</span>
108
+ </button>
109
+ </div>
110
+ </div>
111
+
112
+ <%# ---- The static reference sketch ------------------------------------- %>
42
113
  <div class="fade-edge-x overflow-x-auto pb-2">
43
114
  <div class="flex gap-4 min-w-max">
44
115
  <% board_columns.each do |col| %>
@@ -0,0 +1,22 @@
1
+ <%# locals: (card:, id_attr: "slug", zone_attr: "stage") %>
2
+ <%#
3
+ A demo board card for the /admin/style Board specimen. Renders THROUGH the engine
4
+ card-shell chrome (studio/board/_card_shell) so the specimen exercises the real
5
+ identity contract (id="card-<id>", class .kanban-card, data-<id_attr>/<zone_attr>),
6
+ not a bespoke stand-in. `card` is a plain Hash — the specimen has no models. The
7
+ id_attr / zone_attr flow through from the board so the same partial demonstrates
8
+ the primitive under both the default and a custom attribute naming.
9
+ %>
10
+ <%= render layout: "studio/board/card_shell",
11
+ locals: { id: card[:id], zone: card[:zone], id_attr: id_attr, zone_attr: zone_attr,
12
+ class: "card p-3 space-y-2 cursor-grab active:cursor-grabbing" } do %>
13
+ <div class="flex items-start gap-2">
14
+ <span class="text-muted select-none leading-none" aria-hidden="true">⠿</span>
15
+ <p class="text-sm font-semibold text-heading leading-snug flex-1"><%= card[:title] %></p>
16
+ </div>
17
+ <div class="flex items-center justify-between gap-2">
18
+ <%= render "components/badge", text: card[:repo], scheme: "neutral" %>
19
+ <span class="inline-flex h-6 w-6 items-center justify-center rounded-full text-2xs font-bold text-white shrink-0"
20
+ style="background: var(--color-cta)"><%= card[:who] %></span>
21
+ </div>
22
+ <% end %>
data/lib/studio/engine.rb CHANGED
@@ -6,6 +6,7 @@ module Studio
6
6
  studio/sticky_table_header.js
7
7
  studio/canvas_confetti.js
8
8
  studio/studio_confetti.js
9
+ studio/sortable.js
9
10
  ]
10
11
  end
11
12
 
@@ -1,3 +1,3 @@
1
1
  module Studio
2
- VERSION = "0.22.0"
2
+ VERSION = "0.24.0"
3
3
  end
data/lib/studio.rb CHANGED
@@ -25,6 +25,20 @@ module Studio
25
25
  mattr_accessor :theme_logos, default: []
26
26
  mattr_accessor :sticky_table_headers, default: false
27
27
 
28
+ # ---- Smooth-load convention ---------------------------------------------
29
+ # Opt-in per app: renders the view-transition + no-preview metas so Turbo 8
30
+ # page swaps materialize behind the current page and present with a view
31
+ # transition (layouts/studio/_smooth_load). OFF by default — an app with
32
+ # known multi-second pages should fix those before opting in, because
33
+ # no-preview holds the old page until the fresh response arrives.
34
+ mattr_accessor :smooth_load, default: false
35
+
36
+ # Minimum ms the nav spinner stays visible once shown (_head.html.erb).
37
+ # Apps wrapping multi-second operations in the spinner (e.g. Solana RPC)
38
+ # keep the high default to avoid flicker; smooth-load apps typically drop
39
+ # this to ~300 so fast loads never linger on a spinner.
40
+ mattr_accessor :nav_spinner_min_ms, default: 2500
41
+
28
42
  # ---- Authentication ------------------------------------------------------
29
43
  # Which sign-in methods this app offers. The shared login/signup views render
30
44
  # a button/field per enabled method (gate with Studio.auth_method?). Order is
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: studio-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.0
4
+ version: 0.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McRitchie
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-07-28 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -171,6 +170,7 @@ files:
171
170
  - app/assets/images/resend-favicon.png
172
171
  - app/assets/images/ses-favicon.png
173
172
  - app/assets/javascripts/studio/canvas_confetti.js
173
+ - app/assets/javascripts/studio/sortable.js
174
174
  - app/assets/javascripts/studio/sticky_table_header.js
175
175
  - app/assets/javascripts/studio/studio_confetti.js
176
176
  - app/assets/stylesheets/studio/sticky_table_header.css
@@ -178,6 +178,7 @@ files:
178
178
  - app/assets/tailwind/studio_engine/engine.css
179
179
  - app/controllers/concerns/solana/session_auth.rb
180
180
  - app/controllers/concerns/studio/admin_models.rb
181
+ - app/controllers/concerns/studio/board/reorderable.rb
181
182
  - app/controllers/concerns/studio/error_handling.rb
182
183
  - app/controllers/concerns/studio/impersonation.rb
183
184
  - app/controllers/concerns/studio/link_consumption.rb
@@ -205,6 +206,7 @@ files:
205
206
  - app/mailers/application_mailer.rb
206
207
  - app/mailers/user_mailer.rb
207
208
  - app/models/concerns/sluggable.rb
209
+ - app/models/concerns/studio/board/rankable.rb
208
210
  - app/models/concerns/studio/broadcastable.rb
209
211
  - app/models/current.rb
210
212
  - app/models/error_log.rb
@@ -239,6 +241,7 @@ files:
239
241
  - app/views/layouts/branded_mailer.html.erb
240
242
  - app/views/layouts/studio/_flash.html.erb
241
243
  - app/views/layouts/studio/_head.html.erb
244
+ - app/views/layouts/studio/_smooth_load.html.erb
242
245
  - app/views/magic_links/confirm.html.erb
243
246
  - app/views/navbar/show.html.erb
244
247
  - app/views/registrations/new.html.erb
@@ -246,6 +249,7 @@ files:
246
249
  - app/views/sessions/_sso_continue.html.erb
247
250
  - app/views/sessions/new.html.erb
248
251
  - app/views/studio/_age_verify_assets.html.erb
252
+ - app/views/studio/_board_assets.html.erb
249
253
  - app/views/studio/_confirm_interstitial.html.erb
250
254
  - app/views/studio/_cropper_assets.html.erb
251
255
  - app/views/studio/_leveling_activity_assets.html.erb
@@ -259,6 +263,9 @@ files:
259
263
  - app/views/studio/banners/_email_status_button.html.erb
260
264
  - app/views/studio/banners/_environment.html.erb
261
265
  - app/views/studio/banners/_impersonation.html.erb
266
+ - app/views/studio/board/_board.html.erb
267
+ - app/views/studio/board/_card_shell.html.erb
268
+ - app/views/studio/board/_column.html.erb
262
269
  - app/views/studio/email_images/index.html.erb
263
270
  - app/views/studio/links/confirm.html.erb
264
271
  - app/views/studio/local_emails/index.html.erb
@@ -299,6 +306,7 @@ files:
299
306
  - app/views/style/_tasks.html.erb
300
307
  - app/views/style/_theme.html.erb
301
308
  - app/views/style/_tricks.html.erb
309
+ - app/views/style/board/_demo_card.html.erb
302
310
  - app/views/style/index.html.erb
303
311
  - app/views/style/modals/_age_verify.html.erb
304
312
  - app/views/style/modals/_auth.html.erb
@@ -341,7 +349,6 @@ metadata:
341
349
  source_code_uri: https://github.com/amcritchie/studio-engine/tree/main
342
350
  bug_tracker_uri: https://github.com/amcritchie/studio-engine/issues
343
351
  changelog_uri: https://github.com/amcritchie/studio-engine/blob/main/CHANGELOG.md
344
- post_install_message:
345
352
  rdoc_options: []
346
353
  require_paths:
347
354
  - lib
@@ -356,8 +363,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
356
363
  - !ruby/object:Gem::Version
357
364
  version: '0'
358
365
  requirements: []
359
- rubygems_version: 3.5.22
360
- signing_key:
366
+ rubygems_version: 4.0.9
361
367
  specification_version: 4
362
368
  summary: Shared Rails engine providing auth, SSO, error logging, theming, and S3-backed
363
369
  image caching