studio-engine 0.32.2 → 0.32.3

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: 9525633783466e9475652bb4ded7c35b05c36165cf76db8c68db9e9ee357e0cb
4
- data.tar.gz: 7e2bbfa555bd538f915985213fafe2a27de2ef6bb0a2ae6b4868054a3dea127b
3
+ metadata.gz: e56dfd4be2d6f429f329626c4aebb8b22aa9e0069cfdf8905a02fab4bc47ab4c
4
+ data.tar.gz: 71b6a6d3dafa57d3b25e9cb791049ae9b4a975efa7d14b08b961492b2b870d6f
5
5
  SHA512:
6
- metadata.gz: 33d02975ad35287da64b3a75ee297efec825d68fa5b3823e5ec834dc67eb572df96e669cff6fa94ce731293b343f2b9b9aef8a9a4ce9a6b63d4bddd79989fbda
7
- data.tar.gz: 792e5a6c4159be01aba533ad28e705d09576a2a8b5ee8b9af05f0dc1200ba2baddd8dad772f1eea6dd6d0b7957705d57901d937c0cf73be21a89d726fbccbd20
6
+ metadata.gz: ea74d63cf741530df52e0fbe490a3fe48817845235f12aa911e094d67fe6012e3b9f1d139f95d8f5240d467fa079a2746f6a3985c256a3daee948136752e3c9c
7
+ data.tar.gz: 75146aa7893ca074ada0c58b5ecfb20641615432c63090d3d8bd39bd0b6f6e480a6d7e558bbf45ac8003144a6fd224222fd7eeaac297571b522e255e43343472
data/CHANGELOG.md CHANGED
@@ -2,6 +2,40 @@
2
2
 
3
3
  The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — `MAJOR.MINOR.PATCH`. Consumer Rails apps install the released RubyGems package with `gem "studio-engine", "~> 0.6"`; bumping the gem version and updating consumer lockfiles is a release.
4
4
 
5
+ ## 0.32.3 — 2026-08-09
6
+
7
+ **New public CSS custom property: `--nav-bottom`.** `_head.html.erb` now
8
+ publishes the page header's **bottom edge** in viewport coordinates alongside
9
+ the existing `--nav-h`, which stays the header's **height**. Use them for
10
+ different jobs:
11
+
12
+ - `var(--nav-h)` — to size something as tall as the header.
13
+ - `var(--nav-bottom)` — to **start** something underneath it.
14
+
15
+ They are equal only when the header sits at the top of the viewport, which is
16
+ why one was long used for the other. An app that renders chrome **above** the
17
+ navbar — an environment banner before the `<header>` — pushes the header down,
18
+ and a `fixed` overlay offset by `--nav-h` then rides up over the header by
19
+ exactly the height of that chrome.
20
+
21
+ **Fix — `components/_sidebar_panel` offsets from `--nav-bottom`.** The open
22
+ sidebar panel overlapped the header by 47px on `mcritchie-industries`, covering
23
+ the Log in button. Falls back to `var(--nav-h)` (then `6rem`), so a host that
24
+ overrides `layouts/studio/_head` keeps today's geometry rather than collapsing.
25
+ **No change for a consumer whose header already starts at the viewport top** —
26
+ the two values converge there.
27
+
28
+ Unlike `--nav-h`, `--nav-bottom` is **scroll-dependent**: chrome above the
29
+ header scrolls away while a panel is open, and nothing locks body scroll. It
30
+ republishes on a passive `scroll` listener behind a `requestAnimationFrame`
31
+ throttle, bound once at script eval rather than per Turbo visit. A page with no
32
+ `<header>` now releases the observed node, so a Turbo visit to a headerless page
33
+ can no longer publish an all-zero rect from a detached element.
34
+
35
+ **Note for hosts that override `components/_sidebar_panel`** — an app copy of
36
+ that partial shadows the engine's, so it keeps the old `top:var(--nav-h, 6rem)`
37
+ until updated. `mcritchie-studio` and `turf-monster` both carry such an override.
38
+
5
39
  ## 0.32.2 — 2026-08-09
6
40
 
