diff --git a/app/helpers/wrapstodon_helper.rb b/app/helpers/wrapstodon_helper.rb
new file mode 100644
index 0000000000..da3b0d6fad
--- /dev/null
+++ b/app/helpers/wrapstodon_helper.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module WrapstodonHelper
+ def render_wrapstodon_share_data(report)
+ json = ActiveModelSerializers::SerializableResource.new(
+ AnnualReportsPresenter.new([report]),
+ serializer: REST::AnnualReportsSerializer,
+ scope: nil,
+ scope_name: :current_user
+ ).to_json
+
+ # rubocop:disable Rails/OutputSafety
+ content_tag(:script, json_escape(json).html_safe, type: 'application/json', id: 'wrapstodon-data')
+ # rubocop:enable Rails/OutputSafety
+ end
+end
diff --git a/app/javascript/entrypoints/wrapstodon.tsx b/app/javascript/entrypoints/wrapstodon.tsx
new file mode 100644
index 0000000000..e1eebcce57
--- /dev/null
+++ b/app/javascript/entrypoints/wrapstodon.tsx
@@ -0,0 +1,58 @@
+import { createRoot } from 'react-dom/client';
+
+import { Provider as ReduxProvider } from 'react-redux';
+
+import {
+ importFetchedAccounts,
+ importFetchedStatuses,
+} from '@/mastodon/actions/importer';
+import type { ApiAnnualReportResponse } from '@/mastodon/api/annual_report';
+import { Router } from '@/mastodon/components/router';
+import { WrapstodonShare } from '@/mastodon/features/annual_report/share';
+import { IntlProvider, loadLocale } from '@/mastodon/locales';
+import { loadPolyfills } from '@/mastodon/polyfills';
+import ready from '@/mastodon/ready';
+import { setReport } from '@/mastodon/reducers/slices/annual_report';
+import { store } from '@/mastodon/store';
+
+function loaded() {
+ const mountNode = document.getElementById('wrapstodon');
+ if (!mountNode) {
+ throw new Error('Mount node not found');
+ }
+ const propsNode = document.getElementById('wrapstodon-data');
+ if (!propsNode) {
+ throw new Error('Initial state prop not found');
+ }
+
+ const initialState = JSON.parse(
+ propsNode.textContent,
+ ) as ApiAnnualReportResponse;
+
+ const report = initialState.annual_reports[0];
+ if (!report) {
+ throw new Error('Initial state report not found');
+ }
+ store.dispatch(importFetchedAccounts(initialState.accounts));
+ store.dispatch(importFetchedStatuses(initialState.statuses));
+
+ store.dispatch(setReport(report));
+
+ const root = createRoot(mountNode);
+ root.render(
+
+
+
+
+
+
+ ,
+ );
+}
+
+loadPolyfills()
+ .then(loadLocale)
+ .then(() => ready(loaded))
+ .catch((err: unknown) => {
+ console.error(err);
+ });
diff --git a/app/javascript/mastodon/components/logo.tsx b/app/javascript/mastodon/components/logo.tsx
index fe9680d0e3..c0ed4d78a7 100644
--- a/app/javascript/mastodon/components/logo.tsx
+++ b/app/javascript/mastodon/components/logo.tsx
@@ -1,3 +1,5 @@
+import classNames from 'classnames';
+
import logo from '@/images/logo.svg';
export const WordmarkLogo: React.FC = () => (
@@ -7,8 +9,12 @@ export const WordmarkLogo: React.FC = () => (
);
-export const IconLogo: React.FC = () => (
-