studio-engine 0.17.0 → 0.18.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -0
  3. data/app/assets/tailwind/studio_engine/engine-motion.css +620 -5
  4. data/app/controllers/style_controller.rb +25 -0
  5. data/app/views/studio/modals/_crop_photo.html.erb +5 -1
  6. data/app/views/studio/modals/_host.html.erb +5 -0
  7. data/app/views/studio/modals/_image_upload.html.erb +16 -5
  8. data/app/views/studio/modals/_saving.html.erb +6 -2
  9. data/app/views/studio/modals/auth/_resend_footer.html.erb +25 -0
  10. data/app/views/studio/modals/blocks/_card_header.html.erb +83 -0
  11. data/app/views/studio/modals/blocks/_cta_redirect.html.erb +73 -0
  12. data/app/views/studio/modals/blocks/_onchain_success.html.erb +52 -0
  13. data/app/views/studio/modals/blocks/_progress_pill.html.erb +23 -0
  14. data/app/views/studio/modals/blocks/_shell.html.erb +35 -0
  15. data/app/views/studio/modals/blocks/_solana_tx_link.html.erb +42 -0
  16. data/app/views/studio/modals/shared/_age_attestation.html.erb +34 -0
  17. data/app/views/studio/modals/shared/_email_field.html.erb +70 -0
  18. data/app/views/studio/modals/templates/_action.html.erb +25 -0
  19. data/app/views/studio/modals/templates/_form.html.erb +25 -0
  20. data/app/views/studio/modals/templates/_status.html.erb +45 -0
  21. data/app/views/studio/modals/templates/_success.html.erb +23 -0
  22. data/app/views/studio/modals/templates/_wizard.html.erb +75 -0
  23. data/app/views/style/_modal_specimen.html.erb +120 -0
  24. data/app/views/style/_modals.html.erb +690 -0
  25. data/app/views/style/_specimen.html.erb +64 -0
  26. data/app/views/style/_tasks.html.erb +112 -0
  27. data/app/views/style/_theme.html.erb +222 -0
  28. data/app/views/style/_tricks.html.erb +323 -0
  29. data/app/views/style/index.html.erb +60 -0
  30. data/app/views/style/modals/_auth.html.erb +252 -0
  31. data/app/views/style/modals/_onchain_tx.html.erb +62 -0
  32. data/app/views/style/modals/_wallet_connect.html.erb +138 -0
  33. data/app/views/style/modals/_wallet_deposit.html.erb +56 -0
  34. data/lib/studio/version.rb +1 -1
  35. data/lib/studio.rb +27 -1
  36. metadata +32 -6
  37. data/app/controllers/design_system_controller.rb +0 -12
  38. data/app/views/design_system/_specimen.html.erb +0 -27
  39. data/app/views/design_system/index.html.erb +0 -232
