spine-framework 0.4.3 → 0.5.0-rc.4

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 (320) hide show
  1. package/.env.example +13 -3
  2. package/.framework/cli/commands/bootstrap.ts +392 -0
  3. package/.framework/cli/commands/dev.ts +91 -218
  4. package/.framework/cli/commands/doctor.ts +132 -26
  5. package/.framework/cli/commands/generate.ts +38 -298
  6. package/.framework/cli/commands/init.ts +20 -1
  7. package/.framework/cli/commands/migrate.ts +36 -30
  8. package/.framework/cli/commands/migrations.ts +16 -56
  9. package/.framework/cli/commands/upgrade.ts +35 -0
  10. package/.framework/cli/commands/validate-schema.ts +147 -0
  11. package/.framework/cli/context.ts +223 -110
  12. package/.framework/cli/index.ts +6 -0
  13. package/.framework/cli/lib/project-files.ts +241 -1
  14. package/.framework/functions/_shared/agent-runner.ts +1000 -81
  15. package/.framework/functions/_shared/cil.ts +2 -1
  16. package/.framework/functions/_shared/db.ts +3 -6
  17. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  18. package/.framework/functions/_shared/kb.ts +61 -7
  19. package/.framework/functions/_shared/logging.ts +562 -224
  20. package/.framework/functions/_shared/message-sequence.ts +53 -0
  21. package/.framework/functions/_shared/middleware.ts +47 -44
  22. package/.framework/functions/_shared/pipeline-runner.ts +150 -37
  23. package/.framework/functions/_shared/principal.ts +9 -6
  24. package/.framework/functions/_shared/trigger-engine.ts +149 -40
  25. package/.framework/functions/admin-data.ts +48 -31
  26. package/.framework/functions/ai-agents.ts +103 -33
  27. package/.framework/functions/api-keys.ts +301 -122
  28. package/.framework/functions/apps.ts +43 -26
  29. package/.framework/functions/cil-ingest.ts +48 -15
  30. package/.framework/functions/debug-auth.ts +14 -8
  31. package/.framework/functions/deliveries.ts +39 -12
  32. package/.framework/functions/embeddings.ts +145 -76
  33. package/.framework/functions/integration-outbound.ts +67 -27
  34. package/.framework/functions/integrations.ts +254 -127
  35. package/.framework/functions/logs.ts +273 -133
  36. package/.framework/functions/observability.ts +277 -147
  37. package/.framework/functions/pipeline-executions.ts +1 -1
  38. package/.framework/functions/pipeline-runner-background.ts +120 -0
  39. package/.framework/functions/pipelines.ts +14 -9
  40. package/.framework/functions/prompt-configs.ts +7 -4
  41. package/.framework/functions/roles.ts +8 -5
  42. package/.framework/functions/security.ts +30 -17
  43. package/.framework/functions/system-cron.ts +250 -128
  44. package/.framework/functions/tests.ts +7 -6
  45. package/.framework/functions/timers.ts +310 -107
  46. package/.framework/functions/trigger-scheduler.ts +65 -5
  47. package/.framework/functions/triggers.ts +28 -13
  48. package/.framework/functions/types.ts +13 -10
  49. package/.framework/migrations/064_episode_leases.sql +196 -0
  50. package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
  51. package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
  52. package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
  53. package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
  54. package/.framework/migrations/069_log_stats_window.sql +54 -0
  55. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  56. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  57. package/.framework/migrations/072_slug_autofill.sql +77 -0
  58. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  59. package/.framework/schemas/spine.config.schema.json +62 -0
  60. package/.framework/src/App.tsx +3 -1
  61. package/.framework/src/apps/admin/index.tsx +1141 -775
  62. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  63. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  64. package/.framework/src/components/admin/AdminSidebar.tsx +92 -81
  65. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  66. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  67. package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
  68. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  69. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  70. package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
  71. package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
  72. package/.framework/src/components/layout/Layout.tsx +4 -2
  73. package/.framework/src/components/layout/Sidebar.tsx +304 -179
  74. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  75. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  76. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  77. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  78. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  79. package/.framework/src/components/ui/sheet.tsx +24 -26
  80. package/.framework/src/components/ui/textarea.tsx +74 -48
  81. package/.framework/src/hooks/useBranding.ts +47 -0
  82. package/.framework/src/hooks/useEntityList.ts +12 -9
  83. package/.framework/src/hooks/useListSchema.ts +108 -58
  84. package/.framework/src/instructions/README.md +57 -0
  85. package/.framework/src/instructions/ai-agents.md +69 -0
  86. package/.framework/src/instructions/alerts.md +80 -0
  87. package/.framework/src/instructions/api-keys.md +87 -0
  88. package/.framework/src/instructions/apps.md +77 -0
  89. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  90. package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
  91. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  92. package/.framework/src/instructions/concepts/automation.md +186 -0
  93. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  94. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  95. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  96. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  97. package/.framework/src/instructions/concepts/observability.md +116 -0
  98. package/.framework/src/instructions/concepts/permissions.md +150 -0
  99. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  100. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  101. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  102. package/.framework/src/instructions/deliveries.md +59 -0
  103. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  104. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  105. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  106. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  107. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  108. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  109. package/.framework/src/instructions/integrations.md +96 -0
  110. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  111. package/.framework/src/instructions/invites.md +60 -0
  112. package/.framework/src/instructions/kb.md +80 -0
  113. package/.framework/src/instructions/logs.md +77 -0
  114. package/.framework/src/instructions/observability.md +61 -0
  115. package/.framework/src/instructions/operations/deployment.md +115 -0
  116. package/.framework/src/instructions/operations/migrations.md +125 -0
  117. package/.framework/src/instructions/operations/security-checklist.md +148 -0
  118. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  119. package/.framework/src/instructions/operations/upgrade.md +116 -0
  120. package/.framework/src/instructions/pipeline-executions.md +62 -0
  121. package/.framework/src/instructions/pipelines.md +78 -0
  122. package/.framework/src/instructions/prompts.md +95 -0
  123. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  124. package/.framework/src/instructions/reference/api/auth.md +113 -0
  125. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  126. package/.framework/src/instructions/reference/app-manifest.md +150 -0
  127. package/.framework/src/instructions/reference/cli.md +124 -0
  128. package/.framework/src/instructions/reference/database.md +132 -0
  129. package/.framework/src/instructions/reference/design-schema.md +280 -0
  130. package/.framework/src/instructions/reference/env-vars.md +133 -0
  131. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  132. package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
  133. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  134. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  135. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  136. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  137. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  138. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  139. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  140. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  141. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  142. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  143. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  144. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  145. package/.framework/src/instructions/reference/functions/index.md +103 -0
  146. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  147. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  148. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  149. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  150. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  151. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  152. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  153. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  154. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  155. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  156. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  157. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  158. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  159. package/.framework/src/instructions/reference/functions/security.md +140 -0
  160. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  161. package/.framework/src/instructions/reference/functions/system.md +106 -0
  162. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  163. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  164. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  165. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  166. package/.framework/src/instructions/reference/functions/types.md +146 -0
  167. package/.framework/src/instructions/reference/seeds.md +126 -0
  168. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  169. package/.framework/src/instructions/reference/spine-config.md +114 -0
  170. package/.framework/src/instructions/roles.md +80 -0
  171. package/.framework/src/instructions/runtime-detail.md +70 -0
  172. package/.framework/src/instructions/runtime-list.md +77 -0
  173. package/.framework/src/instructions/security.md +67 -0
  174. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  175. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  176. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  177. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  178. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  179. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  180. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  181. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  182. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  183. package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
  184. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  185. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  186. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  187. package/.framework/src/instructions/testing.md +59 -0
  188. package/.framework/src/instructions/timers.md +82 -0
  189. package/.framework/src/instructions/triggers.md +69 -0
  190. package/.framework/src/instructions/types.md +93 -0
  191. package/.framework/src/lib/branding.ts +128 -0
  192. package/.framework/src/pages/NoAppPage.tsx +3 -1
  193. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  194. package/.framework/src/pages/SetupPage.tsx +5 -2
  195. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  196. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  197. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  198. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  199. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  200. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  201. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  202. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  203. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  204. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  205. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  206. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  207. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  208. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  209. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  210. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  211. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  212. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  213. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  214. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  215. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  216. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  217. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  218. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  219. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  220. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  221. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  222. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  223. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  224. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  225. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  226. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  227. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  228. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  229. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  230. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  231. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  232. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  233. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  234. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  235. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  236. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  237. package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
  238. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
  239. package/.framework/src/pages/auth/LoginPage.tsx +4 -2
  240. package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
  241. package/.framework/templates/agent/AGENTS.md +150 -0
  242. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  243. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  244. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  245. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  246. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
  247. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  248. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
  249. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
  250. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
  251. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  252. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  253. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  254. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  255. package/.framework/templates/project.json +5 -1
  256. package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
  257. package/.framework/tests/unit/agent-files.test.ts +355 -0
  258. package/.framework/tests/unit/agent-runner.test.ts +496 -47
  259. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  260. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  261. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  262. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  263. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  264. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  265. package/.framework/tests/unit/episode-lease.test.ts +695 -0
  266. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  267. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  268. package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
  269. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  270. package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
  271. package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
  272. package/.framework/tests/unit/principal-cache.test.ts +15 -0
  273. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  274. package/.framework/tests/unit/system-cron.test.ts +240 -0
  275. package/.framework/tests/unit/timers.test.ts +159 -0
  276. package/.framework/tests/unit/trigger-engine.test.ts +278 -91
  277. package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
  278. package/README.md +125 -166
  279. package/config/tsconfig.cli.json +2 -2
  280. package/netlify.toml +5 -0
  281. package/package.json +15 -4
  282. package/templates/project/package-lock.json +2 -2
  283. package/.framework/docs/admin-routes.md +0 -76
  284. package/.framework/docs/api-docs-progress.md +0 -38
  285. package/.framework/docs/api-governance.md +0 -146
  286. package/.framework/docs/api-testing-results.md +0 -212
  287. package/.framework/docs/assembly-launch-guide.md +0 -138
  288. package/.framework/docs/audit-results.md +0 -590
  289. package/.framework/docs/db-api-inventory.md +0 -95
  290. package/.framework/docs/gap-remediation-backlog.md +0 -103
  291. package/.framework/docs/ui-adoption-verification.md +0 -111
  292. package/.framework/docs/ui-api-coverage.md +0 -84
  293. package/.framework/docs/v2-compatibility-audit.md +0 -228
  294. package/.framework/mcp/index.ts +0 -133
  295. package/.framework/mcp/load-env.ts +0 -60
  296. package/.framework/mcp/shared/app-manifest.ts +0 -275
  297. package/.framework/mcp/shared/db.ts +0 -477
  298. package/.framework/mcp/shared/kb.ts +0 -118
  299. package/.framework/mcp/tools/architect.ts +0 -431
  300. package/.framework/mcp/tools/cil/context.ts +0 -345
  301. package/.framework/mcp/tools/cil/graph.ts +0 -242
  302. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  303. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  304. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  305. package/.framework/mcp/tools/cil/project.ts +0 -791
  306. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  307. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  308. package/.framework/mcp/tools/cil/search.ts +0 -97
  309. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  310. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  311. package/.framework/mcp/tools/kb/delete.ts +0 -139
  312. package/.framework/mcp/tools/kb/index.ts +0 -52
  313. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  314. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  315. package/.framework/mcp/tools/kb/publish.ts +0 -100
  316. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  317. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  318. package/.framework/mcp/tools/kb/search.ts +0 -101
  319. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  320. package/.framework/mcp/tools/observability.ts +0 -510
