studio-engine 0.66.1 → 0.67.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.
@@ -0,0 +1,87 @@
1
+ <%# /admin/knowledge/:id — one document: metadata, access, download, and the
2
+ triage form that files it out of the inbox. %>
3
+ <style>
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>
10
+
11
+ <div class="mb-4 text-sm">
12
+ <%= link_to "← Knowledge", admin_knowledge_path(folder: @doc.path.presence) %>
13
+ </div>
14
+
15
+ <div class="flex flex-wrap items-start justify-between gap-3 mb-4">
16
+ <div>
17
+ <h1 class="text-xl font-semibold"><%= @doc.title %></h1>
18
+ <div class="text-sm opacity-70"><%= @doc.entity %> · <%= @doc.path.presence || "root" %></div>
19
+ </div>
20
+ <% if @doc.file? %>
21
+ <%= link_to "Download (15-min link)", admin_knowledge_doc_download_path(@doc),
22
+ id: "knowledge-download", class: "px-3 py-1.5 border rounded font-medium" %>
23
+ <% end %>
24
+ </div>
25
+
26
+ <dl id="knowledge-doc-meta" class="grid gap-x-8 gap-y-2 md:grid-cols-2 text-sm mb-6">
27
+ <div><dt class="opacity-60">Status</dt><dd><%= @doc.status %></dd></div>
28
+ <div><dt class="opacity-60">Category</dt><dd><%= @doc.category.presence || "—" %></dd></div>
29
+ <div><dt class="opacity-60">Document date</dt><dd><%= @doc.document_date&.iso8601 || "—" %></dd></div>
30
+ <div><dt class="opacity-60">Uploaded</dt><dd><%= @doc.created_at&.to_date&.iso8601 %> by <%= @doc.uploaded_by.presence || "—" %></dd></div>
31
+ <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? ? "#{@doc.mime_type.presence || 'file'} · #{number_to_human_size(@doc.byte_size) if @doc.byte_size}" : "metadata only" %></dd></div>
33
+ <div class="md:col-span-2"><dt class="opacity-60">Access</dt><dd><%= render "studio/knowledge_docs/access_chips", doc: @doc %></dd></div>
34
+ </dl>
35
+
36
+ <% if @doc.summary.present? %>
37
+ <div class="mb-6">
38
+ <h2 class="font-semibold mb-1">Summary <span class="text-xs opacity-60">(what an "aware" agent may read)</span></h2>
39
+ <p class="text-sm whitespace-pre-line"><%= @doc.summary %></p>
40
+ </div>
41
+ <% end %>
42
+
43
+ <div id="knowledge-triage" class="p-4 border rounded">
44
+ <h2 class="font-semibold mb-3">File it</h2>
45
+ <%= form_with url: admin_knowledge_doc_path(@doc), method: :patch, local: true do |form| %>
46
+ <div class="grid gap-3 md:grid-cols-2">
47
+ <label class="block text-sm">
48
+ <span class="block mb-1">Title</span>
49
+ <%= form.text_field "knowledge_doc[title]", value: @doc.title, class: "block w-full border rounded px-2 py-1" %>
50
+ </label>
51
+ <label class="block text-sm">
52
+ <span class="block mb-1">Folder</span>
53
+ <%= form.text_field "knowledge_doc[path]", value: @doc.path, class: "block w-full border rounded px-2 py-1" %>
54
+ </label>
55
+ <label class="block text-sm">
56
+ <span class="block mb-1">Category</span>
57
+ <%= form.text_field "knowledge_doc[category]", value: @doc.category, class: "block w-full border rounded px-2 py-1" %>
58
+ </label>
59
+ <label class="block text-sm">
60
+ <span class="block mb-1">Document date</span>
61
+ <%= form.date_field "knowledge_doc[document_date]", value: @doc.document_date, class: "block w-full border rounded px-2 py-1" %>
62
+ </label>
63
+ <label class="block text-sm">
64
+ <span class="block mb-1">Status</span>
65
+ <%= form.select "knowledge_doc[status]",
66
+ Studio::KnowledgeDoc::STATUSES.map { |s| [s, s] },
67
+ { selected: @doc.status }, class: "block w-full border rounded px-2 py-1" %>
68
+ </label>
69
+ <% (Array(Studio.knowledge_agents) | @doc.access.keys).each do |agent| %>
70
+ <label class="block text-sm">
71
+ <span class="block mb-1">Access — <%= agent %></span>
72
+ <%= form.select "knowledge_doc[access][#{agent}]",
73
+ [["none", ""], ["aware", "aware"], ["full", "full"]],
74
+ { selected: @doc.access_for(agent) == "none" ? "" : @doc.access_for(agent) },
75
+ class: "block w-full border rounded px-2 py-1" %>
76
+ </label>
77
+ <% end %>
78
+ <label class="block text-sm md:col-span-2">
79
+ <span class="block mb-1">Summary (safe for "aware" agents)</span>
80
+ <%= form.text_area "knowledge_doc[summary]", value: @doc.summary, rows: 3, class: "block w-full border rounded px-2 py-1" %>
81
+ </label>
82
+ </div>
83
+ <div class="mt-3">
84
+ <%= form.submit "Save", class: "px-3 py-1.5 border rounded font-medium" %>
85
+ </div>
86
+ <% end %>
87
+ </div>
@@ -18,9 +18,11 @@
18
18
  app-supplied-policy age-gate DOB primitive, gated by Studio.feature?(:age_gate),
