## Implementation Plan: Markdown Rendering Toggle for Gist Source Code ### Objective Add a source/rendered toggle on the gist detail page when the gist language is `"markdown"`. The user can switch between raw markdown source and rendered HTML. Editing continues to work on the raw source via the existing CodeMirror modal. ### Scope of Changes **Only two files** need modification. No router, model, database, or JavaScript class changes. | File | Change | |------|--------| | `templates/gists/gist_detail.html` | Replace the flat code block with a conditional toggle container for markdown gists | | `static/css/gists.css` | Add styles for tab bar, tabs, and preview pane | ### Detailed Steps #### 1. Template: `templates/gists/gist_detail.html` Locate the section from approximately line 36 to 42: ```html
{{ gist['source_code'] }}
``` Replace with a conditional block. For markdown gists, use the existing `Tabs.js` component via `data-tabs` attributes. For all other languages, keep the existing structure unchanged. ```html {% if gist['language'] == 'markdown' %}
{{ gist['source_code'] }}
{{ render_content(gist['source_code']) | safe }}
{% else %}
{{ gist['source_code'] }}
{% endif %} ``` Key points: - `data-tabs` on the outer `div.gist-code-block` triggers `Tabs.js` initialization. - `render_content()` is a Jinja global already registered and used for the description. - `| safe` is necessary because `render_content()` returns escaped HTML; without it Jinja would double-escape. - The source tab preserves the original `
` block with syntax highlighting.
- The preview pane reuses the global `.rendered-content` class (loaded via `base.html` → `markdown.css`).

#### 2. Stylesheet: `static/css/gists.css`

Append the following styles (anywhere in the file). They apply only inside the markdown toggle container.

```css
.gist-markdown-toggle .tab-bar {
    display: flex;
    border-bottom: 1px solid #ddd;
    margin-bottom: 1em;
}

.gist-markdown-toggle .tab {
    padding: 0.5em 1em;
    cursor: pointer;
    text-decoration: none;
    color: #888;
    border: 1px solid transparent;
    border-radius: 4px 4px 0 0;
    background: transparent;
    border-bottom: none;
}

.gist-markdown-toggle .tab.active {
    color: #333;
    background: #fff;
    border-color: #ddd #ddd transparent;
}

.gist-markdown-toggle .gist-markdown-preview {
    padding: 1em;
    border: 1px solid #ddd;
    border-radius: 4px;
    min-height: 3em;
    background: #fff;
}
```

#### 3. No other changes

- `routers/gists.py` – no change. The template can read `gist['language']` directly.
- `static/js/GistEditor.js` – no change. The toggle is for viewing only.
- `static/js/Tabs.js` – already wired in `Application.js`. No additional initialization needed.
- No model, migration, or schema changes.

### Verification and Definition of Done

- [ ] **Template change** applied: `gist_detail.html` conditionally renders the toggle container when `gist['language'] == 'markdown'`.
- [ ] **CSS change** applied: styles for tab bar, tabs, and preview pane added to `gists.css`.
- [ ] **Lint passes**: `pip install -q ruff && ruff check .` – exit 0.
- [ ] **Unit tests pass**: `PIP_REQUIRE_VIRTUALENV=0 pip install -e '.[dev]' -q && make test-unit` – exit 0, no test failures.
- [ ] **Non-markdown gists unaffected**: viewing a gist with language `"python"` (or any other) shows only the raw code block, no tabs.
- [ ] **Toggle works**: on a markdown gist, two tabs appear – "Source" (default active) and "Preview". Clicking "Preview" renders the markdown (headings, bold, lists, code blocks). Clicking "Source" returns to raw source.
- [ ] **Tabs.js initializes without errors**: no console errors; the `data-tabs` attribute is recognized.
- [ ] **No new dependencies**: no new Python packages, no new JS vendor files.
- [ ] **No database changes**: no migrations, no new columns, no model modifications.