@@ -14,15 +14,14 @@ import { DataStats } from '../runtime/DataStats'
14
14
  import { DataFilters } from '../runtime/DataFilters'
15
15
  import { DataTable } from '../runtime/DataTable'
16
16
  import { PageInstructions } from '../admin/PageInstructions'
17
+ // CIL[END stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:module_header:module_header"]
17
18
  // CIL[DOC-VECTOR type="context" content="purpose='Imports and module setup for the generic list page'; No verified external callers; imports: .framework/src/hooks/useListSchema.ts,.framework/src/hooks/useEntityList.ts,.framework/src/components/runtime/DataHeader.tsx"]
18
19
  // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
19
20
  // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
20
21
  // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
21
22
  // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
22
23
  // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
23
- // CIL[END stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:module_header:module_header"]
24
24
 
25
- // CIL[BEGIN stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:genericlistpageprops:interface" type="interface" identity="genericlistpageprops" parent="~/.framework/src/components/app-shell/GenericListPage.tsx"]
26
25
  // CIL[DOC-MACRO purpose="Props interface for GenericListPage"]
27
26
  // CIL[DOC-RESPONSIBILITY responsibility="Define the expected props for the list page"]
28
27
  // CIL[DOC-CONTEXT module="app-shell" layer="framework" related_files=".framework/src/components/admin/PageInstructions.tsx,.framework/src/components/app-shell/GenericListPage.tsx,.framework/src/components/runtime/DataFilters.tsx,.framework/src/components/runtime/DataHeader.tsx,.framework/src/components/runtime/DataStats.tsx,.framework/src/components/runtime/DataTable.tsx,.framework/src/hooks/useEntityList.ts,.framework/src/hooks/useListSchema.ts"]
