---
name: markdown-to-public-site
description: Repackage local Markdown files or cleaned web docs into Rob’s publicly hosted Zo site by copying into web/public/res, adding a reading route, verifying locally, committing, then publishing to the open internet.
compatibility: Created for Zo Computer
metadata:
  author: rob.zo.computer
---

# markdown-to-public-site

## Motivation / intent

I frequently want to take:

- **Docs already in my workspace** (research notes, reports, personal-data analyses, etc.), or
- **Docs from the internet that I’ve cleaned up** (articles, recipes, references)

…and **repackage them into a durable, readable, minimal page** on my publicly hosted Zo site.

This workflow is explicitly about:

- turning “a thing I’m reading/using” into “a clean page I can share”
- stripping noise (ads, cookie banners, nav chrome, affiliate junk, SEO filler)
- keeping a consistent reading layout and a stable URL

Important nuance:

- the **`zite-…` URL is just the private dev site** (behind auth; useful for preview)
- at the **very end**, publish to the open internet via the **`publish_site` tool** and return the **public URL**

## When to use

Use this whenever the goal is “make this readable Markdown available on my public Zo site”, including:

- a web article you want to keep/share in a clean form
- a cooking recipe where you want *only* the recipe (ingredients + steps + notes)
- an internal document (notes, report, analysis) you want to publish

## Inputs

- **Source** (required):
  - a markdown file path in the workspace, or
  - a URL
- **Route name** (optional): desired path segment, e.g. `my-article` → `/my-article`
  - if omitted, derive a sensible kebab-case slug from title / filename
- **Sensitivity / privacy intent** (implicit but critical): if the content includes personal data, secrets, or anything not intended for public web, stop and ask.

## Procedure

### 0) Decide a route slug

Pick a short, stable **kebab-case** slug.

- avoid collisions with existing routes in `web/public/index.tsx`
- avoid collisions with existing files in `web/public/res/`

If there’s a collision, choose a variant (e.g. `recipe-chili-2`, `zo-report-2026-01`).

### 1) Get/prepare the markdown

#### Scenario A — Source is already a markdown file

- Read it.
- Optionally tighten formatting (headings, lists, code blocks) so it renders well.

#### Scenario B — Source is a URL (web article / recipe / reference)

1. **Save the page** using `save_webpage(url)`.
2. **Read the saved markdown** fully.
3. **Clean aggressively** to make it look “written as markdown”, not “dumped from a web page”.

Cleaning checklist (apply as relevant):

- remove nav/header/footer/sidebar, “related posts”, newsletter prompts, cookie notices
- remove ads / affiliate blocks / promo CTAs / app-download prompts
- remove social share widgets and “follow us” sections
- remove tracking params from links (utm_*, etc.) and dedupe repeated links
- delete empty links, broken images, stray HTML comments, script remnants
- fix heading hierarchy (one H1; then H2/H3…)
- normalize whitespace; fix broken lists and code fences
- preserve *real* citations/footnotes (if meaningful)

Recipe-specific cleanup targets:

- ensure there’s a clear **Ingredients** section
- ensure there’s a clear **Instructions / Directions** section
- keep useful metadata (servings, time, temperature)
- drop the “life story” and SEO preamble

4. **Overwrite** the saved file with the cleaned markdown.

#### Scenario C — Source is an internal doc but not markdown

Convert to markdown first (e.g., via pandoc if it’s a doc format), then treat as Scenario A.

### 2) Add it to the public site

Assumes the public site lives at `web/public`.

1. Copy the markdown into the site resources directory:

```bash
cd /home/workspace/web/public
cp <source_file_path> res/
```

2. Add a new route in `web/public/index.tsx` that:

- reads the file from `./res/<filename>`
- renders it in the existing reading layout
- parses markdown client-side (using `marked`)

Route pattern (adapt to match current code style in the repo):

```ts
app.get("/<route_name>", async (c) => {
  try {
    const file = Bun.file("./res/<filename>");
    const content = await file.text();

    return c.html(
      <Layout isReading={true}>
        <div class="reading-container">
          <div id="markdown-content"></div>
          <a href="/" class="back-link">← Back to home</a>
        </div>
        <script
          dangerouslySetInnerHTML={{
            __html: `
              const markdown = ${JSON.stringify(content)};
              document.getElementById('markdown-content').innerHTML = marked.parse(markdown);
            `,
          }}
        />
      </Layout>,
    );
  } catch (error) {
    return c.text("<description> not found", 404);
  }
});
```

3. Replace placeholders:

- `<route_name>` → your slug
- `<filename>` → the actual file in `res/`
- `<description>` → human-friendly label for the 404

### 3) Verify locally (dev preview)

The site is a managed service that restarts on changes.

Smoke test the route returns HTML:

```bash
curl -s http://localhost:40290/<route_name> | head -20
```

If it fails initially, wait a few seconds and retry (restart lag).

### 4) Commit

```bash
cd /home/workspace/web/public
git add -A
git commit -m "Add <route_name> page

- Copied <source_file_path> to res/
- Added /<route_name> route"
```

### 5) Publish to the open internet (required final step)

Call the **`publish_site` tool** at the very end to publish the updated site and obtain the **public URL**.

- The returned published URL is the one that works on the open internet.
- The `zite-…` URL is only the private/dev instance behind auth.

## What to report back (after a successful run)

- which file was added to `web/public/res/`
- the new route path `/<route_name>`
- confirmation that `curl` returned HTML
- git commit hash/message
- **public published URL from `publish_site`** (plus the `zite-…` preview URL if helpful)

## Guardrails

- Don’t publish secrets or sensitive personal data without explicit intent.
- If content is copyrighted and the goal is public distribution, ask for confirmation and/or prefer a short excerpt + link.
