studio-engine 0.67.1 → 0.68.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 +43 -0
- data/README.md +48 -0
- data/app/controllers/studio/knowledge_docs_controller.rb +5 -2
- data/app/models/studio/knowledge_doc.rb +15 -3
- data/app/views/studio/knowledge_docs/_browser.html.erb +1 -5
- data/app/views/studio/knowledge_docs/_chip_styles.html.erb +10 -0
- data/app/views/studio/knowledge_docs/show.html.erb +2 -8
- data/app/views/style/modals/_auth.html.erb +67 -44
- data/db/migrate/20260901000001_create_studio_knowledge_docs.rb +7 -2
- data/lib/studio/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc639657a629af209c7afe28ae5114aff1d6121a0c6e5fc456c0d7fec51a40b3
|
|
4
|
+
data.tar.gz: 4875b0355b16ea935f873f4dca59d1657a2e8b7e2b7b7ae17f79df80ea194e48
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 867c4b70c5c76e7619c6b4ed1d087a79ccb1cd3c9d99770f109c4c039d56372ca7835a3281d00bcc1fc076cb1759c700156959db4f29010e4fa5239b3a344944
|
|
7
|
+
data.tar.gz: 31eec34f181c1caec640f1085d97b3437bd6e0416e90ff4f4d23af29039b88406093ed42adbd11c973ef4a7687eeea76e208f5b57df88d25e66f37a937933bae
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,49 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **The auth modal's Solana button moved to solana-studio, behind a CREDENTIAL
|
|
10
|
+
SLOT.** `style/modals/_auth` no longer draws the button; it renders whatever
|
|
11
|
+
resolves at `solana_studio/auth/wallet_credential` and nothing when that path
|
|
12
|
+
is empty, so bundling the gem IS the registration and a web2 app carries no
|
|
13
|
+
wallet markup at all. This completes the two-template split begun in 0.67.0,
|
|
14
|
+
which moved the four web3 MODALS to the gem and left the sign-in button as the
|
|
15
|
+
last web3 markup in the base engine. The gem half has shipped since
|
|
16
|
+
solana-studio **0.5.2** — until now this engine referenced that partial
|
|
17
|
+
nowhere, so the gem shipped a button no host rendered.
|
|
18
|
+
**Supersedes the stranded PR #245**, whose consumer lane was red only because
|
|
19
|
+
it opened before solana-studio 0.5.2 published.
|
|
20
|
+
|
|
21
|
+
**What did NOT change, deliberately:** `_methodDefaults.wallet` is still
|
|
22
|
+
`Studio.auth_method?(:wallet) && Studio.feature?(:web3)`. The Ruby gate
|
|
23
|
+
answers "is it implemented" (is the picker registered, does the credential
|
|
24
|
+
partial resolve) and gates the RENDER; Alpine's `methodOn('wallet')` still
|
|
25
|
+
answers "should it show" and gates VISIBILITY, which keeps the style guide's
|
|
26
|
+
method toggles working. Folding policy into the Ruby gate would delete the
|
|
27
|
+
button from the DOM on a web3-off app that bundles the gem, and the "or"
|
|
28
|
+
divider — which reads `methodOn('wallet')` too — would then float above a
|
|
29
|
+
button that is not there.
|
|
30
|
+
|
|
31
|
+
Hosts need no change: the engine's auth modal is a style-guide specimen
|
|
32
|
+
reached through `/admin/style`, and an app that bundles solana-studio 0.5.2 or
|
|
33
|
+
later sees the same button in the same place.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- **Knowledge layer hardening** — the five findings from the 0.67.0 reviews:
|
|
38
|
+
upload keys carry a random suffix (same-named uploads in the same second no
|
|
39
|
+
longer overwrite the first object before the unique index can refuse);
|
|
40
|
+
`normalize_path` drops dot segments (`a/../b` → `a/b` — the S3 key mirrors
|
|
41
|
+
the path, and a `..` reads as traversal to every scanner); `access_for`
|
|
42
|
+
normalizes the queried agent the way writes normalize keys (a capitalized
|
|
43
|
+
`Studio.knowledge_agents` roster entry no longer reads `none`); the inbox
|
|
44
|
+
badge counts before the status filter (filtering to `filed` no longer zeroes
|
|
45
|
+
it); chip styles moved to one shared partial (`_chip_styles`); and reference
|
|
46
|
+
migrations are now written omakase-rubocop-compatible, with the convention
|
|
47
|
+
noted in the migration header (consumer copies are linted by the consumer's
|
|
48
|
+
rubocop — measured on the Industries adoption ship).
|
|
49
|
+
|
|
7
50
|
### Added
|
|
8
51
|
|
|
9
52
|
- **Knowledge layer primitive** — `Studio::KnowledgeDoc` + `/admin/knowledge`:
|
data/README.md
CHANGED
|
@@ -257,6 +257,54 @@ What the engine **keeps** is the SESSION half of Solana sign-in:
|
|
|
257
257
|
`Studio.wallet_sign_in_statement` — the single source for the signed statement,
|
|
258
258
|
which the gem's deep link reads so the two cannot drift.
|
|
259
259
|
|
|
260
|
+
#### The auth modal's credential slot
|
|
261
|
+
|
|
262
|
+
The **sign-in modal itself stays here** and is never forked: every app in the
|
|
263
|
+
ecosystem signs people in, and most of them are web2. What varies is *which
|
|
264
|
+
credentials it offers*. Google and magic-link are implemented by this engine and
|
|
265
|
+
render directly. The wallet button is implemented by the web3 layer, so it is
|
|
266
|
+
**contributed** — `style/modals/_auth` looks for a partial at one fixed path and
|
|
267
|
+
renders whatever it finds:
|
|
268
|
+
|
|
269
|
+
solana_studio/auth/_wallet_credential (shipped by the solana-studio gem)
|
|
270
|
+
|
|
271
|
+
Bundling the gem **is** the registration. There is no registry call and no
|
|
272
|
+
config flag; an app without that layer renders nothing there, so a web2 app
|
|
273
|
+
carries no wallet markup at all rather than markup hidden behind a flag. This is
|
|
274
|
+
the same optional-partial convention as `modals/_host_extras` above, and it uses
|
|
275
|
+
the same three-term `lookup_context.exists?`.
|
|
276
|
+
|
|
277
|
+
Copying the auth modal into the gem was rejected deliberately: it would fork a
|
|
278
|
+
surface both apps sign in through, and the two copies would drift — which is how
|
|
279
|
+
the wallet picker reached three copies before it was promoted.
|
|
280
|
+
|
|
281
|
+
**Two layers answer two different questions, and they are not merged:**
|
|
282
|
+
|
|
283
|
+
| Question | Answered by | Where it is decided |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| Is it **implemented**? | the picker is registered, **and** the credential partial resolves | Ruby, in `style/modals/_auth` — gates the render |
|
|
286
|
+
| Should it **show**? | `methodOn('wallet')`, falling back to `Studio.auth_method?(:wallet) && Studio.feature?(:web3)` | Alpine, inside the contributed partial — gates visibility |
|
|
287
|
+
|
|
288
|
+
Both terms of the Ruby gate are load-bearing, and they fail differently. Without
|
|
289
|
+
the **registration** term a layer that ships the credential but not the picker
|
|
290
|
+
draws a button that opens an empty panel — not hypothetical, solana-studio 0.5.2
|
|
291
|
+
shipped exactly that pair. Without the **existence** term an app whose picker
|
|
292
|
+
resolves but whose credential does not raises `Missing partial` in front of
|
|
293
|
+
someone signing in, instead of quietly rendering no button.
|
|
294
|
+
|
|
295
|
+
Policy stays **out** of the Ruby gate on purpose. Folding
|
|
296
|
+
`auth_method?(:wallet)` and `feature?(:web3)` into the render would delete the
|
|
297
|
+
button from the DOM on a web3-off app that bundles the gem — and the "or"
|
|
298
|
+
divider reads `methodOn('wallet')` too, so ticking Solana Wallet on the style
|
|
299
|
+
guide's Sign in card would then float a divider above a button that is not
|
|
300
|
+
there.
|
|
301
|
+
|
|
302
|
+
A contributed partial renders inside the modal's own Alpine scope, so it may use
|
|
303
|
+
`methodOn(...)`, `attested()` (the legal-age gate — call it, or wallet becomes
|
|
304
|
+
the one credential that skips attestation) and `props.submitting`. It receives
|
|
305
|
+
one local, `modal_store`: the engine's real host is `"modals"`, the living style
|
|
306
|
+
guide's page-scoped host is `"dsModals"`.
|
|
307
|
+
|
|
260
308
|
It also keeps two blocks the gem renders **by name** across the gem boundary:
|
|
261
309
|
`studio/modals/blocks/wallet_brand_sprite` and `studio/modals/blocks/card_header`.
|
|
262
310
|
Renaming either is a cross-repo change.
|
|
@@ -25,11 +25,14 @@ module Studio
|
|
|
25
25
|
|
|
26
26
|
scope = Studio::KnowledgeDoc.order(created_at: :desc)
|
|
27
27
|
scope = scope.for_entity(@entity) if @entity
|
|
28
|
-
scope = scope.where(status: @status) if @status
|
|
29
28
|
|
|
30
|
-
@entities
|
|
29
|
+
@entities = Studio::KnowledgeDoc.distinct.pluck(:entity).sort
|
|
30
|
+
# Counted BEFORE the status filter: the badge answers "what waits for
|
|
31
|
+
# triage in this entity", and filtering to status=filed must not zero it.
|
|
31
32
|
@inbox_size = scope.inbox.count
|
|
32
33
|
|
|
34
|
+
scope = scope.where(status: @status) if @status
|
|
35
|
+
|
|
33
36
|
if @view == "folders"
|
|
34
37
|
@folders = scope.folders_under(@folder)
|
|
35
38
|
@docs = scope.in_folder(@folder)
|
|
@@ -93,8 +93,13 @@ module Studio
|
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
# "/a//b/" -> "a/b". Nil-safe; the empty string is the root folder.
|
|
96
|
+
# Dot segments are dropped ("a/../b" -> "a/b"): harmless server-side, but
|
|
97
|
+
# the S3 key mirrors the path and a ".." reads as traversal to every
|
|
98
|
+
# scanner and human who ever greps the bucket.
|
|
96
99
|
def normalize_path(value)
|
|
97
|
-
value.to_s.strip.squeeze("/").
|
|
100
|
+
value.to_s.strip.squeeze("/").split("/")
|
|
101
|
+
.reject { |segment| segment.empty? || segment == "." || segment == ".." }
|
|
102
|
+
.join("/")
|
|
98
103
|
end
|
|
99
104
|
|
|
100
105
|
def default_title(file)
|
|
@@ -105,8 +110,11 @@ module Studio
|
|
|
105
110
|
|
|
106
111
|
# --- access ---------------------------------------------------------------
|
|
107
112
|
|
|
113
|
+
# The queried agent is normalized the same way written keys are, so a
|
|
114
|
+
# capitalized Studio.knowledge_agents roster entry reads its real level
|
|
115
|
+
# instead of silently defaulting to "none".
|
|
108
116
|
def access_for(agent)
|
|
109
|
-
level = access.is_a?(Hash) ? access[agent.to_s] : nil
|
|
117
|
+
level = access.is_a?(Hash) ? access[agent.to_s.strip.downcase] : nil
|
|
110
118
|
ACCESS_LEVELS.include?(level) ? level : "none"
|
|
111
119
|
end
|
|
112
120
|
|
|
@@ -133,9 +141,13 @@ module Studio
|
|
|
133
141
|
content_type = file.respond_to?(:content_type) ? file.content_type : nil
|
|
134
142
|
body = file.respond_to?(:read) ? file.read : file.to_s
|
|
135
143
|
|
|
144
|
+
# The random suffix is load-bearing: the timestamp is second-granularity,
|
|
145
|
+
# so two same-named uploads in one second would otherwise write the same
|
|
146
|
+
# key — the second S3 PUT overwrites the first object BEFORE the unique
|
|
147
|
+
# index rejects the second row (found in PR #251 review, twice over).
|
|
136
148
|
key = [
|
|
137
149
|
"knowledge", entity, path.presence,
|
|
138
|
-
"#{Time.current.strftime('%Y%m%d%H%M%S')}-#{self.class.sanitize_filename(filename)}"
|
|
150
|
+
"#{Time.current.strftime('%Y%m%d%H%M%S')}-#{SecureRandom.hex(4)}-#{self.class.sanitize_filename(filename)}"
|
|
139
151
|
].compact.join("/")
|
|
140
152
|
|
|
141
153
|
Studio::S3.upload(key: key, body: body, content_type: content_type)
|
|
@@ -11,12 +11,8 @@
|
|
|
11
11
|
Styling note: gem-specific looks live in the scoped <style> below because a
|
|
12
12
|
host's Tailwind build may not scan this gem's views (same reasoning as the
|
|
13
13
|
geo grid). Layout leans on utilities every host already ships. %>
|
|
14
|
+
<%= render "studio/knowledge_docs/chip_styles" %>
|
|
14
15
|
<style>
|
|
15
|
-
.knowledge-chip { display: inline-block; padding: 0 .5rem; border-radius: 9999px;
|
|
16
|
-
font-size: .7rem; line-height: 1.4rem; border: 1px solid transparent; white-space: nowrap; }
|
|
17
|
-
.knowledge-chip-full { background: rgb(16 185 129 / .12); border-color: rgb(16 185 129 / .4); color: rgb(5 150 105); }
|
|
18
|
-
.knowledge-chip-aware { background: rgb(245 158 11 / .12); border-color: rgb(245 158 11 / .4); color: rgb(180 83 9); }
|
|
19
|
-
.knowledge-chip-none { background: rgb(148 163 184 / .12); border-color: rgb(148 163 184 / .4); color: rgb(100 116 139); }
|
|
20
16
|
.knowledge-status { font-size: .7rem; padding: 0 .45rem; border-radius: .25rem; border: 1px solid rgb(148 163 184 / .5); }
|
|
21
17
|
.knowledge-status-inbox { border-color: rgb(245 158 11 / .6); color: rgb(180 83 9); }
|
|
22
18
|
.knowledge-status-superseded { text-decoration: line-through; opacity: .6; }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%# The access-chip look, shared by the browser and the show page — one
|
|
2
|
+
definition so the two can never drift. Scoped <style> rather than utility
|
|
3
|
+
classes because a host's Tailwind build may not scan this gem's views. %>
|
|
4
|
+
<style>
|
|
5
|
+
.knowledge-chip { display: inline-block; padding: 0 .5rem; border-radius: 9999px;
|
|
6
|
+
font-size: .7rem; line-height: 1.4rem; border: 1px solid transparent; white-space: nowrap; }
|
|
7
|
+
.knowledge-chip-full { background: rgb(16 185 129 / .12); border-color: rgb(16 185 129 / .4); color: rgb(5 150 105); }
|
|
8
|
+
.knowledge-chip-aware { background: rgb(245 158 11 / .12); border-color: rgb(245 158 11 / .4); color: rgb(180 83 9); }
|
|
9
|
+
.knowledge-chip-none { background: rgb(148 163 184 / .12); border-color: rgb(148 163 184 / .4); color: rgb(100 116 139); }
|
|
10
|
+
</style>
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
<%# /admin/knowledge/:id — one document: metadata, access, download, and the
|
|
2
2
|
triage form that files it out of the inbox. %>
|
|
3
|
-
|
|
4
|
-
.knowledge-chip { display: inline-block; padding: 0 .5rem; border-radius: 9999px;
|
|
5
|
-
font-size: .7rem; line-height: 1.4rem; border: 1px solid transparent; white-space: nowrap; }
|
|
6
|
-
.knowledge-chip-full { background: rgb(16 185 129 / .12); border-color: rgb(16 185 129 / .4); color: rgb(5 150 105); }
|
|
7
|
-
.knowledge-chip-aware { background: rgb(245 158 11 / .12); border-color: rgb(245 158 11 / .4); color: rgb(180 83 9); }
|
|
8
|
-
.knowledge-chip-none { background: rgb(148 163 184 / .12); border-color: rgb(148 163 184 / .4); color: rgb(100 116 139); }
|
|
9
|
-
</style>
|
|
3
|
+
<%= render "studio/knowledge_docs/chip_styles" %>
|
|
10
4
|
|
|
11
5
|
<div class="mb-4 text-sm">
|
|
12
6
|
<%= link_to "← Knowledge", admin_knowledge_path(folder: @doc.path.presence) %>
|
|
@@ -29,7 +23,7 @@
|
|
|
29
23
|
<div><dt class="opacity-60">Document date</dt><dd><%= @doc.document_date&.iso8601 || "—" %></dd></div>
|
|
30
24
|
<div><dt class="opacity-60">Uploaded</dt><dd><%= @doc.created_at&.to_date&.iso8601 %> by <%= @doc.uploaded_by.presence || "—" %></dd></div>
|
|
31
25
|
<div><dt class="opacity-60">Source</dt><dd><%= @doc.source_note.presence || "—" %></dd></div>
|
|
32
|
-
<div><dt class="opacity-60">File</dt><dd><%= @doc.file? ?
|
|
26
|
+
<div><dt class="opacity-60">File</dt><dd><%= @doc.file? ? [ @doc.mime_type.presence || "file", (number_to_human_size(@doc.byte_size) if @doc.byte_size) ].compact.join(" · ") : "metadata only" %></dd></div>
|
|
33
27
|
<div class="md:col-span-2"><dt class="opacity-60">Access</dt><dd><%= render "studio/knowledge_docs/access_chips", doc: @doc %></dd></div>
|
|
34
28
|
</dl>
|
|
35
29
|
|
|
@@ -18,26 +18,69 @@
|
|
|
18
18
|
No real auth happens — the demo shows the real UI.
|
|
19
19
|
|
|
20
20
|
LOCALS:
|
|
21
|
-
web3_gem (Boolean, required) — does solana-studio resolve
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
on purpose: a caller that forgets it should fail loudly here rather than
|
|
21
|
+
web3_gem (Boolean, required) — does solana-studio resolve, meaning is the
|
|
22
|
+
wallet-connect picker on the view path. style/_modals owns the lookup and
|
|
23
|
+
registers wallet-connect behind the same flag. Fetched without a default on
|
|
24
|
+
purpose: a caller that forgets it should fail loudly here rather than
|
|
26
25
|
silently drop Solana sign-in from an app that has the gem.
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
app
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
THE CREDENTIAL SLOT. Google and magic-link are implemented by this engine, so
|
|
28
|
+
they render below directly. Wallet is not: it belongs to the web3 bolt-on, and
|
|
29
|
+
a base-template app — a newsletter, a hub — must not carry markup it can never
|
|
30
|
+
render. So the wallet button is CONTRIBUTED by whichever layer implements it,
|
|
31
|
+
as a partial this file merely looks for:
|
|
32
|
+
|
|
33
|
+
solana_studio/auth/wallet_credential
|
|
34
|
+
|
|
35
|
+
Bundling the gem IS the registration. An app without that layer renders
|
|
36
|
+
nothing here and carries no wallet markup at all, rather than shipping a
|
|
37
|
+
button hidden behind a flag.
|
|
38
|
+
|
|
39
|
+
TWO LAYERS ANSWER TWO DIFFERENT QUESTIONS, and they are deliberately NOT
|
|
40
|
+
merged into one:
|
|
41
|
+
|
|
42
|
+
Ruby, below IS IT IMPLEMENTED. Is the picker this button swaps to
|
|
43
|
+
registered (web3_gem), and does a layer actually ship the
|
|
44
|
+
credential partial. This gates the RENDER.
|
|
45
|
+
Alpine, in the SHOULD IT SHOW. methodOn('wallet'), which reads
|
|
46
|
+
contributed props.methods first so the Sign in card's toggle overrides
|
|
47
|
+
partial the server default outright. This gates VISIBILITY.
|
|
48
|
+
|
|
49
|
+
KEEPING POLICY OUT OF THE RUBY GATE IS LOAD-BEARING, and it is why
|
|
50
|
+
auth_method?(:wallet) and feature?(:web3) are NOT terms in the render gate.
|
|
51
|
+
Fold them in and the button leaves the DOM on a web3-off app that bundles the
|
|
52
|
+
gem — and then ticking Solana Wallet on the Sign in card turns the divider
|
|
53
|
+
below on above a button that is not there, because the divider reads
|
|
54
|
+
methodOn('wallet') too. Measured: that is the exact failure this slot was
|
|
55
|
+
meant to remove, reintroduced one layer up.
|
|
56
|
+
|
|
57
|
+
NOT gated on the slot, deliberately: _methodDefaults.wallet below. It stays
|
|
58
|
+
the app's own policy answer and is unchanged by the move. It is the fallback
|
|
59
|
+
methodOn uses when an opener passes no methods hash, and on a base app nothing
|
|
60
|
+
reaches it — the Sign in card always passes explicit booleans, and the only
|
|
61
|
+
opener that omits them is the wallet picker's back button, which needs the gem
|
|
62
|
+
to exist at all.
|
|
35
63
|
|
|
36
64
|
CRITICAL: rendered inside <template x-if="id==='auth'"> — Alpine requires a
|
|
37
65
|
SINGLE root, so everything lives inside the outer <div>. The x-data is a
|
|
38
66
|
double-quoted attribute: keep it free of double-quotes and backticks.
|
|
39
67
|
%>
|
|
40
68
|
<% web3_gem = local_assigns.fetch(:web3_gem) %>
|
|
69
|
+
<%# BOTH terms are load-bearing, and they fail differently.
|
|
70
|
+
|
|
71
|
+
web3_gem is the picker this button swaps to actually REGISTERED. A layer
|
|
72
|
+
that ships the credential partial without the picker draws a button that
|
|
73
|
+
opens an empty panel — not hypothetical: solana-studio 0.5.2 shipped
|
|
74
|
+
exactly that pair, the credential without wallet_connect.
|
|
75
|
+
exists? does a layer ship the button AT ALL. Without it, an app whose
|
|
76
|
+
picker resolves but whose credential does not raises Missing partial in
|
|
77
|
+
front of someone signing in, instead of rendering no button.
|
|
78
|
+
|
|
79
|
+
Same three-term lookup_context.exists? the modal host uses for host_extras
|
|
80
|
+
(studio/modals/_host.html.erb): name, prefixes, partial. %>
|
|
81
|
+
<% wallet_credential = "solana_studio/auth/wallet_credential" %>
|
|
82
|
+
<% wallet_credential_available = web3_gem &&
|
|
83
|
+
lookup_context.exists?("wallet_credential", ["solana_studio/auth"], true) %>
|
|
41
84
|
<div x-data="{
|
|
42
85
|
email: '',
|
|
43
86
|
ageAttested: false,
|
|
@@ -85,10 +128,6 @@
|
|
|
85
128
|
if (pp) pp.submitting = null;
|
|
86
129
|
}, 1200);
|
|
87
130
|
},
|
|
88
|
-
openWalletHub() {
|
|
89
|
-
if (!this.attested()) return;
|
|
90
|
-
Alpine.store('dsModals').swap('wallet-connect', { backTo: 'auth', ageAttested: true });
|
|
91
|
-
},
|
|
92
131
|
async submitMagicLink() {
|
|
93
132
|
if (!this.attested()) return;
|
|
94
133
|
var p = this.props;
|
|
@@ -184,35 +223,19 @@
|
|
|
184
223
|
<p role="alert" class="text-red-400 text-xs -mt-2 mb-3" x-text="props.googleError"></p>
|
|
185
224
|
</template>
|
|
186
225
|
|
|
187
|
-
<%# 2.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
server default outright.
|
|
194
|
-
Ruby web3_gem CAPABILITY — does solana-studio resolve, so
|
|
195
|
-
that something actually REGISTERED the wallet-connect id this button
|
|
196
|
-
swaps to. style/_modals wraps that registration in the same gate.
|
|
226
|
+
<%# 2. Wallet — CONTRIBUTED, not written here. See the credential-slot note
|
|
227
|
+
at the top of this file. The partial owns the button, its brand mark
|
|
228
|
+
and its click handler; this engine owns only the decision to look for
|
|
229
|
+
it. An app with no web3 layer on its view path renders nothing at all
|
|
230
|
+
here, which is the whole point: the base template ships no wallet
|
|
231
|
+
markup.
|
|
197
232
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
<% if
|
|
203
|
-
|
|
204
|
-
class="btn btn-neutral btn-lg w-full gap-3 mb-3 disabled:cursor-wait">
|
|
205
|
-
<svg width="18" height="14" viewBox="0 0 397 311" fill="none" x-show="props.submitting !== 'wallet'" aria-hidden="true">
|
|
206
|
-
<defs><linearGradient id="auth-solana-grad" x1="361" y1="-9" x2="153" y2="389" gradientUnits="userSpaceOnUse">
|
|
207
|
-
<stop offset="0" stop-color="#00FFA3"/><stop offset="1" stop-color="#DC1FFF"/>
|
|
208
|
-
</linearGradient></defs>
|
|
209
|
-
<path d="M65 234c2-2 6-4 9-4h317c6 0 9 7 5 11l-63 63c-2 2-6 4-9 4H6c-6 0-9-7-5-11l64-63z" fill="url(#auth-solana-grad)"/>
|
|
210
|
-
<path d="M65 4c2-2 6-4 9-4h317c6 0 9 7 5 11l-63 63c-2 2-6 4-9 4H6c-6 0-9-7-5-11L65 4z" fill="url(#auth-solana-grad)"/>
|
|
211
|
-
<path d="M333 119c-2-2-6-4-9-4H7c-6 0-9 7-5 11l63 63c2 2 6 4 9 4h317c6 0 9-7 5-11l-63-63z" fill="url(#auth-solana-grad)"/>
|
|
212
|
-
</svg>
|
|
213
|
-
<span x-show="props.submitting === 'wallet'" class="spinner" aria-hidden="true"></span>
|
|
214
|
-
<span x-text="props.submitting === 'wallet' ? 'Connecting…' : 'Solana'"></span>
|
|
215
|
-
</button>
|
|
233
|
+
Visibility is still Alpine's. The contributed partial carries
|
|
234
|
+
x-show methodOn('wallet'), so the Sign in card's toggle drives it
|
|
235
|
+
exactly as it drove the button that used to live here, and the divider
|
|
236
|
+
below can never float above a button the DOM is missing. %>
|
|
237
|
+
<% if wallet_credential_available %>
|
|
238
|
+
<%= render wallet_credential, modal_store: "dsModals" %>
|
|
216
239
|
<% end %>
|
|
217
240
|
|
|
218
241
|
<%# "or" divider — only when magic-link AND a social method are both on. %>
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
# (`bin/rails studio_engine:install:migrations && bin/rails db:migrate`) so the
|
|
4
4
|
# table is created in the app's database. Do not hand-copy — a hand copy
|
|
5
5
|
# collides with the installed copy on `class CreateStudioKnowledgeDocs`.
|
|
6
|
+
#
|
|
7
|
+
# STYLE NOTE: reference migrations are written omakase-rubocop-compatible
|
|
8
|
+
# (spaces inside array brackets) because the installed copy is linted by the
|
|
9
|
+
# CONSUMER's rubocop, not this gem's — an engine-styled copy fails the
|
|
10
|
+
# consumer's cert untouched (measured on the Industries adoption ship).
|
|
6
11
|
class CreateStudioKnowledgeDocs < ActiveRecord::Migration[7.2]
|
|
7
12
|
def change
|
|
8
13
|
create_table :studio_knowledge_docs do |t|
|
|
@@ -41,8 +46,8 @@ class CreateStudioKnowledgeDocs < ActiveRecord::Migration[7.2]
|
|
|
41
46
|
t.timestamps
|
|
42
47
|
end
|
|
43
48
|
|
|
44
|
-
add_index :studio_knowledge_docs, [:entity, :status]
|
|
45
|
-
add_index :studio_knowledge_docs, [:entity, :path]
|
|
49
|
+
add_index :studio_knowledge_docs, [ :entity, :status ]
|
|
50
|
+
add_index :studio_knowledge_docs, [ :entity, :path ]
|
|
46
51
|
add_index :studio_knowledge_docs, :s3_key, unique: true
|
|
47
52
|
add_index :studio_knowledge_docs, :superseded_by_id
|
|
48
53
|
end
|
data/lib/studio/version.rb
CHANGED
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.
|
|
4
|
+
version: 0.68.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
@@ -440,6 +440,7 @@ files:
|
|
|
440
440
|
- app/views/studio/geo_settings/edit.html.erb
|
|
441
441
|
- app/views/studio/knowledge_docs/_access_chips.html.erb
|
|
442
442
|
- app/views/studio/knowledge_docs/_browser.html.erb
|
|
443
|
+
- app/views/studio/knowledge_docs/_chip_styles.html.erb
|
|
443
444
|
- app/views/studio/knowledge_docs/_doc_row.html.erb
|
|
444
445
|
- app/views/studio/knowledge_docs/_upload_form.html.erb
|
|
445
446
|
- app/views/studio/knowledge_docs/index.html.erb
|