@@ -0,0 +1,23 @@
1
+ <%# === Template: SUCCESS modal ======================================
2
+ Large-title celebration for end states — action completed, item created.
3
+ card_header at :lg gives a text-3xl title; the primary CTA pulls the user
4
+ forward. For a success that warrants a Solana tx link / auto-redirect drain,
5
+ use studio/modals/blocks/_onchain_success instead. Ported from turf-monster.
6
+
7
+ Local: modal_store — Alpine store name backing close(). Default "modals". %>
8
+ <% modal_store = local_assigns.fetch(:modal_store, "modals") %>
9
+ <div x-data>
10
+ <%= render "studio/modals/blocks/card_header",
11
+ size: :lg,
12
+ icon_color: 'primary',
13
+ title: "Done!",
14
+ subtitle: "Operation completed. Swap in the real success copy + CTA destination." %>
15
+
16
+ <button @click="$store.<%= modal_store %>.close()" class="btn btn-primary btn-lg w-full">
17
+ Continue
18
+ </button>
19
+ <button @click="$store.<%= modal_store %>.close()"
20
+ class="block mx-auto mt-3 text-sm text-secondary hover:text-heading transition">
21
+ Dismiss
22
+ </button>
23
+ </div>
@@ -0,0 +1,75 @@
1
+ <%# === Template: WIZARD modal ======================================
2
+ Multi-step flow with a segmented progress pill + centered header. Use for
3
+ onboarding, checkout journeys, anything that breaks into 2-4 screens. Ported
4
+ from turf-monster.
5
+
6
+ Visual identity (distinct from form/action/status): centered title above the
7
+ pill; N-segment pill (filled = primary, upcoming = inset+subtle); single
8
+ primary CTA pulling forward; Back as a subtle link, hidden on step 1.
9
+
10
+ Local: modal_store — Alpine store name backing close(). Default "modals". %>
11
+ <% modal_store = local_assigns.fetch(:modal_store, "modals") %>
12
+ <div x-data="{ step: 1, total: 3 }">
13
+ <div class="relative mb-3 -mt-2">
14
+ <h3 class="text-heading font-bold text-lg leading-tight text-center pt-1">Wizard Template</h3>
15
+ <button type="button" @click="$store.<%= modal_store %>.close()"
16
+ class="absolute top-0 right-0 -mr-2 text-secondary hover:text-heading text-xl leading-none"
17
+ aria-label="Close">&times;</button>
18
+ </div>
19
+
20
+ <%# Inline x-for pill — the demo's step counter lives in local x-data, so the
21
+ static-ERB progress_pill block doesn't fit. Real wizards driven by props
22
+ can render studio/modals/blocks/progress_pill directly. %>
23
+ <div class="flex items-center justify-center gap-1.5 mx-auto max-w-[200px] mb-6">
24
+ <template x-for="i in total" :key="i">
25
+ <div class="h-1.5 flex-1 rounded-full transition-colors duration-300"
26
+ :class="step >= i ? 'bg-primary' : 'bg-inset border border-subtle'"></div>
27
+ </template>
28
+ </div>
29
+
30
+ <template x-if="step === 1">
31
+ <div class="text-center space-y-3 min-h-[120px]">
32
+ <h4 class="text-heading font-semibold text-base">Welcome</h4>
33
+ <p class="text-sm text-secondary">
34
+ First step — collect any prerequisites or intro context here. Replace this
35
+ block with whatever your wizard's opening screen needs.
36
+ </p>
37
+ </div>
38
+ </template>
39
+
40
+ <template x-if="step === 2">
41
+ <div class="text-center space-y-3 min-h-[120px]">
42
+ <h4 class="text-heading font-semibold text-base">Configure</h4>
43
+ <p class="text-sm text-secondary">
44
+ Middle step(s) — forms, selections, anything that needs user input. Each step
45
+ gets its own <code class="text-xs bg-inset px-1 rounded">&lt;template x-if&gt;</code> block.
46
+ </p>
47
+ </div>
48
+ </template>
49
+
50
+ <template x-if="step === 3">
51
+ <div class="text-center space-y-3 min-h-[120px]">
52
+ <h4 class="text-heading font-semibold text-base">Confirm</h4>
53
+ <p class="text-sm text-secondary">
54
+ Final step — show the summary + primary commit action. The Finish button
55
+ below typically calls your real submit handler then closes the modal.
56
+ </p>
57
+ </div>
58
+ </template>
59
+
60
+ <button type="button"
61
+ @click="step < total ? step++ : $store.<%= modal_store %>.close()"
62
+ class="btn btn-primary btn-lg w-full mt-2"
63
+ x-text="step === total ? 'Finish' : 'Next'"></button>
64
+ <button type="button"
65
+ x-show="step > 1"
66
+ @click="step--"
67
+ class="block mx-auto mt-3 text-sm text-secondary hover:text-heading transition">
68
+ &larr; Back
69
+ </button>
70
+ <button type="button"
71
+ @click="$store.<%= modal_store %>.close()"
72
+ class="block mx-auto mt-2 text-xs text-muted hover:text-heading transition">
73
+ Close
74
+ </button>
75
+ </div>
@@ -0,0 +1,120 @@
1
+ <%# Modal specimen card for the living style guide (admin/style) Modals section.
2
+
3
+ Unlike the generic style/_specimen (Tricks), a modal specimen is INTERACTIVE:
4
+
5
+ - the WHOLE card is the trigger (role=button, Enter/Space) — click it to
6
+ open the real modal with the current toggle config (item 3).
7
+ - the header carries a small Copy that yields a CONVERSATIONAL reference
8
+ an agent can act on (not a raw JS blob) — the operator references this
9
+ page when chatting (item 1).
10
+ - an optional row of option toggles lets the user configure the modal
11
+ before opening; their clicks/keys do NOT bubble to the card open (item 2).
12
+ - an optional active-card GLOW (.studio-team-glow) that reactively lights
13
+ when this card represents the currently-open modal + step, and slides to
14
+ the next card as the step machine advances (items 5/6). The glow rides an
15
+ un-clipped wrapper (the card itself is overflow-hidden), mirroring TM's
16
+ holo-wrap / holo-card split, and fades via --studio-team-glow-opacity so
17
+ it transitions smoothly between cards.
18
+
19
+ Locals:
20
+ label (String, required) - the specimen name (header, uppercase).
21
+ reference (String, required) - the conversational copy text.
22
+ open_expr (String, required) - Alpine expression run to open the modal.
23
+ May read `opts.*` from card_data (the toggle state).
24
+ card_data (String, optional) - INNER x-data (no braces), merged after
25
+ copiedRef. E.g. "opts: { magicLink: true, google: true }".
26
+ glow_when (String, optional) - Alpine boolean expr; truthy => active glow.
27
+ toggles (Array<Hash{model:,label:}>, optional) - option checkboxes bound
28
+ into the card x-data (e.g. { model: 'opts.google', label: 'Google' }).
29
+ disabled (Boolean, optional) - capability off: greyed + badged.
30
+ openable (Boolean, optional) - with disabled, keep it clickable (preview).
31
+ disabled_label (String, optional) - the flag text.
32
+ Block: the mini-mock stage (a not-to-scale visual representation). %>
33
+ <%
34
+ label = local_assigns.fetch(:label)
35
+ # Developer-authored reference sentence, rendered as hidden text content (the
36
+ # Copy button reads textContent). html_safe keeps its quotes literal in the
37
+ # markup; it holds no HTML-special chars.
38
+ reference = local_assigns.fetch(:reference).to_s.html_safe
39
+ # open_expr / glow_when are developer-authored Alpine expressions (not user
40
+ # input); mark html_safe so their single quotes render literally into the
41
+ # attribute rather than as &#39; (cleaner HTML; the browser decodes either way).
42
+ open_expr = local_assigns.fetch(:open_expr).to_s.html_safe
43
+ card_data = local_assigns.fetch(:card_data, "").to_s.strip
44
+ glow_when = local_assigns[:glow_when]&.to_s&.html_safe
45
+ toggles = local_assigns[:toggles] || []
46
+ disabled = local_assigns[:disabled]
47
+ openable = local_assigns[:openable]
48
+ disabled_label = local_assigns[:disabled_label].presence || "disabled on this app"
49
+ clickable = !(disabled && !openable)
50
+ inner_cls = openable ? "opacity-60 grayscale" : "opacity-40 grayscale select-none pointer-events-none"
51
+ card_x_data = "{ copiedRef: false#{card_data.present? ? ", #{card_data}" : ''} }"
52
+ %>
53
+ <%# Wrapper — carries the active-card glow (un-clipped) + the shared x-data. %>
54
+ <%# h-full so the card stretches to its grid row's tallest (per-row equal
55
+ height via the grid's default align-items: stretch). %>
56
+ <div class="relative h-full<%= " studio-team-glow rounded-xl" if glow_when %>"
57
+ x-data="<%= card_x_data %>"
58
+ <% if glow_when %>:style="{ '--studio-team-glow-opacity': (<%= glow_when %>) ? '0.95' : '0' }"<% end %>>
59
+ <article
60
+ class="card overflow-hidden flex flex-col h-full<%= " opacity-95" if disabled %><%= " cursor-pointer" if clickable %>"
61
+ <%= 'aria-disabled="true"'.html_safe if disabled %>
62
+ <% if clickable %>
63
+ role="button" tabindex="0"
64
+ @click="<%= open_expr %>"
65
+ @keydown.enter.prevent="<%= open_expr %>"
66
+ @keydown.space.prevent="<%= open_expr %>"
67
+ aria-label="Open the <%= label %> modal"
68
+ <% end %>>
69
+
70
+ <%# Header — the centered TITLE is itself the copy control (a button + a small
71
+ clipboard icon that flips to a check). @click.stop + @keydown.stop keep the
72
+ copy from bubbling to the card's open handler: clicking the title copies
73
+ and does NOT open; clicking anywhere else on the card still opens. The
74
+ button activates on Enter/Space natively (firing @click.stop = copy). %>
75
+ <div class="flex items-center justify-center px-3 pt-3">
76
+ <button type="button"
77
+ class="inline-flex items-center gap-1.5 text-2xs font-semibold uppercase tracking-wider text-muted hover:text-heading transition"
78
+ title="Copy an agent-ready reference to this modal"
79
+ aria-label="Copy an agent-ready reference to this modal"
80
+ @click.stop="navigator.clipboard?.writeText($refs.ref.textContent.trim()); copiedRef = true; setTimeout(() => copiedRef = false, 1400)"
81
+ @keydown.stop>
82
+ <span><%= label %></span>
83
+ <svg x-show="!copiedRef" class="w-3 h-3 shrink-0" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24" aria-hidden="true">
84
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
85
+ </svg>
86
+ <svg x-show="copiedRef" x-cloak class="w-3 h-3 shrink-0" style="color: var(--color-success)" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24" aria-hidden="true">
87
+ <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
88
+ </svg>
89
+ <span class="sr-only" x-text="copiedRef ? 'Copied' : ''"></span>
90
+ </button>
91
+ </div>
92
+ <span x-ref="ref" class="hidden"><%= reference %></span>
93
+
94
+ <%# Stage — the not-to-scale mini-mock. %>
95
+ <div class="relative flex flex-1 flex-wrap items-center justify-center gap-4 p-6 min-h-32">
96
+ <% if disabled %>
97
+ <span class="absolute top-2 right-2 badge z-10" style="color: var(--color-warning); border-color: var(--color-warning)"><%= disabled_label %></span>
98
+ <div class="flex flex-wrap items-center justify-center gap-4 <%= inner_cls %>"><%= yield %></div>
99
+ <% else %>
100
+ <%= yield %>
101
+ <% end %>
102
+ <% if clickable %>
103
+ <span class="pointer-events-none absolute bottom-2 right-2 text-2xs text-secondary opacity-70">Open &nearr;</span>
104
+ <% end %>
105
+ </div>
106
+
107
+ <%# Toggles — configure the modal before opening (clicks/keys stay local). %>
108
+ <% if toggles.any? %>
109
+ <div class="border-t border-subtle px-3 py-2.5 flex flex-wrap gap-x-4 gap-y-1.5"
110
+ @click.stop @keydown.stop>
111
+ <% toggles.each do |tg| %>
112
+ <label class="flex items-center gap-1.5 text-2xs text-secondary cursor-pointer select-none">
113
+ <input type="checkbox" x-model="<%= tg[:model] %>" class="w-3.5 h-3.5 rounded accent-primary cursor-pointer">
114
+ <span><%= tg[:label] %></span>
115
+ </label>
116
+ <% end %>
117
+ </div>
118
+ <% end %>
119
+ </article>
120
+ </div>