7
41
  **Two public-page navigation fixes** (found by the industries sidebar-adoption
@@ -1,7 +1,18 @@
1
1
  <%# Shared fixed right-sidebar shell (lifted from the mcritchie-studio hub).
2
2
  Alpine-driven: `open` is an Alpine expression; the panel slides in from
3
3
  off-screen right under x-transition and positions beneath the sticky
4
- header via the --nav-h custom property _head.html.erb publishes. %>
4
+ header via the --nav-bottom custom property _head.html.erb publishes.
5
+ --nav-bottom is the header's BOTTOM EDGE, not its height: the two differ
6
+ the moment an app renders chrome above the navbar (an environment banner
7
+ before the header), and positioning off the height put this panel over the
8
+ header by exactly that much.
9
+
10
+ The --nav-h fallback is NOT for a host pinning an older gem — an older gem
11
+ ships the old _head and the old panel together, so that path never runs.
12
+ It is reachable only when a host OVERRIDES layouts/studio/_head, in which
13
+ case --nav-bottom is never published and this degrades to the previous,
14
+ BUGGY geometry. That is a deliberate floor, not a safe default: it keeps an
15
+ overriding host at today's behaviour instead of collapsing to 6rem. %>
5
16
  <% id = local_assigns.fetch(:id, nil) %>
6
17
  <% title = local_assigns.fetch(:title, nil) %>
7
18
  <% open = local_assigns.fetch(:open) %>
@@ -33,7 +44,7 @@
33
44
  x-transition:leave-start="translate-x-0"
34
45
  x-transition:leave-end="translate-x-full"
35
46
  class="<%= display_class %> fixed right-0 <%= width_class %> bg-surface border-l border-subtle shadow-2xl <%= z_class %> flex-col <%= panel_class %>"
36
- style="top:var(--nav-h, 6rem);height:calc(100% - var(--nav-h, 6rem))"
47
+ style="top:var(--nav-bottom, var(--nav-h, 6rem));height:calc(100% - var(--nav-bottom, var(--nav-h, 6rem)))"
37
48
  role="<%= role %>"
38
49
  <% if labelledby %>aria-labelledby="<%= labelledby %>"<% elsif aria_label.present? %>aria-label="<%= aria_label %>"<% end %>>
39
50
  <div class="flex flex-col h-full">
@@ -96,19 +96,54 @@
96
96
  body { overflow-anchor: none; }
97
97
  </style>
98
98
  <script>
99
- // studio-engine: publish the sticky header's live height as the --nav-h
100
- // CSS variable so fixed/sticky elements below the navbar can position off
101
- // it (e.g. top: var(--nav-h)) without hardcoded px. Tracks every resize,
102
- // including scroll-collapse animations, and re-attaches after Turbo nav.
99
+ // studio-engine: publish the sticky header's live geometry as CSS custom
100
+ // properties so fixed/sticky elements can position off it without hardcoded
101
+ // px. Tracks every resize, including scroll-collapse animations, and
102
+ // re-attaches after Turbo nav.
103
+ //
104
+ // --nav-h the header's HEIGHT
105
+ // --nav-bottom the header's BOTTOM EDGE, in viewport coordinates
106
+ //
107
+ // They are equal only when the header sits at the top of the viewport, which
108
+ // is why one was long mistaken for the other. An app that stacks chrome ABOVE
109
+ // the header — an environment banner rendered before the navbar, as
110
+ // mcritchie-industries does — pushes the header down, and a `fixed` overlay
111
+ // positioned at --nav-h then rides UP over the header by exactly the height of
112
+ // that chrome. Use --nav-h to size something as tall as the header; use
113
+ // --nav-bottom to start something underneath it.
103
114
  (function () {
104
115
  if (!window.ResizeObserver) return;
105
116
  var ro = null;
117
+ var current = null;
118
+ var queued = false;
106
119
  function publish(header) {
107
- document.documentElement.style.setProperty('--nav-h', header.offsetHeight + 'px');
120
+ var style = document.documentElement.style;
121
+ style.setProperty('--nav-h', header.offsetHeight + 'px');
122
+ // Scroll-dependent where --nav-h is not: chrome above the header scrolls
123
+ // away while an overlay is open (nothing here locks body scroll), so this
124
+ // is republished on scroll. Clamped at 0 — once the sticky header is
125
+ // pinned at top:0 the two values converge, which is why a consumer whose
126
+ // header already starts at the viewport top sees no change at all.
127
+ style.setProperty('--nav-bottom', Math.max(0, header.getBoundingClientRect().bottom) + 'px');
128
+ }
129
+ function schedule() {
130
+ if (queued || !current) return;
131
+ queued = true;
132
+ window.requestAnimationFrame(function () { queued = false; if (current) publish(current); });
108
133
  }
109
134
  function attach() {
110
135
  var header = document.querySelector('header');
111
- if (!header) return;
136
+ if (!header) {
137
+ // A page with no <header> — a bare error page, a print view. RELEASE the
138
+ // old node instead of returning: the scroll listener outlives the visit,
139
+ // and publishing from a DETACHED element yields an all-zero rect, which
140
+ // would drive --nav-h to 0px. Before this listener existed nothing ran
141
+ // after detach, so --nav-h simply kept its last value; keep that promise.
142
+ if (ro) { ro.disconnect(); ro = null; }
143
+ current = null;
144
+ return;
145
+ }
146
+ current = header;
112
147
  if (ro) ro.disconnect();
113
148
  ro = new ResizeObserver(function () { publish(header); });
114
149
  ro.observe(header);
@@ -116,6 +151,10 @@
116
151
  }
117
152
  document.addEventListener('DOMContentLoaded', attach);
118
153
  document.addEventListener('turbo:load', attach);
154
+ // Registered ONCE at script eval, not inside attach(), so a Turbo nav
155
+ // re-points `current` instead of stacking another listener per visit.
156
+ window.addEventListener('scroll', schedule, { passive: true });
157
+ window.addEventListener('resize', schedule, { passive: true });
119
158
  })();
120
159
  </script>
121
160
 
@@ -1,3 +1,3 @@
1
1
  module Studio
2
- VERSION = "0.32.2"
2
+ VERSION = "0.32.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: studio-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.2
4
+ version: 0.32.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McRitchie