import { Link, Route, Routes, useParams } from 'react-router'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { useCatalogue, usePlugin, type CatalogueEntry, type Load } from './catalogue'; /** * The public catalogue. * * It is a WINDOW, and that is the whole design. Nothing here installs anything: installing means cloning * a repository, pushing a schema and starting a process on someone's own machine, and a website has no * business being able to do that. So every plugin page ends at the same place — copy the app name, open * your own Officer, install it there. * * That constraint is what keeps this side simple enough to be public: no accounts, no sessions, no write * routes, nothing to compromise. The interesting security property of this store is that there is * nothing here worth attacking. */ export const App = () => (
} /> } /> } />
); const Header = () => (
officer marketplace officer.dev →
); const Footer = () => ( ); /** One place where loading, empty and broken are told apart — see `useJson` for why that matters. */ function Pending({ load, children }: { load: Load; children: (data: T) => React.ReactNode }) { if (load.state === 'loading') return

Loading…

; if (load.state === 'error') return

{load.message}

; return <>{children(load.data)}; } const CataloguePage = () => { const load = useCatalogue(); return (

Plugins

Everything here is free and open. Install from the marketplace inside your own Officer — nothing is installed from this page.

{(plugins) => plugins.length === 0 ? (

No plugins listed yet.

) : (
{plugins.map((plugin) => ( ))}
) }
); }; const PluginCard = ({ plugin }: { plugin: CatalogueEntry }) => (

{plugin.label}

{plugin.summary}

{plugin.publisher} · v{plugin.version}

); /** * Artwork if the repository ships it, a coloured initial if not. * * No lucide here on purpose. The platform can draw `icon: 'Network'` because it bundles the icon set; * pulling that whole dependency into a public page to render three glyphs is not a trade worth making, * and a letter in the plugin's own colour is honest about what it is. */ const Icon = ({ plugin }: { plugin: CatalogueEntry }) => plugin.iconUrl ? ( { ev.currentTarget.style.display = 'none'; }} /> ) : (
{plugin.label.charAt(0).toUpperCase()}
); const PluginPage = () => { const { appName } = useParams<{ appName: string }>(); const load = usePlugin(appName ?? ''); return (
← All plugins {(plugin) => ( <>

{plugin.label}

{plugin.summary}

{plugin.publisher} · v{plugin.version} · requires Officer {plugin.platform}

{plugin.permissions.length > 0 && (

Permissions it asks for

    {plugin.permissions.map((permission) => (
  • {permission.label}

    {permission.description}

  • ))}
)} {plugin.osDependencies && plugin.osDependencies.length > 0 && (

Software it installs on your machine

    {plugin.osDependencies.map((dep) => (
  • {dep.binary}

    {dep.reason}

  • ))}
)} {plugin.readme ? (
{plugin.readme}
) : (

This plugin has no README yet.

)} )}
); }; /** What to do with a plugin you want. Not a button — see the note on `App`. */ const Install = ({ appName, source }: { appName: string; source: string }) => (

Installing it

Open your own Officer, go to Marketplace, and install{' '} {appName} from there. Your server clones it directly from the repository below — nothing passes through this site.

{source}
); const Missing = () => (

Nothing here.

Back to the catalogue
);