studio-engine 0.96.2 → 0.97.0.rc1

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +183 -0
  3. data/app/assets/tailwind/studio_engine/engine-motion.css +57 -0
  4. data/app/assets/tailwind/studio_engine/engine.css +8 -0
  5. data/app/javascript/studio/alpine_shims.js +23 -0
  6. data/app/javascript/studio/application.js +9 -21
  7. data/app/javascript/studio/board.js +593 -0
  8. data/app/javascript/studio/booking.js +176 -0
  9. data/app/javascript/studio/controllers/board_controller.js +41 -0
  10. data/app/javascript/studio/controllers/booking_controller.js +24 -0
  11. data/app/javascript/studio/controllers/email_banner_scale_controller.js +22 -0
  12. data/app/javascript/studio/controllers/footer_map_controller.js +22 -0
  13. data/app/javascript/studio/controllers/geo_settings_controller.js +24 -0
  14. data/app/javascript/studio/controllers/hold_button_controller.js +64 -0
  15. data/app/javascript/studio/controllers/link_preview_card_controller.js +25 -0
  16. data/app/javascript/studio/controllers/link_sidebar_controller.js +18 -0
  17. data/app/javascript/studio/controllers/survey_controller.js +18 -0
  18. data/app/javascript/studio/controllers/toast_controller.js +26 -0
  19. data/app/javascript/studio/email_banner.js +277 -0
  20. data/app/javascript/studio/email_banner_scale.js +60 -0
  21. data/app/javascript/studio/footer_map.js +143 -0
  22. data/app/javascript/studio/geo_settings.js +105 -0
  23. data/app/javascript/studio/hold_button.js +416 -0
  24. data/app/javascript/studio/hold_button_hooks.js +162 -0
  25. data/app/javascript/studio/lazy_controllers.js +138 -0
  26. data/app/javascript/studio/leveling_activity.js +252 -0
  27. data/app/javascript/studio/link_preview_card.js +34 -0
  28. data/app/javascript/studio/link_sidebar.js +157 -0
  29. data/app/javascript/studio/stimulus.js +72 -0
  30. data/app/javascript/studio/survey.js +316 -0
  31. data/app/javascript/studio/theme_editor.js +197 -0
  32. data/app/javascript/studio/toast.js +270 -0
  33. data/app/views/components/_link_sidebar.html.erb +5 -100
  34. data/app/views/components/_link_sidebar_trigger.html.erb +2 -2
  35. data/app/views/layouts/studio/_flash.html.erb +13 -118
  36. data/app/views/layouts/studio/_head.html.erb +30 -14
  37. data/app/views/studio/_board_assets.html.erb +9 -452
  38. data/app/views/studio/_hold_button.html.erb +38 -445
  39. data/app/views/studio/_hold_button_guard.html.erb +172 -0
  40. data/app/views/studio/_leveling_activity_assets.html.erb +9 -283
  41. data/app/views/studio/board/_board.html.erb +16 -5
  42. data/app/views/studio/booking/_assets.html.erb +5 -139
  43. data/app/views/studio/booking/_frame.html.erb +3 -3
  44. data/app/views/studio/booking/_popup.html.erb +4 -4
  45. data/app/views/studio/emails/_banner_editor.html.erb +4 -152
  46. data/app/views/studio/emails/_banner_scale.html.erb +5 -56
  47. data/app/views/studio/emails/index.html.erb +0 -1
  48. data/app/views/studio/geo_settings/edit.html.erb +1 -97
  49. data/app/views/studio/site_footer/_map.html.erb +7 -5
  50. data/app/views/studio/site_footer/_map_assets.html.erb +5 -129
  51. data/app/views/studio/site_identities/edit.html.erb +1 -24
  52. data/app/views/studio/surveys/show.html.erb +4 -5
  53. data/app/views/style/_tasks.html.erb +3 -8
  54. data/app/views/style/_theme.html.erb +2 -52
  55. data/app/views/theme_settings/edit.html.erb +3 -82
  56. data/config/importmap.rb +6 -0
  57. data/docs/BOOKING.md +6 -5
  58. data/docs/SITE_FOOTER.md +16 -12
  59. data/lib/studio/version.rb +1 -1
  60. metadata +28 -3
  61. data/app/views/studio/emails/_recipient_repaint.html.erb +0 -122
  62. data/app/views/studio/surveys/_script.html.erb +0 -309
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 636f573bb4164b3e8dc813a50d71347dcdb81bba7942ea302e9487fbf83b2d11
4
- data.tar.gz: b1cc9010532d7adfd1c39b352bdc4f257ee1bfb6db42955c9caaa423f3c3951a
3
+ metadata.gz: ee44de927b41f3860880f106cf5abc81b69d076a7d35f4de767bd904533b63c4
4
+ data.tar.gz: 200c6bdb2758bc78fadded463649a281dadc7042b18d9a8634a33ab6efd5ddc2
5
5
  SHA512:
6
- metadata.gz: ca888c9b19db94fd66441af537b0e785b269d4e9263458eb73affe2720c92609d10853a274802290a2a2e3f561d66dca990e49c85c4da295e47e593e40c64942
7
- data.tar.gz: 2fd26c5e568d21f278e54781bb3759fb67f4284252ecd8779d745340019257d52741305284a824854ab6ad39590ca633e7caa8500ee331fa2f253c4d15aa99c1
6
+ metadata.gz: d92ba31d0fd6a1ff1c5a2dd0d140fb6dc3c396e2e7813ab034d64666520c611a9497ec396d99307e59f67793b53295b88988c10f1b23e3025e142d1ade370834
7
+ data.tar.gz: ce67bc16e6c58eda9afa49a401d2b079b635340244d01320bc0cd67873d189e10c30b47e5fb53b6c52176c378fe79b17f399d6bcf9b3a94ac424df968b83376d
data/CHANGELOG.md CHANGED
@@ -4,6 +4,189 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.97.0 — 2026-10-09
8
+
9
+ ### Changed
10
+
11
+ - **The toast queue, the link sidebar and the hold button move to ES modules and
12
+ Stimulus controllers.** `layouts/studio/_flash`, `components/_link_sidebar`
13
+ and `studio/_hold_button` render markup only; their inline scripts are gone.
14
+ Rendered markup and styling are unchanged apart from the attributes named
15
+ here.
16
+ - **Toast.** The queue is `studio/toast`, registered as `$store.toasts`. The
17
+ partial's root is a bare `x-data` carrying `data-studio-controller="toast"`
18
+ and the flash as `data-toast-initial-value`. The API is unchanged: dispatch
19
+ the `toast` window event. A page's Turbo snapshot no longer keeps its
20
+ flash, so Back does not replay it.
21
+ - **Link sidebar.** `$store.sidebars.linkTreeOpen` and the document click and
22
+ Escape handlers are `studio/link_sidebar`; the partial renders one
23
+ `<template data-studio-controller="link-sidebar">` anchor. The flag
24
+ registers only on a page that renders the panel, and a host's own
25
+ `sidebars` store keeps its keys.
26
+ - **Hold button.** The timeline, the idle nudge and the fizz portal are
27
+ `studio/hold_button`, on a `hold-button` controller that sits on the
28
+ `.hold-stack`; the button's presses are `data-studio-action`s. The idle
29
+ nudge runs only on a button the partial rendered, from the moment it
30
+ connects. `studio/stimulus` imports the controller statically, so it is
31
+ preloaded with the page and a button that renders later (a modal's) makes
32
+ no further request before its first press.
33
+ - `layouts/studio/_head` imports `studio/toast`, `studio/link_sidebar` and
34
+ `studio/stimulus` by their own nonced module tags, as it does
35
+ `studio/alpine_stores` and `studio/modal_host`, so a boot that fails to
36
+ load keeps a page its flash, its sidebar and its hold button.
37
+ - **The engine's Stimulus application is `studio/stimulus`.**
38
+ `studio/application` still exports `application` and `schema`. A controller
39
+ for one kind of page is listed in `studio/stimulus`'s `LAZY` and registered by
40
+ `studio/lazy_controllers` the first time an element names it; the list starts
41
+ empty. A lazy load that fails is final for the document, as it is in a
42
+ browser: the registry imports a controller once, reports a failure once, and
43
+ marks each element naming it with `data-studio-controller-failed`.
44
+ - **The admin pages, the email manager, booking, the footer map and the survey
45
+ stepper move to ES modules.** Their inline scripts are gone; rendered markup
46
+ and styling are unchanged apart from the `data-studio-controller` attributes
47
+ named here. Six controllers register lazily, each fetched only by a page that
48
+ names it:
49
+ - `geo-settings` on `/admin/geo`'s root (`studio/geo_settings`), and
50
+ `link-preview-card` on `/admin/link_preview`'s root
51
+ (`studio/link_preview_card`).
52
+ - `email-banner-scale` on the `<template>` anchor `studio/emails/_banner_scale`
53
+ renders (`studio/email_banner_scale`).
54
+ - `booking` on the frame's wrapper and the popup's dialog (`studio/booking`),
55
+ and `footer-map` on the map element (`studio/footer_map`). Both install
56
+ once per document under the same guards (`__studioBookingFramesArmed`,
57
+ `__studioBookingPopupArmed`, `__studioFooterMapsArmed`) and
58
+ `window.__studioBookingLoad` is still published. Until the booking
59
+ controller connects, a booking link is an ordinary link to the booking page.
60
+ - `survey` on the survey's root (`studio/survey`). Until it connects, and if
61
+ it never does, the page is the plain form.
62
+ - The theme editors and the email manager's editors stay Alpine factories:
63
+ `themeEditor` and `dsThemeEditor` are `studio/theme_editor`, and
64
+ `emailBannerEditor` and `emailRecipients` are `studio/email_banner`.
65
+ `studio/alpine_shims` publishes all four on every page, because an `x-data`
66
+ is evaluated the moment Alpine starts. `themeEditor` takes its colours as
67
+ an argument.
68
+ - **`test/views/no_inline_script_test.rb` holds the views to it.** A view with
69
+ an inline script fails unless it is on the test's allow-list, where each entry
70
+ names the task that owns it; an entry whose script is gone fails too. Two are
71
+ left inline on purpose: `studio/_at_time_script`, whose first pass runs before
72
+ first paint, and `solana_sessions/phantom_callback`, the wallet's return leg.
73
+
74
+ - **The board primitive and the leveling activity modals move to ES modules.**
75
+ `studio/_board_assets` and `studio/_leveling_activity_assets` render nothing;
76
+ their inline scripts are gone. A consumer's `render` of either keeps working
77
+ and can be deleted. Rendered markup is unchanged apart from
78
+ `data-studio-controller="board"` on the board's `<section>`.
79
+ - **Board.** The factory is `studio/board`. `x-data="studioBoard({...})"`
80
+ still resolves: `studio/alpine_shims` publishes `window.studioBoard` on
81
+ every page, and every name on the scope is unchanged (`state`, `toasts`,
82
+ `toast`, `updateCounts`, `animateCardExit`, the state helpers). The zones
83
+ are made draggable by a `board` controller, registered lazily, once
84
+ SortableJS has loaded; `data-alpine-ready` is set after that, so it still
85
+ means "the board drags". A board written by hand with only the `x-data`
86
+ names the controller on itself.
87
+ - **A board whose controller fails to load says so.** The board controller is
88
+ lazy, and a failed lazy load is final for the document. The board's section
89
+ then carries `data-studio-controller-failed="board"` and shows "This board
90
+ could not load its controls. Reload the page." above its cards, which stay
91
+ readable; the notice is revealed by a rule in `engine.css`, so an app sees
92
+ it once its Tailwind build includes this engine's `engine.css`.
93
+ - **Leveling activity.** The factory is `studio/leveling_activity`, published
94
+ as `window.levelingActionModal`. Its opts and JSON contract are unchanged.
95
+ - **SortableJS loads only on a page that drags.** `layouts/studio/_head` no
96
+ longer loads `studio/sortable` on every page. The vendored build is pinned as
97
+ `sortablejs` and imported by `studio/board` on the first board a page
98
+ renders. `window.Sortable` is a shim until then: `Sortable.create(el, options)`
99
+ in a page script loads the library and then creates the sortable, so a host
100
+ that calls it (the hub's deploy board) needs no change. The shim's `create`
101
+ returns nothing; `await Sortable.load()` resolves with SortableJS for a
102
+ caller that needs the instance. canvas-confetti and `studio_confetti` stay in
103
+ the head: `fireSuccessConfetti` and consumer scripts call them on any page.
104
+
105
+ ### Added
106
+
107
+ - **The hold button dispatches `hold-button:guard`, `hold-button:start`,
108
+ `hold-button:validate`, `hold-button:early` and `hold-button:success`**, on
109
+ the button, bubbling, each with `detail.id` (the `hold_id`). `preventDefault()`
110
+ on `guard` refuses the hold, on `early` takes the action over, and on
111
+ `success` leaves the button's state to the listener;
112
+ `$event.detail.waitUntil(answer)` on `validate` takes a boolean or a promise,
113
+ and a false or failed answer aborts the hold. The string locals (`guard:`,
114
+ `on_hold_start:`, `validate:`, `early_action:`, `early_action_guard:`,
115
+ `on_success:`) still work and are heard beside the events; write new call
116
+ sites against the events. `validate_at:` and `early_action_at:` are honoured
117
+ without their string local.
118
+ - **The hold button fails safe.** A `guard:` that throws, or that has no Alpine
119
+ scope to run in, refuses the hold (it used to let the hold through); a second
120
+ press without a release restarts the hold, so one hold confirms once; a
121
+ validation answer for a press that was released does not abort the next one;
122
+ nothing of the timeline runs after the hold completes.
123
+
124
+ ### Fixed
125
+
126
+ - **The style guide's theme editor sends a save once.** The controller answers
127
+ a save with a 302 to `/admin/theme`, and a fetch that follows a 302 keeps a
128
+ PATCH a PATCH, so Save sent the theme twenty-one times and then toasted "Save
129
+ failed". The request no longer follows the redirect.
130
+ - **The email manager's banner editor repaints the banner on the email's own
131
+ page.** `studio/emails/_banner_preview` isolates the banner in an iframe, and
132
+ the editor looked for its nodes in the page's own document only, so typing a
133
+ header, sub-text or tint changed nothing there. The editor now reaches into
134
+ the frame, as the emails list already did.
135
+ - **Two boards on one page no longer drive each other's zones.** A board made
136
+ every `.kanban-dropzone` on the page sortable and counted cards by document
137
+ id, so the first board's group, filter and lock rule governed every board
138
+ after it: on `/admin/style` the depth-chart specimen's cards left their
139
+ lanes and its locked starters dragged. A board now wires, watches and counts
140
+ only the zones inside its own element.
141
+ - **A hold button that cannot work says so.** The button runs on five modules,
142
+ and a browser keeps a failed module request for the life of the document, so
143
+ one failure among them at page load left the button rendered, enabled and
144
+ deaf: a full hold sent nothing and showed nothing until a reload. The head
145
+ now carries a small inline, nonced guard (`studio/_hold_button_guard`) that
146
+ imports nothing. A `.hold-stack` whose controller has not connected 1.5 s
147
+ after `DOMContentLoaded` (or shortly after it arrives later, by a Turbo visit
148
+ or a modal's `x-if`) is marked `data-studio-controller-failed="hold-button"`:
149
+ the button dims and is `aria-disabled`, the bubbles stop, and "This button
150
+ could not load. Reload the page." shows under it in a `role="status"`
151
+ region; a press says it again. The controller writes
152
+ `data-hold-button-connected` on its stack and clears the mark the moment it
153
+ connects, however late. `studio/_hold_button` renders one more element, an
154
+ empty `<span class="hold-notice" role="status">` held out of the layout, so
155
+ a working button measures as before. A host with a nonce-based
156
+ `script-src` needs nothing new; one that allow-lists inline scripts by hash
157
+ adds this script's.
158
+
159
+ ### Breaking
160
+
161
+ - **`window.toastManager` is gone.** Nothing binds it: the flash partial reads
162
+ `$store.toasts`. A template that wrote `x-data="toastManager(...)"` of its own
163
+ renders `layouts/studio/flash` and dispatches the `toast` event instead.
164
+ - **`window.holdBtnStart`, `window.holdBtnEnd` and `window._holdBtnInit` are
165
+ gone.** The button binds its own presses; a page that called either function
166
+ dispatches the pointer events on the button, or renders the partial.
167
+ - **`window.__studioLinkSidebarBridge` is gone.** It was the inline script's
168
+ run-once flag; a test that looked for it in a page's HTML looks for
169
+ `data-studio-controller="link-sidebar"`.
170
+ - **`studio/emails/_recipient_repaint` and `studio/surveys/_script` are gone.**
171
+ Each rendered only a script. The engine's own pages no longer render them; a
172
+ host template that did deletes the line.
173
+ - **The footer's and the booking primitives' scripts are no longer in the
174
+ page's HTML.** A test that looked for `__studioFooterMapsArmed`,
175
+ `__studioBookingFramesArmed` or `__studioBookingPopupArmed` in a response body
176
+ looks for `data-studio-controller="footer-map"` on the map, or
177
+ `data-studio-controller="booking"` on the frame's wrapper and the dialog. The
178
+ globals themselves are still set, once the controller has connected.
179
+ `window.__studioSurveyBound` and `window.__studioSurveyTurboHook` are gone;
180
+ nothing outside the survey's script read them.
181
+ - **`window.Sortable` is not SortableJS until a page asks for it.** On a page
182
+ with no board it is the loading shim, which has `create` and `load` and
183
+ nothing else: `new Sortable(...)`, `Sortable.get` and the other statics fail
184
+ until the library has loaded. A page script that needs more than `create`
185
+ awaits `Sortable.load()` first. A page that loads its own SortableJS is
186
+ unaffected. A test that looked for `window.studioBoard =` or a
187
+ `studio/sortable` script tag in a page's HTML looks for
188
+ `data-studio-controller="board"` on the board.
189
+
7
190
  ## 0.96.2 — 2026-10-08
8
191
 
9
192
  ### Added
@@ -1733,6 +1733,63 @@ body.modal-open { overflow: visible; }
1733
1733
  z-index: 1;
1734
1734
  }
1735
1735
 
1736
+ /* THE BUTTON THAT COULD NOT LOAD. The guard in the head
1737
+ (studio/_hold_button_guard) marks a stack whose controller never connected
1738
+ data-studio-controller-failed="hold-button" and fills its .hold-notice. Until
1739
+ then the notice is an empty live region held out of the layout, so a working
1740
+ button measures exactly as it does without one. Marked, the button reads as
1741
+ unavailable, the bubbles stop, and the notice sits under the button in the
1742
+ danger ink, which the theme resolver holds to AA on every surface. */
1743
+ .hold-stack > .hold-notice {
1744
+ position: absolute;
1745
+ width: 1px;
1746
+ height: 1px;
1747
+ overflow: hidden;
1748
+ clip-path: inset(50%);
1749
+ white-space: nowrap;
1750
+ }
1751
+
1752
+ .hold-stack[data-studio-controller-failed~="hold-button"] > .hold-btn {
1753
+ opacity: 0.45;
1754
+ filter: grayscale(0.7);
1755
+ cursor: not-allowed;
1756
+ }
1757
+
1758
+ .hold-stack[data-studio-controller-failed~="hold-button"] > .hold-fizz,
1759
+ .hold-stack[data-studio-controller-failed~="hold-button"] > .hold-fizz-portal {
1760
+ display: none;
1761
+ }
1762
+
1763
+ .hold-stack[data-studio-controller-failed~="hold-button"] > .hold-notice {
1764
+ position: static;
1765
+ display: block;
1766
+ width: auto;
1767
+ height: auto;
1768
+ overflow: visible;
1769
+ clip-path: none;
1770
+ white-space: normal;
1771
+ margin-top: 0.5rem;
1772
+ font-size: 0.8125rem;
1773
+ font-weight: 600;
1774
+ line-height: 1.35;
1775
+ text-align: center;
1776
+ color: var(--color-danger-ink, var(--color-danger));
1777
+ }
1778
+
1779
+ /* A press on the marked button: the notice answers it, with a pulse only for
1780
+ a reader who has not asked for less motion. */
1781
+ @media (prefers-reduced-motion: no-preference) {
1782
+ .hold-stack[data-hold-notice-pressed] > .hold-notice {
1783
+ animation: hold-notice-pressed 0.45s ease-out;
1784
+ }
1785
+ }
1786
+
1787
+ @keyframes hold-notice-pressed {
1788
+ 0% { transform: scale(1); }
1789
+ 35% { transform: scale(1.06); }
1790
+ 100% { transform: scale(1); }
1791
+ }
1792
+
1736
1793
  .hold-stack > .hold-fizz,
1737
1794
  .hold-stack > .hold-fizz-portal,
1738
1795
  .hold-fizz-portal > .hold-fizz {
@@ -565,3 +565,11 @@ body.modal-open .studio-app-banner {
565
565
  }
566
566
  }
567
567
  }
568
+
569
+ /* A board whose controller failed to load (studio/lazy_controllers marks its
570
+ section data-studio-controller-failed="board") shows the notice
571
+ studio/board/_board renders with an inline display: none. !important is what
572
+ lets a stylesheet rule answer an inline style. */
573
+ .studio-board[data-studio-controller-failed~="board"] > .studio-board-failed {
574
+ display: block !important;
575
+ }
@@ -19,6 +19,10 @@ import { NavCollapse } from "studio/nav_collapse"
19
19
  import {
20
20
  showNavSpinner, hideNavSpinner, installSpinnerReset, fireSuccessConfetti
21
21
  } from "studio/head_chrome"
22
+ import { studioBoard, loadSortable, sortableShim } from "studio/board"
23
+ import { levelingActionModal } from "studio/leveling_activity"
24
+ import { themeEditor, dsThemeEditor } from "studio/theme_editor"
25
+ import { emailBannerEditor, emailRecipients } from "studio/email_banner"
22
26
 
23
27
  // x-data="navCollapse()": the hub's own header. A host that defines its own
24
28
  // window.navCollapse (turf-monster does, inline, before this runs) keeps it.
@@ -41,5 +45,24 @@ export function installAlpineShims() {
41
45
  if (!window.showNavSpinner) window.showNavSpinner = showNavSpinner
42
46
  if (!window.hideNavSpinner) window.hideNavSpinner = hideNavSpinner
43
47
  if (!window.fireSuccessConfetti) window.fireSuccessConfetti = fireSuccessConfetti
48
+ // x-data="studioBoard({...})": studio/board/_board, and any board a host
49
+ // writes by hand. Alpine passes the opts alone.
50
+ if (!window.studioBoard) window.studioBoard = (opts) => studioBoard(opts)
51
+ // x-data="levelingActionModal({...})": studio/modals/blocks/_leveling_activity
52
+ // and _change_username.
53
+ if (!window.levelingActionModal) window.levelingActionModal = (opts) => levelingActionModal(opts)
54
+ // Sortable.create(el, options) in a page script (the hub's deploy board):
55
+ // no page carries SortableJS until something asks for it, so the call loads
56
+ // it. SortableJS replaces this object when it arrives; a page that loads its
57
+ // own copy (turf-monster's slate page) replaces it the same way.
58
+ if (!window.Sortable) window.Sortable = sortableShim(() => loadSortable())
59
+ // x-data="themeEditor({...})" (theme_settings/edit) and
60
+ // x-data="dsThemeEditor({...})" (style/_theme). Alpine passes the seed alone.
61
+ if (!window.themeEditor) window.themeEditor = (seed) => themeEditor(seed)
62
+ if (!window.dsThemeEditor) window.dsThemeEditor = (seed) => dsThemeEditor(seed)
63
+ // x-data="emailBannerEditor({...})" (studio/emails/show) and
64
+ // x-data="emailRecipients({...})" (studio/emails/index).
65
+ if (!window.emailBannerEditor) window.emailBannerEditor = (config) => emailBannerEditor(config)
66
+ if (!window.emailRecipients) window.emailRecipients = (config) => emailRecipients(config)
44
67
  installSpinnerReset()
45
68
  }
@@ -2,38 +2,26 @@
2
2
  // layouts/studio/_head (javascript_import_module_tag, which carries the
3
3
  // request's CSP nonce).
4
4
  //
5
- // The engine runs its own Stimulus application on its OWN attributes:
6
- //
7
- // data-studio-controller="nav-collapse" data-studio-action data-studio-target
8
- //
9
- // so a host's Stimulus application never sees an engine controller. That is not
10
- // tidiness: stimulus-loading's lazy loader (cyvasse) imports
11
- // "controllers/<identifier>_controller" for every data-controller it meets, and
12
- // would log "Failed to autoload controller" for each engine element. Values and
13
- // classes keep Stimulus' own naming (data-nav-collapse-scrolled-class).
14
- // "@hotwired/stimulus" resolves to the engine's vendored copy unless the host
15
- // pins its own.
5
+ // The Stimulus application itself, its attribute schema, the hold button and
6
+ // the lazily registered page-specific controllers are studio/stimulus. This
7
+ // file registers the every-page chrome on it.
16
8
  //
17
9
  // Everything this file imports statically is preloaded on every page
18
10
  // (Studio::Engine.javascript_boot_graph), so the boot costs one round trip.
19
- import { Application, defaultSchema } from "@hotwired/stimulus"
11
+ import { application, schema } from "studio/stimulus"
20
12
  import NavCollapseController from "studio/controllers/nav_collapse_controller"
21
13
  import ModalHostController from "studio/controllers/modal_host_controller"
14
+ import ToastController from "studio/controllers/toast_controller"
15
+ import LinkSidebarController from "studio/controllers/link_sidebar_controller"
22
16
  import { startPinnedStack } from "studio/pinned_stack"
23
17
  import { installAlpineShims } from "studio/alpine_shims"
24
18
 
25
19
  installAlpineShims()
26
20
  startPinnedStack()
27
21
 
28
- export const schema = {
29
- ...defaultSchema,
30
- controllerAttribute: "data-studio-controller",
31
- actionAttribute: "data-studio-action",
32
- targetAttribute: "data-studio-target"
33
- }
34
-
35
- const application = Application.start(document.documentElement, schema)
36
22
  application.register("nav-collapse", NavCollapseController)
37
23
  application.register("modal-host", ModalHostController)
24
+ application.register("toast", ToastController)
25
+ application.register("link-sidebar", LinkSidebarController)
38
26
 
39
- export { application }
27
+ export { application, schema }