@@ -30,21 +29,21 @@ import { PageInstructions } from '../admin/PageInstructions'
30
29
  // CIL[DOC-MICRO logic_summary="TypeScript interface describing typeSlug, viewSlug, appPrefix, and detailPath" notable_decisions="[]"]
31
30
  // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
32
31
  // CIL[DOC-STABILITY stability="stable"]
32
+ // CIL[BEGIN stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:GenericListPageProps:interface" type="interface" identity="GenericListPageProps" parent="~/.framework/src/components/app-shell/GenericListPage.tsx"]
33
33
  interface GenericListPageProps {
34
34
  typeSlug: string
35
35
  viewSlug?: string
36
36
  appPrefix: string
37
37
  detailPath: string
38
38
  }
39
+ // CIL[END stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:GenericListPageProps:interface"]
39
40
  // CIL[DOC-VECTOR type="context" content="purpose='Props interface for GenericListPage'; No verified external callers; imports: .framework/src/hooks/useListSchema.ts,.framework/src/hooks/useEntityList.ts,.framework/src/components/runtime/DataHeader.tsx"]
40
41
  // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
41
42
  // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
42
43
  // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
43
44
  // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
44
45
  // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
45
- // CIL[END stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:genericlistpageprops:interface"]
46
46
 
47
- // CIL[BEGIN stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:genericlistpage:function" type="function" identity="genericlistpage" parent="~/.framework/src/components/app-shell/GenericListPage.tsx"]
48
47
  // CIL[DOC-MACRO purpose="Render the generic list page for an app type"]
