From a983837f86c08b07278f2d7d786c7d9342a2011b Mon Sep 17 00:00:00 2001 From: Logan Besecker Date: Thu, 1 Oct 2026 13:42:51 -0700 Subject: [PATCH] Put the sponsor sidebar on every page type, with the book first and last Only listing pages carried the sponsor block. Tool, prompt, resource and agent pages, changelogs, the directory, the submit form and the home page had none. - Layouts.app takes sponsors={true}: content in a main column, the sponsor sidebar beside it, pinned by its bottom so it scrolls with the page and then holds - On the home page the sidebar starts beside the featured servers, so the full-width hero, search and numbers above keep their width - The book now closes the block as well as opening it; the sidebar settles with its bottom in view, so the last tile is the one that stays on screen - The listing keeps its own sidebar and shares the one hook - /book is left out: a book tile there would link to the page itself Closes #89 --- Pages affected: - [browse MCP servers](https://ai.mcpharbor.dev/servers) -- gains the sponsor sidebar. - [MCP Harbor](https://ai.mcpharbor.dev/) -- sponsors beside the featured servers. - [MCP server changelog](https://ai.mcpharbor.dev/changelog) -- gains the sponsor sidebar. Co-Authored-By: Claude Opus 5.5 --- lib/mcp_registry_web/components/layouts.ex | 115 ++++++- lib/mcp_registry_web/live/changelog_live.ex | 2 +- .../live/server_live/agent.ex | 2 +- .../live/server_live/capabilities.ex | 2 +- .../live/server_live/changelog.ex | 2 +- lib/mcp_registry_web/live/server_live/home.ex | 307 +++++++++--------- .../live/server_live/index.ex | 2 +- lib/mcp_registry_web/live/server_live/new.ex | 2 +- lib/mcp_registry_web/live/server_live/show.ex | 43 +-- .../live/server_live/tools.ex | 6 +- .../live/server_live_test.exs | 2 +- .../live/sponsor_sidebar_test.exs | 70 ++++ 12 files changed, 349 insertions(+), 206 deletions(-) create mode 100644 test/mcp_registry_web/live/sponsor_sidebar_test.exs diff --git a/lib/mcp_registry_web/components/layouts.ex b/lib/mcp_registry_web/components/layouts.ex index fc8b11c..4ab8ece 100644 --- a/lib/mcp_registry_web/components/layouts.ex +++ b/lib/mcp_registry_web/components/layouts.ex @@ -38,6 +38,13 @@ defmodule McpRegistryWeb.Layouts do default: false, doc: "drop the reading-width cap, for the landing page's full-bleed sections" + attr :sponsors, :boolean, + default: false, + doc: """ + put the content in a main column with the sponsor sidebar beside it. A page + with a sidebar of its own (a listing) keeps it, and uses the same hook. + """ + slot :rail, doc: """ A full-width bar pinned under the site header — breadcrumbs on the left, @@ -96,8 +103,18 @@ defmodule McpRegistryWeb.Layouts do
-
- {render_slot(@inner_block)} +
+ <%= if @sponsors do %> +
+
{render_slot(@inner_block)}
+ <.sponsor_sidebar class="lg:col-span-4" /> +
+ <% else %> + {render_slot(@inner_block)} + <% end %>
@@ -227,7 +244,80 @@ defmodule McpRegistryWeb.Layouts do end @doc """ - The sponsored block: four square slots, two up. + A right-hand sidebar that scrolls with the page and then holds, holding the + sponsored block below whatever the caller puts in it. + + The sidebar is taller than the window -- five 250px tiles alone are -- so + pinning it by its top, the usual sticky sidebar, would show only its first + screenful until the main column ran out, sometimes 10,000 words later. The + `.StickySidebar` hook pins it by its bottom instead: it scrolls with the page + until its last item is in view, then stays. + + ## Examples + + + + A page with its own sidebar can use the behaviour without the component: + `phx-hook="McpRegistryWeb.Layouts.StickySidebar"`, plus + `phx-mounted={JS.ignore_attributes(["style"])}`. + """ + attr :id, :string, default: "sponsor-sidebar" + attr :class, :any, default: nil + slot :inner_block + + def sponsor_sidebar(assigns) do + ~H""" + + """ + end + + @doc """ + The sponsored block: five square slots, one per row -- the book first and + last, and the three slots for sale between. Lives here rather than in `CoreComponents` because it is site chrome: it belongs to the page frame, not to the listing being described, and any page @@ -256,15 +346,22 @@ defmodule McpRegistryWeb.Layouts do @enquiry_subject "Interested Sponsor for MCP Harbor" @enquiry_body "I'm interested in bidding $____ for a Sponsor position on MCP Harbor." + @book_slot %{ + file: "Book.png", + alt: "MCP Server Optimization — the book behind this registry", + kind: :book + } + + # The book opens and closes the block. The sidebar is taller than the window + # and settles with its bottom in view, so the last tile is the one a reader + # keeps seeing for the rest of a long page -- and the first is the one they + # see before they scroll at all. @sponsor_slots [ - %{ - file: "Book.png", - alt: "MCP Server Optimization — the book behind this registry", - kind: :book - }, + @book_slot, %{file: "sponsor-1.png", alt: "Sponsor slot available — email to enquire", kind: :enquiry}, %{file: "sponsor-2.png", alt: "Sponsor slot available — email to enquire", kind: :enquiry}, - %{file: "sponsor-3.png", alt: "Sponsor slot available — email to enquire", kind: :enquiry} + %{file: "sponsor-3.png", alt: "Sponsor slot available — email to enquire", kind: :enquiry}, + @book_slot ] def sponsors(assigns) do diff --git a/lib/mcp_registry_web/live/changelog_live.ex b/lib/mcp_registry_web/live/changelog_live.ex index 8f39453..c64e23f 100644 --- a/lib/mcp_registry_web/live/changelog_live.ex +++ b/lib/mcp_registry_web/live/changelog_live.ex @@ -31,7 +31,7 @@ defmodule McpRegistryWeb.ChangelogLive do @impl true def render(assigns) do ~H""" - +

What changed in MCP servers diff --git a/lib/mcp_registry_web/live/server_live/agent.ex b/lib/mcp_registry_web/live/server_live/agent.ex index 98a6804..2020867 100644 --- a/lib/mcp_registry_web/live/server_live/agent.ex +++ b/lib/mcp_registry_web/live/server_live/agent.ex @@ -103,7 +103,7 @@ defmodule McpRegistryWeb.ServerLive.Agent do @impl true def render(assigns) do ~H""" - + <:rail>