studio-engine 0.67.0 → 0.67.2
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 +15 -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.html.erb +38 -8
- data/app/views/style/modals/_auth.html.erb +47 -15
- data/db/migrate/20260901000001_create_studio_knowledge_docs.rb +7 -2
- data/lib/studio/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79895e24dd5ade937ec8c0b4afacd84605ad495530a4c37f94e053a2c1040170
|
|
4
|
+
data.tar.gz: 0f1716ef07d10fc5388ea15853767f6c7e8911e076e280a096a8424a494eba9c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7c6e391302c996753b399019f2baeded267820a69d1f7451fe7ff68f82609fc625cc477cbdebb6127f74d0d5ed3a326ed4357d02c24b595e0fd1c213c3605895
|
|
7
|
+
data.tar.gz: 26bda400a871f3998a7baa64c72f43ed71a2d6d4ff177e02c4c6ae76d9bff65438e0bb33d48564802ea982bc2f628631906fc25e0a24167ec11d38796908310d
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **Knowledge layer hardening** — the five findings from the 0.67.0 reviews:
|
|
10
|
+
upload keys carry a random suffix (same-named uploads in the same second no
|
|
11
|
+
longer overwrite the first object before the unique index can refuse);
|
|
12
|
+
`normalize_path` drops dot segments (`a/../b` → `a/b` — the S3 key mirrors
|
|
13
|
+
the path, and a `..` reads as traversal to every scanner); `access_for`
|
|
14
|
+
normalizes the queried agent the way writes normalize keys (a capitalized
|
|
15
|
+
`Studio.knowledge_agents` roster entry no longer reads `none`); the inbox
|
|
16
|
+
badge counts before the status filter (filtering to `filed` no longer zeroes
|
|
17
|
+
it); chip styles moved to one shared partial (`_chip_styles`); and reference
|
|
18
|
+
migrations are now written omakase-rubocop-compatible, with the convention
|
|
19
|
+
noted in the migration header (consumer copies are linted by the consumer's
|
|
20
|
+
rubocop — measured on the Industries adoption ship).
|
|
21
|
+
|
|
7
22
|
### Added
|
|
8
23
|
|
|
9
24
|
- **Knowledge layer primitive** — `Studio::KnowledgeDoc` + `/admin/knowledge`:
|
|
@@ -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
|
|
|
@@ -76,9 +76,35 @@
|
|
|
76
76
|
# Auth method-toggle defaults — mirror the app's Studio.auth_method? config;
|
|
77
77
|
# Solana Wallet also needs the web3 capability, so it defaults OFF on an app
|
|
78
78
|
# (like McRitchie Studio) that ships web3 off.
|
|
79
|
+
#
|
|
80
|
+
# web3_gem is the THIRD term, and it answers a question neither of the other
|
|
81
|
+
# two asks. auth_methods declares which CREDENTIALS an app accepts; features
|
|
82
|
+
# gates PRODUCT SURFACES — lib/studio.rb spells that split out where it
|
|
83
|
+
# deliberately does NOT gate the /auth/solana routes on :web3. Both are
|
|
84
|
+
# POLICY. Whether the wallet picker can RENDER AT ALL is template resolution,
|
|
85
|
+
# and the Solana button does nothing except swap to that picker. A trigger
|
|
86
|
+
# must carry at least the gate its target's REGISTRATION carries (see
|
|
87
|
+
# web3_gem above, which wraps the wallet-connect registration) or it opens an
|
|
88
|
+
# empty panel — the same failure the three specimen cards had, through a
|
|
89
|
+
# different door.
|
|
79
90
|
ml_default = Studio.auth_method?(:magic_link)
|
|
80
91
|
g_default = Studio.auth_method?(:google)
|
|
81
|
-
w_default = Studio.auth_method?(:wallet) && web3_on
|
|
92
|
+
w_default = Studio.auth_method?(:wallet) && web3_on && web3_gem
|
|
93
|
+
|
|
94
|
+
# The Sign in card's toggles. Solana Wallet is DROPPED where the gem is
|
|
95
|
+
# absent, because that checkbox is the door this page actually opens:
|
|
96
|
+
# methodOn('wallet') reads props.methods.wallet — the toggle's own boolean —
|
|
97
|
+
# BEFORE it ever falls back to the defaults above, so a ticked box overrides
|
|
98
|
+
# w_default outright. Leaving it offered would keep the empty panel one click
|
|
99
|
+
# away in EVERY base-app configuration, including the stock auth_methods
|
|
100
|
+
# default that declares no :wallet at all. A control that cannot do its one
|
|
101
|
+
# job is worse than an absent one; the section prose says why it is gone.
|
|
102
|
+
auth_toggles = [
|
|
103
|
+
{ model: "opts.magicLink", label: "Magic Link" },
|
|
104
|
+
{ model: "opts.google", label: "Google" }
|
|
105
|
+
]
|
|
106
|
+
auth_toggles << { model: "opts.wallet", label: "Solana Wallet" } if web3_gem
|
|
107
|
+
auth_toggles << { model: "opts.terms", label: "Terms" }
|
|
82
108
|
|
|
83
109
|
# Active-card glow-match expression builder: yields an Alpine boolean that is
|
|
84
110
|
# true when $store.dsModals.current() is THIS specimen's modal. It discriminates
|
|
@@ -365,8 +391,12 @@
|
|
|
365
391
|
:class="$store.dsModals.cardClasses()">
|
|
366
392
|
|
|
367
393
|
<%# --- Auth suite --- %>
|
|
394
|
+
<%# web3_gem is passed, not recomputed: this file owns the lookup (see
|
|
395
|
+
above) and the auth modal's Solana button needs the SAME answer,
|
|
396
|
+
because that button's only job is to swap to the wallet-connect
|
|
397
|
+
registration gated on it a few lines down. One lookup, two readers. %>
|
|
368
398
|
<template x-if="$store.dsModals.current().id === 'auth'">
|
|
369
|
-
<div><%= render "style/modals/auth" %></div>
|
|
399
|
+
<div><%= render "style/modals/auth", web3_gem: web3_gem %></div>
|
|
370
400
|
</template>
|
|
371
401
|
|
|
372
402
|
<%# --- Onboarding step (its card now lives in Profile) --- %>
|
|
@@ -629,6 +659,11 @@
|
|
|
629
659
|
magic-link-sent → magic-link-resent</code>. Toggle the methods below a
|
|
630
660
|
card, then open it to watch them gate the modal. The live card
|
|
631
661
|
<strong>glows</strong>, and the glow follows the step machine as you advance.
|
|
662
|
+
<% unless web3_gem %>
|
|
663
|
+
<strong>Solana sign-in needs solana-studio, which this app does not
|
|
664
|
+
bundle</strong>, so that button and its toggle are absent here —
|
|
665
|
+
nothing registers the wallet picker the button opens.
|
|
666
|
+
<% end %>
|
|
632
667
|
</p>
|
|
633
668
|
</div>
|
|
634
669
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
@@ -639,12 +674,7 @@
|
|
|
639
674
|
card_data: "opts: { magicLink: #{ml_default}, google: #{g_default}, wallet: #{w_default}, terms: true }",
|
|
640
675
|
open_expr: "$store.dsModals.open('auth', { step: 'credentials', picksRequired: 6, methods: { magicLink: opts.magicLink, google: opts.google, wallet: opts.wallet }, terms: opts.terms })",
|
|
641
676
|
glow_when: ds_glow.call("auth", step: "credentials"),
|
|
642
|
-
toggles:
|
|
643
|
-
{ model: "opts.magicLink", label: "Magic Link" },
|
|
644
|
-
{ model: "opts.google", label: "Google" },
|
|
645
|
-
{ model: "opts.wallet", label: "Solana Wallet" },
|
|
646
|
-
{ model: "opts.terms", label: "Terms" }
|
|
647
|
-
] } do %>
|
|
677
|
+
toggles: auth_toggles } do %>
|
|
648
678
|
<div class="pointer-events-none w-40 rounded-lg bg-surface border border-subtle shadow p-4 space-y-2 text-center">
|
|
649
679
|
<span class="block h-2 w-16 mx-auto rounded" style="background: var(--color-text); opacity: .18"></span>
|
|
650
680
|
<span class="block h-6 w-full rounded border" style="border-color: var(--color-border-strong)"></span>
|
|
@@ -17,10 +17,27 @@
|
|
|
17
17
|
resets; Solana swaps to the wallet-connect picker (a real ported specimen).
|
|
18
18
|
No real auth happens — the demo shows the real UI.
|
|
19
19
|
|
|
20
|
+
LOCALS:
|
|
21
|
+
web3_gem (Boolean, required) — does solana-studio resolve. style/_modals
|
|
22
|
+
owns the lookup and registers wallet-connect behind the same flag; the
|
|
23
|
+
Solana button below is rendered only when it is true, because a button that
|
|
24
|
+
swaps to an unregistered id opens an empty panel. Fetched without a default
|
|
25
|
+
on purpose: a caller that forgets it should fail loudly here rather than
|
|
26
|
+
silently drop Solana sign-in from an app that has the gem.
|
|
27
|
+
|
|
28
|
+
NOT gated on web3_gem, deliberately: _methodDefaults.wallet below. It is the
|
|
29
|
+
fallback methodOn uses when an opener passes no methods hash, and on a base
|
|
30
|
+
app nothing reaches it — the Sign in card always passes explicit booleans, and
|
|
31
|
+
the only opener that omits them is the wallet picker's back button, which
|
|
32
|
+
needs the gem to exist at all. Adding the term there would read like a third
|
|
33
|
+
gate while changing no rendered outcome and no behaviour, and a gate that
|
|
34
|
+
cannot be shown to bite is decoration. The trigger below is the real gate.
|
|
35
|
+
|
|
20
36
|
CRITICAL: rendered inside <template x-if="id==='auth'"> — Alpine requires a
|
|
21
37
|
SINGLE root, so everything lives inside the outer <div>. The x-data is a
|
|
22
38
|
double-quoted attribute: keep it free of double-quotes and backticks.
|
|
23
39
|
%>
|
|
40
|
+
<% web3_gem = local_assigns.fetch(:web3_gem) %>
|
|
24
41
|
<div x-data="{
|
|
25
42
|
email: '',
|
|
26
43
|
ageAttested: false,
|
|
@@ -167,21 +184,36 @@
|
|
|
167
184
|
<p role="alert" class="text-red-400 text-xs -mt-2 mb-3" x-text="props.googleError"></p>
|
|
168
185
|
</template>
|
|
169
186
|
|
|
170
|
-
<%# 2. Solana — progresses to the Connect Wallet picker modal.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
187
|
+
<%# 2. Solana — progresses to the Connect Wallet picker modal. TWO gates,
|
|
188
|
+
asking two different questions, and only one of them is Alpine's:
|
|
189
|
+
|
|
190
|
+
x-show methodOn('wallet') POLICY — does this app offer wallet as a
|
|
191
|
+
credential, and has the specimen card's toggle turned it on. Reads
|
|
192
|
+
props.methods.wallet first, so the card's checkbox overrides the
|
|
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.
|
|
197
|
+
|
|
198
|
+
The policy gate cannot stand in for the capability one: a base app can
|
|
199
|
+
answer yes to the first and no to the second, and then the button opens
|
|
200
|
+
an empty panel. It has to be the RUBY gate because the toggle can flip
|
|
201
|
+
every Alpine term, and a control the operator can tick is not a gate. %>
|
|
202
|
+
<% if web3_gem %>
|
|
203
|
+
<button @click="openWalletHub()" x-show="methodOn('wallet')" :disabled="!!props.submitting"
|
|
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>
|
|
216
|
+
<% end %>
|
|
185
217
|
|
|
186
218
|
<%# "or" divider — only when magic-link AND a social method are both on. %>
|
|
187
219
|
<div class="relative my-4" x-show="methodOn('magicLink') && (methodOn('google') || methodOn('wallet'))">
|
|
@@ -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,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.67.
|
|
4
|
+
version: 0.67.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -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
|