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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +67 -0
- data/app/assets/tailwind/studio_engine/engine-motion.css +620 -5
- data/app/controllers/style_controller.rb +25 -0
- data/app/views/studio/modals/_crop_photo.html.erb +5 -1
- data/app/views/studio/modals/_host.html.erb +5 -0
- data/app/views/studio/modals/_image_upload.html.erb +16 -5
- data/app/views/studio/modals/_saving.html.erb +6 -2
- data/app/views/studio/modals/auth/_resend_footer.html.erb +25 -0
- data/app/views/studio/modals/blocks/_card_header.html.erb +83 -0
- data/app/views/studio/modals/blocks/_cta_redirect.html.erb +73 -0
- data/app/views/studio/modals/blocks/_onchain_success.html.erb +52 -0
- data/app/views/studio/modals/blocks/_progress_pill.html.erb +23 -0
- data/app/views/studio/modals/blocks/_shell.html.erb +35 -0
- data/app/views/studio/modals/blocks/_solana_tx_link.html.erb +42 -0
- data/app/views/studio/modals/shared/_age_attestation.html.erb +34 -0
- data/app/views/studio/modals/shared/_email_field.html.erb +70 -0
- data/app/views/studio/modals/templates/_action.html.erb +25 -0
- data/app/views/studio/modals/templates/_form.html.erb +25 -0
- data/app/views/studio/modals/templates/_status.html.erb +45 -0
- data/app/views/studio/modals/templates/_success.html.erb +23 -0
- data/app/views/studio/modals/templates/_wizard.html.erb +75 -0
- data/app/views/style/_modal_specimen.html.erb +120 -0
- data/app/views/style/_modals.html.erb +690 -0
- data/app/views/style/_specimen.html.erb +64 -0
- data/app/views/style/_tasks.html.erb +112 -0
- data/app/views/style/_theme.html.erb +222 -0
- data/app/views/style/_tricks.html.erb +323 -0
- data/app/views/style/index.html.erb +60 -0
- data/app/views/style/modals/_auth.html.erb +252 -0
- data/app/views/style/modals/_onchain_tx.html.erb +62 -0
- data/app/views/style/modals/_wallet_connect.html.erb +138 -0
- data/app/views/style/modals/_wallet_deposit.html.erb +56 -0
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +27 -1
- metadata +32 -6
- data/app/controllers/design_system_controller.rb +0 -12
- data/app/views/design_system/_specimen.html.erb +0 -27
- 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">×</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"><template x-if></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
|
+
← 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 ' (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 ↗</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>
|