19
19
  plus the app-specific entry-tokens purchase flow documented via engine chrome),
20
20
  Profile (crop / upload, opened
21
- through studio/cropper_assets), Web3 (wallet-connect with engine-owned inline
22
- brand icons / on-chain-tx / deposit / the generic entry-confirmed celebration,
23
- gated by Studio.feature?(:web3) — disabled-but-present-yet-openable when off),
21
+ through studio/cropper_assets), Web3 (wallet-connect and web3-step-up now
22
+ rendered from the solana-studio gem / on-chain-tx / deposit / the generic
23
+ entry-confirmed celebration, gated by Studio.feature?(:web3) —
24
+ disabled-but-present-yet-openable when off, except the two gem-backed ids,
25
+ which an app without solana-studio lists but cannot open),
24
26
  System and status (the reusable card blocks), Templates (the copy-from
25
27
  archetypes), and Rewards (level-up + the entry-confirmed seeds bar + the
26
28
  Free Entry Earned reward, gated by :leveling).
@@ -33,6 +35,44 @@
33
35
  leveling_on = Studio.feature?(:leveling)
34
36
  age_gate_on = Studio.feature?(:age_gate)
35
37
 
38
+ # THE WEB3 SPECIMENS NOW COME FROM A GEM THIS ENGINE DOES NOT DEPEND ON.
39
+ # wallet_connect and web3_step_up used to live in app/views/studio/modals and
40
+ # were rendered here unconditionally. The two-template split moved them to
41
+ # solana-studio: BASE is studio-engine + mcritchie-studio, WEB3 ADD is
42
+ # solana-studio + turf-monster. The engine keeps the SESSION half (the
43
+ # /auth/solana routes, Solana::SessionAuth, the phantom callback) and ships no
44
+ # wallet UI at all.
45
+ #
46
+ # So resolution is a RUNTIME question here, not a build-time one. Three of the
47
+ # six apps mounting this engine (acquisition-studio, mcritchie-industries,
48
+ # moms-app) bundle no solana-studio, and an unconditional render would turn
49
+ # their /admin/style into a missing-template 500. The gem is a development and
50
+ # test dependency of this engine precisely so the guide can show the REAL
51
+ # cards; where it is absent the specimens stay listed but unopenable.
52
+ #
53
+ # Same three-term lookup_context.exists? the modal host uses for host_extras
54
+ # (studio/modals/_host.html.erb): name, prefixes, partial.
55
+ web3_gem = lookup_context.exists?("wallet_connect", ["solana_studio/modals"], true)
56
+
57
+ # web3_gem gates the REGISTRATION below. It must also gate the TRIGGER on the
58
+ # three gem-backed cards (Connect wallet + the two Sign Wallet cards), or they
59
+ # stay role=button over a modal id nothing registered: a card that opens an
60
+ # EMPTY panel. The registration gate cannot prevent that on its own — it is
61
+ # what stops the missing-template 500, one layer down.
62
+ #
63
+ # Both terms are load-bearing, because the two gates answer different
64
+ # questions and a BASE app can fail either:
65
+ # openable: web3_gem -> with the capability OFF, the section's
66
+ # disabled-but-openable preview contract would otherwise keep the card
67
+ # clickable (clickable is !(disabled AND NOT openable)).
68
+ # disabled: web3_card_off -> with the capability ON but no gem, `disabled`
69
+ # alone is false, so the card renders as a NORMAL live card: no badge, no
70
+ # aria-disabled, and clickable whatever `openable` says. That is the worse
71
+ # of the two, and it is the exact scenario the BASE-app test runs in.
72
+ # The other six web3 specimens are engine-owned and registered
73
+ # unconditionally, so they keep the plain capability gate.
74
+ web3_card_off = !web3_on || !web3_gem
75
+
36
76
  # Auth method-toggle defaults — mirror the app's Studio.auth_method? config;