49
48
  // CIL[DOC-RESPONSIBILITY responsibility="Display a sortable, filterable data table with schema-driven headers, stats, filters, and instructions"]
50
49
  // CIL[DOC-CONTEXT module="app-shell" layer="framework" related_files=".framework/src/components/admin/PageInstructions.tsx,.framework/src/components/app-shell/GenericListPage.tsx,.framework/src/components/runtime/DataFilters.tsx,.framework/src/components/runtime/DataHeader.tsx,.framework/src/components/runtime/DataStats.tsx,.framework/src/components/runtime/DataTable.tsx,.framework/src/hooks/useEntityList.ts,.framework/src/hooks/useListSchema.ts"]
@@ -52,6 +51,7 @@ interface GenericListPageProps {
52
51
  // CIL[DOC-MICRO logic_summary="Resolve the list schema, build a config, load entity data, and render the list page with header, instructions, stats, filters, and table" notable_decisions="[]"]
53
52
  // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
54
53
  // CIL[DOC-STABILITY stability="stable"]
54
+ // CIL[BEGIN stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:GenericListPage:component" type="component" identity="GenericListPage" parent="~/.framework/src/components/app-shell/GenericListPage.tsx"]
55
55
  export function GenericListPage({ typeSlug, viewSlug, appPrefix, detailPath }: GenericListPageProps) {
56
56
  const resolvedViewSlug = viewSlug || 'default_list'
57
57
 
@@ -61,8 +61,13 @@ export function GenericListPage({ typeSlug, viewSlug, appPrefix, detailPath }: G
61
61
  viewSlug: resolvedViewSlug
62
62
  })
63
63
 
64
+ // nav_item `view` is a single slug shared by list + detail pages; a
65
+ // detail-shaped view (sections, no fields) must not resolve here.
66
+ const resolvedListView = view && ((view as any).type === 'list' || (view as any).fields)
67
+ ? view
68
+ : schema?.views?.default_list ?? view
64
69
 
65
- const config = view && schema ? {
70
+ const config = resolvedListView && schema ? {
66
71
  entity: 'items',
67
72
  typeSlug,
68
73
  icon: 'database',
@@ -71,12 +76,22 @@ export function GenericListPage({ typeSlug, viewSlug, appPrefix, detailPath }: G
71
76
  listAction: 'list'
72
77
  },
73
78
  list: {
74
- defaultSort: (view as any).default_sort || { field: 'created_at', direction: 'desc' },
75
- stats: (view as any).stats || [],
76
- filters: (view as any).filters || [],
77
- columns: Object.entries((view as any).fields || {}).map(([key, fieldConfig]: [string, any]) => ({
79
+ defaultSort: (resolvedListView as any).default_sort || { field: 'created_at', direction: 'desc' },
80
+ stats: (resolvedListView as any).stats || [],
81
+ // Docs allow bare field-name strings; DataFilters needs full
82
+ // EntityFilter objects, so normalize strings against the schema.
83
+ filters: ((resolvedListView as any).filters || []).map((f: any) => {
84
+ if (typeof f !== 'string') return f
85
+ const field: any = (schema.fields as any)?.[f] || (schema.fields as any)?.[f.replace(/^data\./, '')] || {}
86
+ const options = (field.options || []).map((o: any) => (typeof o === 'string' ? o : o.value))
87
+ const type = field.data_type === 'boolean' || field.data_type === 'checkbox'
88
+ ? 'boolean'
89
+ : options.length ? 'enum' : 'search'
90
+ return { key: f, label: field.label || f, type, options: options.length ? options : undefined }
91
+ }),
92
+ columns: Object.entries((resolvedListView as any).fields || {}).map(([key, fieldConfig]: [string, any]) => ({
78
93
  key,
79
- label: schema.fields[key]?.label || key,
94
+ label: schema.fields[key]?.label || schema.fields[key.replace(/^data\./, '')]?.label || key,
80
95
  sortable: fieldConfig.sortable !== false,
81
96
  display_type: fieldConfig.display_type
82
97
  }))
@@ -115,32 +130,13 @@ export function GenericListPage({ typeSlug, viewSlug, appPrefix, detailPath }: G
115
130
  return (
116
131
  <div className="space-y-6">
117
132
  <DataHeader
118
- title={schema?.fields ? Object.keys(schema.fields).length > 0 ? (view as any)?.label || typeSlug : typeSlug : typeSlug}
133
+ title={schema?.fields ? Object.keys(schema.fields).length > 0 ? (resolvedListView as any)?.label || typeSlug : typeSlug : typeSlug}
119
134
  icon={config.icon}
120
135
  description={`Manage ${typeSlug}`}
121
136
  newButtonHref={`${appPrefix}/${detailPath}/new`}
122
137
  />
123
138
 
124
- <PageInstructions
125
- sections={[
126
- {
127
- title: 'What this page is for',
128
- content: `This page lists all ${typeSlug} records for this app. It displays the stored records, their field values, and current status based on the app type schema.`,
129
- },
130
- {
131
- title: 'How to find records',
132
- content: `Use the filters above the table to narrow results. Click any column header to sort ascending or descending. The available columns and filters are defined by the type's design schema.`,
133
- },
134
- {
135
- title: 'How to create, view, and edit records',
136
- content: `Click the New button in the top-right to create a new ${typeSlug}. Click any row to open its detail view. From the detail view you can edit field values or delete the record. Permissions may restrict which actions are available to your role.`,
137
- },
138
- {
139
- title: 'Understanding the stats',
140
- content: `The cards above the table show summary counts and key metrics for this record type. Use them to quickly assess volume and status before drilling into the list.`,
141
- },
142
- ]}
143
- />
139
+ <PageInstructions pageKey="runtime-list" />
144
140
 
145
141
  <DataStats
146
142
  stats={config.list.stats}
@@ -170,10 +166,10 @@ export function GenericListPage({ typeSlug, viewSlug, appPrefix, detailPath }: G
170
166
  </div>
171
167
  )
172
168
  }
169
+ // CIL[END stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:GenericListPage:component"]
173
170
  // CIL[DOC-VECTOR type="context" content="purpose='Render the generic list page for an app type'; No verified external callers; imports: .framework/src/hooks/useListSchema.ts,.framework/src/hooks/useEntityList.ts,.framework/src/components/runtime/DataHeader.tsx"]
174
171
  // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
175
172
  // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
176
173
  // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
177
174
  // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
178
175
  // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
179
- // CIL[END stable_id="~/.framework/src/components/app-shell/GenericListPage.tsx:genericlistpage:function"]
@@ -16,12 +16,15 @@ export const turnstileEnabled = !!SITE_KEY
16
16
  export function TurnstileWidget({ onToken }: { onToken: (token: string | null) => void }) {
17
17
  if (!SITE_KEY) return null
18
18
  return (
19
- <Turnstile
20
- siteKey={SITE_KEY}
21
- onSuccess={onToken}
22
- onExpire={() => onToken(null)}
23
- onError={() => onToken(null)}
24
- />
19
+ <div className="flex justify-center">
20
+ <Turnstile
21
+ siteKey={SITE_KEY}
22
+ options={{ theme: 'light' }}
23
+ onSuccess={onToken}
24
+ onExpire={() => onToken(null)}
25
+ onError={() => onToken(null)}
26
+ />
27
+ </div>
25
28
  )
26
29
  }
27
30
  // CIL[END stable_id="~/.framework/src/components/auth/TurnstileWidget.tsx:module_header:module_header"]
@@ -1,4 +1,12 @@
1
1
  // CIL[BEGIN stable_id="~/.framework/src/components/kb/KBArticleEditor.tsx:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/src/components/kb/KBArticleEditor.tsx"]
2
+ // CIL[DOC-MACRO purpose="Schema-driven knowledge-article editor — loads a record's design_schema and renders view/edit/new modes"]
3
+ // CIL[DOC-RESPONSIBILITY responsibility="Load type schema + article record, render main fields (title/content/description) plus a metadata sidebar, and handle save/publish/delete"]
4
+ // CIL[DOC-CONTEXT module=".framework/src/components/kb/KBArticleEditor.tsx" layer="framework_ui" related_files=".framework/src/components/ui/RichTextEditor.tsx, .framework/src/components/shared/SchemaFields.tsx, .framework/functions/_shared/kb.ts"]
5
+ // CIL[DOC-CONTRACT inputs="KBArticleEditorProps (typeSlug, mode, articleId, onPublish?, sidebarFieldNames?, ...)" outputs="React editor view" errors="inline banner" side_effects="admin-data get/create/update/remove, optional custom onPublish or /api/kb publish"]
6
+ // CIL[DOC-MICRO logic_summary="Fields split into main (title, content) and sidebar (everything else, optionally allowlisted via sidebarFieldNames); handleSave always saves first, then publishes via onPublish or /api/kb?action=publish; header wraps and truncates gracefully so long titles/dates never clip or overlap the action buttons" notable_decisions="custom onPublish callers save at current status — the backend decides publication after validation so a hard-failing draft never becomes published"]
7
+ // CIL[DOC-CONFIDENCE pass="second" score="0.9" flags="none"]
8
+ // CIL[DOC-STABILITY stability="stable"]
9
+ // CIL[DOC-AUDIENCE audience="developers"]
2
10
 
3
11
 
4
12
 
@@ -49,6 +57,13 @@ import { ArrowLeft, Save, Send, Trash, X, Edit } from 'lucide-react'
49
57
 
50
58
 
51
59
  // CIL[BEGIN stable_id="~/.framework/src/components/kb/KBArticleEditor.tsx:KBArticleEditorProps:interface" type="interface" identity="KBArticleEditorProps" parent="~/.framework/src/components/kb/KBArticleEditor.tsx"]
60
+ // CIL[DOC-MACRO purpose="Props interface for KBArticleEditor"]
61
+ // CIL[DOC-RESPONSIBILITY responsibility="Declare mode, record id, permission flags, callbacks, publish overrides, and the sidebar field allowlist"]
62
+ // CIL[DOC-CONTEXT module=".framework/src/components/kb/KBArticleEditor.tsx" layer="framework_ui" related_files=""]
63
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
64
+ // CIL[DOC-MICRO logic_summary="onPublish replaces the default /api/kb publish call; sidebarFieldNames filters the metadata sidebar (empty array hides it entirely while still seeding formData)" notable_decisions="[]"]
65
+ // CIL[DOC-CONFIDENCE pass="first" score="0.8" flags="none"]
66
+ // CIL[DOC-STABILITY stability="stable"]
52
67
  export interface KBArticleEditorProps {
53
68
  typeSlug: string
54
69
  typeName?: string
@@ -109,6 +124,13 @@ export interface KBArticleEditorProps {
109
124
 
110
125
 
111
126
  // CIL[BEGIN stable_id="~/.framework/src/components/kb/KBArticleEditor.tsx:KBRecord:interface" type="interface" identity="KBRecord" parent="~/.framework/src/components/kb/KBArticleEditor.tsx"]
127
+ // CIL[DOC-MACRO purpose="Minimal runtime-record shape the editor consumes"]
128
+ // CIL[DOC-RESPONSIBILITY responsibility="Type the article record's id/title/status/created_at plus an open data bag"]
129
+ // CIL[DOC-CONTEXT module=".framework/src/components/kb/KBArticleEditor.tsx" layer="framework_ui" related_files=""]
130
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
131
+ // CIL[DOC-MICRO logic_summary="Loose shape — extra record fields pass through the index signature" notable_decisions="[]"]
132
+ // CIL[DOC-CONFIDENCE pass="first" score="0.8" flags="none"]
133
+ // CIL[DOC-STABILITY stability="stable"]
112
134
  interface KBRecord {
113
135
  id: string
114
136
  title: string
@@ -146,6 +168,13 @@ interface KBRecord {
146
168
 
147
169
 
148
170
  // CIL[BEGIN stable_id="~/.framework/src/components/kb/KBArticleEditor.tsx:getFieldDefault:function" type="function" identity="getFieldDefault" parent="~/.framework/src/components/kb/KBArticleEditor.tsx"]
171
+ // CIL[DOC-MACRO purpose="Default value for a schema field when no record value exists"]
172
+ // CIL[DOC-RESPONSIBILITY responsibility="Map data_type to a sensible empty value: [] for multiselect/array, false for booleans, null for numbers/dates, first option for selects, '' otherwise"]
173
+ // CIL[DOC-CONTEXT module=".framework/src/components/kb/KBArticleEditor.tsx" layer="framework_ui" related_files=""]
174
+ // CIL[DOC-CONTRACT inputs="def: schema field definition" outputs="default value" errors="none" side_effects="none"]
175
+ // CIL[DOC-MICRO logic_summary="Branch on data_type; select/radio take the first option's value" notable_decisions="[]"]
176
+ // CIL[DOC-CONFIDENCE pass="first" score="0.8" flags="none"]
177
+ // CIL[DOC-STABILITY stability="stable"]
149
178
  function getFieldDefault(def: any): any {
150
179
  if (def.data_type === 'multiselect' || (def.data_type === 'array' && def.options)) return []
151
180
  if (def.data_type === 'boolean' || def.data_type === 'checkbox' || def.data_type === 'switch') return false
@@ -189,6 +218,13 @@ function getFieldDefault(def: any): any {
189
218
 
190
219
 
191
220
  // CIL[BEGIN stable_id="~/.framework/src/components/kb/KBArticleEditor.tsx:initialFormData:function" type="function" identity="initialFormData" parent="~/.framework/src/components/kb/KBArticleEditor.tsx"]
221
+ // CIL[DOC-MACRO purpose="Seed the form's initial values from schema + existing record"]
222
+ // CIL[DOC-RESPONSIBILITY responsibility="Hydrate every schema field from the record (system fields top-level, data fields under record.data) or fall back to field defaults"]
223
+ // CIL[DOC-CONTEXT module=".framework/src/components/kb/KBArticleEditor.tsx" layer="framework_ui" related_files=""]
224
+ // CIL[DOC-CONTRACT inputs="schema, record|null" outputs="formData object" errors="none" side_effects="none"]
225
+ // CIL[DOC-MICRO logic_summary="New records additionally get status='draft' and empty title/content/description" notable_decisions="system:true fields read record[field] directly; others read record.data[field]"]
226
+ // CIL[DOC-CONFIDENCE pass="first" score="0.8" flags="none"]
227
+ // CIL[DOC-STABILITY stability="stable"]
192
228
  function initialFormData(schema: any, record: KBRecord | null): Record<string, any> {
193
229
  const values: Record<string, any> = {}
194
230
  Object.entries(schema?.fields || {}).forEach(([fieldName, fieldDef]: [string, any]) => {
@@ -237,6 +273,13 @@ function initialFormData(schema: any, record: KBRecord | null): Record<string, a
237
273
 
238
274
 
239
275
  // CIL[BEGIN stable_id="~/.framework/src/components/kb/KBArticleEditor.tsx:KBArticleEditor:component" type="component" identity="KBArticleEditor" parent="~/.framework/src/components/kb/KBArticleEditor.tsx"]
276
+ // CIL[DOC-MACRO purpose="Render and operate the schema-driven article editor"]
277
+ // CIL[DOC-RESPONSIBILITY responsibility="Load schema+record, hydrate formData, render the wrapping header, main fields, and metadata sidebar, and run save/publish/delete flows"]
278
+ // CIL[DOC-CONTEXT module=".framework/src/components/kb/KBArticleEditor.tsx" layer="framework_ui" related_files=".framework/src/components/ui/RichTextEditor.tsx, .framework/src/components/shared/SchemaFields.tsx"]
279
+ // CIL[DOC-CONTRACT inputs="KBArticleEditorProps" outputs="editor JSX" errors="save/publish/delete failures in an inline banner" side_effects="admin-data CRUD, kb publish or custom onPublish callback"]
280
+ // CIL[DOC-MICRO logic_summary="Header uses flex-wrap + min-w-0/truncate so title, badge and date wrap instead of clipping behind the buttons; onFormChange fires only for post-init edits via a ref flag; publish saves first then delegates to onPublish or /api/kb?action=publish" notable_decisions="DOMPurify sanitizes stored rich text in view mode (stored-XSS defense)"]
281
+ // CIL[DOC-CONFIDENCE pass="second" score="0.9" flags="none"]
282
+ // CIL[DOC-STABILITY stability="stable"]
240
283
  export function KBArticleEditor({
241
284
  typeSlug,
242
285
  typeName,
@@ -486,27 +529,27 @@ export function KBArticleEditor({
486
529
 
487
530
  return (
488
531
  <div className="flex flex-col h-full">
489
- <div className="px-6 py-3 border-b border-border shrink-0 flex items-center justify-between gap-4">
490
- <div className="flex items-center gap-3 min-w-0">
532
+ <div className="px-6 py-3 border-b border-border shrink-0 flex flex-wrap items-center justify-between gap-x-4 gap-y-2">
533
+ <div className="flex items-center gap-3 min-w-0 flex-wrap">
491
534
  {onBack && (
492
535
  <Button variant="ghost" size="sm" onClick={onBack} className="gap-1 text-muted-foreground">
493
536
  <ArrowLeft className="h-3.5 w-3.5" /> KB
494
537
  </Button>
495
538
  )}
496
- <h1 className="text-base font-semibold truncate">
539
+ <h1 className="text-base font-semibold truncate max-w-[44ch]">
497
540
  {isCreating ? `New ${typeName || typeSlug}` : (record?.title || 'Edit Article')}
498
541
  </h1>
499
542
  {!isCreating && record && (
500
- <div className="hidden sm:flex items-center gap-2">
543
+ <div className="hidden sm:flex items-center gap-2 shrink-0">
501
544
  <Badge variant={record.status === 'published' ? 'default' : 'outline'} className="text-xs">
502
545
  {record.status || 'draft'}
503
546
  </Badge>
504
- <span className="text-xs text-muted-foreground">{formatDateTime(record.created_at)}</span>
547
+ <span className="text-xs text-muted-foreground whitespace-nowrap">{formatDateTime(record.created_at)}</span>
505
548
  </div>
506
549
  )}
507
550
  </div>
508
551
 
509
- <div className="flex items-center gap-2 shrink-0">
552
+ <div className="flex items-center gap-2 flex-wrap">
510
553
  {mode === 'view' && articleId && canEdit && (
511
554
  <Button size="sm" variant="outline" onClick={() => articleId && onEdit?.(articleId)} className="gap-1">
512
555
  <Edit className="h-3.5 w-3.5" /> Edit
@@ -11,6 +11,7 @@
11
11
  import React, { useState } from 'react'
12
12
  import { Sidebar, SidebarContent } from './Sidebar'
13
13
  import { useAuth } from '../../contexts/AuthContext'
14
+ import { useBranding } from '../../hooks/useBranding'
14
15
  import { useLocation } from 'react-router-dom'
15
16
  // CIL[DOC-VECTOR type="context" content="purpose='Module imports and top-level declarations for the Layout component.'; No verified external callers; imports: .framework/src/components/layout/Sidebar.tsx,.framework/src/contexts/AuthContext.tsx"]
16
17
  // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
@@ -50,6 +51,7 @@ interface LayoutProps {
50
51
  export function Layout({ children }: LayoutProps) {
51
52
  const [sidebarOpen, setSidebarOpen] = useState(false)
52
53
  const { user, logout } = useAuth()
54
+ const branding = useBranding()
53
55
  const location = useLocation()
54
56
 
55
57
  const isActive = (href: string) =>
@@ -87,8 +89,8 @@ export function Layout({ children }: LayoutProps) {
87
89
  </svg>
88
90
  </button>
89
91
  <div className="flex items-center gap-3">
90
- <img src="/spine-logo.svg" alt="Spine Framework" className="h-8 w-auto" />
91
- <span className="text-lg font-semibold tracking-tight text-slate-900 whitespace-nowrap">Spine Framework</span>
92
+ <img src={branding.logo_url} alt={branding.app_name} className="h-8 w-auto" />
93
+ <span className="text-lg font-semibold tracking-tight text-slate-900 whitespace-nowrap">{branding.title}</span>
92
94
  </div>
93
95
  </div>
94
96