37
77
  # Solana Wallet also needs the web3 capability, so it defaults OFF on an app
38
78
  # (like McRitchie Studio) that ships web3 off.
@@ -432,17 +472,21 @@
432
472
  <template x-if="$store.dsModals.current().id === 'cosign-rejected'">
433
473
  <div><%= render "style/modals/cosign_rejected" %></div>
434
474
  </template>
435
- <template x-if="$store.dsModals.current().id === 'wallet-connect'">
436
- <div><%= render "style/modals/wallet_connect" %></div>
437
- </template>
438
- <%# web3-step-up the REAL shared partial, not a style-guide specimen.
439
- The other Web3 cards here render style/modals/* demos while each app
440
- keeps its own live modal; this one renders studio/modals/* — the
441
- same file a host renders in production — so what is reviewed here IS
442
- the shipped card. modal_store mounts it on this page's own host. %>
443
- <template x-if="$store.dsModals.current().id === 'web3-step-up'">
444
- <div><%= render "studio/modals/web3_step_up", modal_store: "dsModals" %></div>
445
- </template>
475
+ <%# Both web3 sign-in cards render from SOLANA-STUDIO, and both are the
476
+ REAL shipped partials rather than style-guide forks — the same files
477
+ turf-monster renders in production, so what is reviewed here IS the
478
+ card that ships. They left this engine with the two-template split.
479
+ Registered only when the gem resolves (see web3_gem above): an app
480
+ that bundles no solana-studio still lists these specimens, it just
481
+ cannot open them. %>
482
+ <% if web3_gem %>
483
+ <template x-if="$store.dsModals.current().id === 'wallet-connect'">
484
+ <div><%= render "style/modals/wallet_connect" %></div>
485
+ </template>
486
+ <template x-if="$store.dsModals.current().id === 'web3-step-up'">
487
+ <div><%= render "solana_studio/modals/web3_step_up", modal_store: "dsModals" %></div>
488
+ </template>
489
+ <% end %>
446
490
  <template x-if="$store.dsModals.current().id === 'onchain-tx'">
447
491
  <div><%= render "style/modals/onchain_tx" %></div>
448
492
  </template>
@@ -1053,7 +1097,9 @@
1053
1097
 
1054
1098
  <%# ===================================================================== %>
1055
1099
  <%# 2. WEB3 — the wallet + on-chain modals, in the order a player meets them. %>
1056
- <%# Gated by :web3; off = disabled-but-present-yet-openable. %>
1100
+ <%# Gated by :web3; off = disabled-but-present-yet-openable. The three
1101
+ gem-backed cards carry a second gate, web3_card_off, because a preview
1102
+ you can open needs something registered to open. %>
1057
1103
  <%# ===================================================================== %>
1058
1104
  <section id="modals-web3" class="space-y-5">
1059
1105
  <div class="space-y-1">
@@ -1070,6 +1116,12 @@
1070
1116
  <code class="font-mono text-2xs">Studio.feature?(:web3)</code>. Web3 is
1071
1117
  <strong><%= web3_on ? "on" : "off" %></strong> here, so these render
1072
1118
  <%= web3_on ? "live." : "greyed and badged — but STILL openable as a preview." %>
1119
+ <% unless web3_gem %>
1120
+ <strong>This app bundles no solana-studio</strong>, so the three cards it
1121
+ backs &mdash; Connect wallet and the two Sign Wallet states &mdash; are
1122
+ listed for reference but <strong>cannot be opened here</strong>. Nothing
1123
+ registers them, so a trigger would open an empty panel.
1124
+ <% end %>
1073
1125
  The cards run in the order a player meets them &mdash; <strong>get a wallet,
1074
1126
  prove it, spend from it</strong>: Connect wallet &rarr; Setup Wallet &rarr;
1075
1127
  the two Sign Wallet states &rarr; Processing &rarr; success or error &rarr;
@@ -1116,10 +1168,10 @@
1116
1168
  <%# 1. Connect wallet — the walk's entry; picking a wallet swaps to Processing. %>
1117
1169
  <%= render layout: "style/modal_specimen", locals: {
1118
1170
  label: "Connect wallet",
1119
- reference: %(the Web3 "Connect wallet" picker (studio-engine style/modals/_wallet_connect) — the walk's entry: picking a wallet runs a brief connecting state then swaps to the on-chain Processing modal. Open with $store.dsModals.open('wallet-connect')),
1171
+ reference: %(the Web3 "Connect wallet" picker (solana-studio solana_studio/modals/_wallet_connect, configured by style/modals/_wallet_connect) — the walk's entry: picking a wallet runs a brief connecting state then swaps to the on-chain Processing modal. Open with $store.dsModals.open('wallet-connect')),
1120
1172
  open_expr: "$store.dsModals.open('wallet-connect')",
1121
1173
  glow_when: ds_glow.call("wallet-connect"),
1122
- disabled: !web3_on, openable: true } do %>
1174
+ disabled: web3_card_off, openable: web3_gem } do %>
1123
1175
  <%# A PICKER: brand tiles with names beside them, which is what this modal
1124
1176
  actually is. Two empty outlines said "form with two inputs" and were
1125
1177
  the least informative sketch in the section — the one card whose
@@ -1182,10 +1234,10 @@
1182
1234
  wallet, so no user can ever meet both. %>
1183
1235
  <%= render layout: "style/modal_specimen", locals: {
1184
1236
  label: "Sign Wallet",
1185
- reference: %(the Web3 "Sign Wallet" card (studio/modals/_web3_step_up — the REAL shared partial, not a specimen copy) — shown to an account that holds a self-custody wallet but authenticated this session with a web2 credential. One wallet row, glowing, because it is the only thing to press. Open with $store.dsModals.open('web3-step-up', { provider: 'phantom', providerLabel: 'Phantom', walletHint: '7xKp…JZ2Q' })),
1237
+ reference: %(the Web3 "Sign Wallet" card (solana-studio solana_studio/modals/_web3_step_up — the REAL shared partial, not a specimen copy) — shown to an account that holds a self-custody wallet but authenticated this session with a web2 credential. One wallet row, glowing, because it is the only thing to press. Open with $store.dsModals.open('web3-step-up', { provider: 'phantom', providerLabel: 'Phantom', walletHint: '7xKp…JZ2Q' })),
1186
1238
  open_expr: "$store.dsModals.open('web3-step-up', { provider: 'phantom', providerLabel: 'Phantom', walletHint: '7xKp…JZ2Q' })",
1187
1239
  glow_when: ds_glow.call("web3-step-up", provider: true),
1188
- disabled: !web3_on, openable: true } do %>
1240
+ disabled: web3_card_off, openable: web3_gem } do %>
1189
1241
  <%# A FILLED row: brand tile, the wallet's name, an Installed badge. The
1190
1242
  two step-up thumbnails used to differ by a single border-dashed class
1191
1243
  on a 20px row, which is not a difference anyone can see at thumbnail
@@ -1205,10 +1257,10 @@
1205
1257
 
1206
1258
  <%= render layout: "style/modal_specimen", locals: {
1207
1259
  label: "Sign Wallet (no remembered brand)",
1208
- reference: %(the Web3 "Sign Wallet" card with NO remembered brand (studio/modals/_web3_step_up) — every wallet linked before a host recorded brands lands here, so it is a live population and not a defensive branch. The row falls back to the picker rather than dead-ending. Open with $store.dsModals.open('web3-step-up', {})),
1260
+ reference: %(the Web3 "Sign Wallet" card with NO remembered brand (solana-studio solana_studio/modals/_web3_step_up) — every wallet linked before a host recorded brands lands here, so it is a live population and not a defensive branch. The row falls back to the picker rather than dead-ending. Open with $store.dsModals.open('web3-step-up', {})),
1209
1261
  open_expr: "$store.dsModals.open('web3-step-up', {})",
1210
1262
  glow_when: ds_glow.call("web3-step-up", provider: false),
1211
- disabled: !web3_on, openable: true } do %>
1263
+ disabled: web3_card_off, openable: web3_gem } do %>
1212
1264
  <%# An EMPTY slot: dashed outline, no brand tile, a "?" where the wallet's
1213
1265
  name would be. Reads at a glance as "we do not know which wallet",
1214
1266
  which is the whole difference between this card and the one before it. %>
@@ -1,13 +1,19 @@
1
1
  <%#
2
2
  "Connect Wallet" specimen for the living style guide.
3
3
 
4
- As of the wallet-picker promotion this is a THIN CONFIGURATION of the real
5
- engine partial (studio/modals/_wallet_connect) rather than a parallel port of
6
- it. That is the point: the card on /admin/style now demonstrates the SAME code
7
- the apps render, so a drift between the specimen and production is no longer
4
+ This is a THIN CONFIGURATION of the real picker rather than a parallel port of
5
+ it. That is the point: the card on /admin/style demonstrates the SAME code the
6
+ apps render, so a drift between the specimen and production is no longer
8
7
  possible. Before this, the picker existed three times — turf-monster's,
9
8
  mcritchie-studio's, and this one — and only this one was ever looked at.
10
9
 
10
+ THE PARTIAL IS NO LONGER THIS ENGINE'S. It moved to solana-studio
11
+ (solana_studio/modals/_wallet_connect) with the two-template split: BASE is
12
+ studio-engine + mcritchie-studio, WEB3 ADD is solana-studio + turf-monster.
13
+ The engine keeps the session half and ships no wallet UI. Nothing renders this
14
+ file unless the gem resolved — style/_modals.html.erb gates the registration
15
+ on web3_gem — so an app with no solana-studio never reaches this render.
16
+
11
17
  What the configuration supplies, all of it demo scaffolding:
12
18
  store — the guide's page-scoped host ($store.dsModals), not the app
13
19
  host, so the specimen never collides with a consuming app's
@@ -75,7 +81,7 @@
75
81
  }
76
82
  JS
77
83
  %>
78
- <%= render "studio/modals/wallet_connect",
84
+ <%= render "solana_studio/modals/wallet_connect",
79
85
  store: "dsModals",
80
86
  connect_fn: "dsWalletConnectDemo",
81
87
  slot: "style/modals/wallet_connect_slot",
@@ -0,0 +1,49 @@
1
+ # Reference migration for the Studio::KnowledgeDoc model. Like studio_links,
2
+ # each consumer app installs its own copy into db/migrate
3
+ # (`bin/rails studio_engine:install:migrations && bin/rails db:migrate`) so the
4
+ # table is created in the app's database. Do not hand-copy — a hand copy
5
+ # collides with the installed copy on `class CreateStudioKnowledgeDocs`.
6
+ class CreateStudioKnowledgeDocs < ActiveRecord::Migration[7.2]
7
+ def change
8
+ create_table :studio_knowledge_docs do |t|
9
+ t.string :title, null: false
10
+ # Which business the document belongs to (e.g. "commercial-welding-llc").
11
+ # One app can host several entities' knowledge; every read is entity-scoped.
12
+ t.string :entity, null: false
13
+ # Folder path with no leading/trailing slash ("financials/aging-inventory/2026-08").
14
+ # Folders are implicit — they exist because a document claims the path —
15
+ # and the S3 key mirrors it, so the bucket stays human-browsable.
16
+ t.string :path, null: false, default: ""
17
+ t.string :category
18
+ t.string :mime_type
19
+ # The document's own as-of date (an August aging report dated August),
20
+ # distinct from created_at (when it was uploaded).
21
+ t.date :document_date
22
+ # inbox -> filed -> superseded. Uploads land as inbox for agent triage.
23
+ t.string :status, null: false, default: "inbox"
24
+ # Per-agent access map: {"samson" => "full", "dawn" => "aware"}.
25
+ # "aware" = the agent knows the document exists and gets the safe summary,
26
+ # not the contents; absent agents default to "none".
27
+ t.jsonb :access, null: false, default: {}
28
+ t.jsonb :tags, null: false, default: []
29
+ # The safe summary — what an "aware" agent may read and repeat.
30
+ t.text :summary
31
+ # Provenance: who/where the document came from ("broker email 2026-08-30").
32
+ t.string :source_note
33
+ t.string :uploaded_by
34
+ # Object storage pointer (Studio::S3 logical key); nil for a metadata-only
35
+ # record. byte_size is captured at attach time for the index view.
36
+ t.string :s3_key
37
+ t.bigint :byte_size
38
+ # Set on the OLD row when a newer document replaces it.
39
+ t.bigint :superseded_by_id
40
+
41
+ t.timestamps
42
+ end
43
+
44
+ add_index :studio_knowledge_docs, [:entity, :status]
45
+ add_index :studio_knowledge_docs, [:entity, :path]
46
+ add_index :studio_knowledge_docs, :s3_key, unique: true
47
+ add_index :studio_knowledge_docs, :superseded_by_id
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Studio
2
- VERSION = "0.66.1"
2
+ VERSION = "0.67.0"
3
3
  end
data/lib/studio.rb CHANGED
@@ -554,6 +554,14 @@ module Studio
554
554
  # A trailing slash is added if you leave it off.
555
555
  mattr_accessor :s3_key_prefix, default: nil
556
556
 
557
+ # Knowledge layer — the S3-backed document store + /admin/knowledge browser
558
+ # (Studio::KnowledgeDoc). Routes are opt-in like every route surface.
559
+ # knowledge_agents is the roster of agent slugs the intake UI offers
560
+ # per-agent access selects for (e.g. %w[samson dawn] on Industries); the
561
+ # access map itself accepts any agent slug regardless.
562
+ mattr_accessor :draw_knowledge_routes, default: false
563
+ mattr_accessor :knowledge_agents, default: []
564
+
557
565
  class S3ConfigError < StandardError; end
558
566
 
559
567
  # Whether to validate the host app's User model at boot. See docs/USER_CONTRACT.md.
@@ -846,6 +854,19 @@ module Studio
846
854
  constraints: { token: %r{[^/]+} }
847
855
  end
848
856
 
857
+ # Knowledge layer — /admin/knowledge (Studio::KnowledgeDoc): folder/flat
858
+ # document browser, upload-to-inbox intake, per-agent access map, and
859
+ # 15-minute presigned downloads. Opt-in (default off) like every route
860
+ # surface, so no app grows an admin page it never asked for.
861
+ if Studio.draw_knowledge_routes
862
+ get "admin/knowledge", to: "studio/knowledge_docs#index", as: :admin_knowledge
863
+ post "admin/knowledge", to: "studio/knowledge_docs#create"
864
+ get "admin/knowledge/:id", to: "studio/knowledge_docs#show", as: :admin_knowledge_doc
865
+ patch "admin/knowledge/:id", to: "studio/knowledge_docs#update"
866
+ get "admin/knowledge/:id/download", to: "studio/knowledge_docs#download",
867
+ as: :admin_knowledge_doc_download
868
+ end
869
+
849
870
  # Solana / Phantom wallet sign-in (nonce challenge + signature verify),
850
871
  # plus the MOBILE deep-link callback Phantom redirects back to. The
851
872
  # callback used to be listed here as app-specific and is not any more —
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.66.1
4
+ version: 0.67.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McRitchie
@@ -328,6 +328,7 @@ files:
328
328
  - app/controllers/studio/email_images_controller.rb
329
329
  - app/controllers/studio/emails_controller.rb
330
330
  - app/controllers/studio/geo_settings_controller.rb
331
+ - app/controllers/studio/knowledge_docs_controller.rb
331
332
  - app/controllers/studio/links_controller.rb
332
333
  - app/controllers/studio/local_emails_controller.rb
333
334
  - app/controllers/studio/local_reviews_controller.rb
@@ -361,6 +362,7 @@ files:
361
362
  - app/models/studio/email_setting.rb
362
363
  - app/models/studio/enumeral.rb
363
364
  - app/models/studio/geo_setting.rb
365
+ - app/models/studio/knowledge_doc.rb
364
366
  - app/models/studio/link.rb
365
367
  - app/models/studio/model_page.rb
366
368
  - app/models/theme_setting.rb
@@ -436,6 +438,12 @@ files:
436
438
  - app/views/studio/emails/show.html.erb
437
439
  - app/views/studio/fields/_date_of_birth.html.erb
438
440
  - app/views/studio/geo_settings/edit.html.erb
441
+ - app/views/studio/knowledge_docs/_access_chips.html.erb
442
+ - app/views/studio/knowledge_docs/_browser.html.erb
443
+ - app/views/studio/knowledge_docs/_doc_row.html.erb
444
+ - app/views/studio/knowledge_docs/_upload_form.html.erb
445
+ - app/views/studio/knowledge_docs/index.html.erb
446
+ - app/views/studio/knowledge_docs/show.html.erb
439
447
  - app/views/studio/links/confirm.html.erb
440
448
  - app/views/studio/local_emails/index.html.erb
441
449
  - app/views/studio/mailers/_layered_banner.html.erb
@@ -445,8 +453,6 @@ files:
445
453
  - app/views/studio/modals/_load_convention.html.erb
446
454
  - app/views/studio/modals/_saving.html.erb
447
455
  - app/views/studio/modals/_scoped_host.html.erb
448
- - app/views/studio/modals/_wallet_connect.html.erb
449
- - app/views/studio/modals/_web3_step_up.html.erb
450
456
  - app/views/studio/modals/auth/_resend_footer.html.erb
451
457
  - app/views/studio/modals/blocks/_age_gate.html.erb
452
458
  - app/views/studio/modals/blocks/_birthday.html.erb
@@ -499,8 +505,6 @@ files:
499
505
  - app/views/studio/profiles/_save_controls.html.erb
500
506
  - app/views/studio/profiles/edit.html.erb
501
507
  - app/views/studio/profiles/show.html.erb
502
- - app/views/studio/solana/_deeplink_assets.html.erb
503
- - app/views/studio/solana/_phantom_deeplink.html.erb
504
508
  - app/views/style/_modal_specimen.html.erb
505
509
  - app/views/style/_modals.html.erb
506
510
  - app/views/style/_specimen.html.erb
@@ -548,6 +552,7 @@ files:
548
552
  - db/migrate/20260813010000_add_body_cta_footer_to_studio_email_settings.rb
549
553
  - db/migrate/20260813220000_add_standard_user_profile_columns.rb
550
554
  - db/migrate/20260818120000_create_studio_geo_settings.rb
555
+ - db/migrate/20260901000001_create_studio_knowledge_docs.rb
551
556
  - lib/studio-engine.rb
552
557
  - lib/studio.rb
553
558
  - lib/studio/cable.rb