Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions .github/workflows/desktop-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ on:
push:
tags:
- "desktop-v*"
workflow_dispatch:

permissions:
contents: write

jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04

steps:
- name: Checkout repository
Expand All @@ -21,13 +22,21 @@ jobs:
with:
node-version: "lts/*"

- name: Inject version from tag
working-directory: desktop-app
run: |
VERSION="${GITHUB_REF_NAME#desktop-v}"
jq --arg v "$VERSION" '.version = $v' neutralino.config.json > tmp.json \
&& mv tmp.json neutralino.config.json
echo "::notice::Building version $VERSION"

- name: Setup Neutralinojs binaries
working-directory: desktop-app
run: npm run setup

- name: Build all binaries (embedded + portable)
working-directory: desktop-app
run: npm run build:all
run: npm run build

- name: Stage release assets
working-directory: desktop-app
Expand Down Expand Up @@ -63,6 +72,7 @@ jobs:
--exclude='desktop-app/bin' \
--exclude='desktop-app/node_modules' \
--exclude='desktop-app/output' \
--exclude="desktop-app/$STAGING" \
--exclude='.git' \
desktop-app/
cd desktop-app
Expand All @@ -74,6 +84,6 @@ jobs:
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: "Markdown Viewer Desktop ${{ github.ref_name }}"
name: "${{ github.ref_name }}"
generate_release_notes: true
files: desktop-app/release-assets/*
75 changes: 39 additions & 36 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ name: Build and Push Docker Image

on:
push:
branches: [ main ]
branches: [main]
paths-ignore:
- "desktop-app/**"
pull_request:
branches: [ main ]
branches: [main]
paths-ignore:
- "desktop-app/**"

env:
REGISTRY: ghcr.io
Expand All @@ -18,37 +22,36 @@ jobs:
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Salesforce
.sfdx/

# Node
node_modules/
dist/
*.log
npm-debug.log*

# Generated desktop resources (rebuilt by prepare.js)
desktop-app/resources/
desktop-app/bin/

# Vite cache
.vite/

# OS generated files
.DS_Store
Thumbs.db

# Editor
.vscode/
.idea/
.vercel/*
!.vercel/project.json
.env*.local

# Claude Code
.claude/
1 change: 1 addition & 0 deletions .vercel/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"projectId":"prj_caH2tkxw3dHNcLO0DzCbVMNObzA3","orgId":"team_lzDr5cAAkpQFExaMSnzJ5zvc","projectName":"markdown-viewer"}
94 changes: 94 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Markdown Viewer — Agent Instructions

A vanilla JS web app + Neutralinojs desktop app for rendering Markdown. Uses Vite for bundling.

## Build & Dev

```bash
# Install dependencies
npm install

# Web dev server (http://localhost:5173)
npm run dev

# Build web app to dist/
npm run build

# Desktop setup + dev
npm run desktop:install # Install desktop-app dependencies (once)
npm run desktop:prepare # Build + copy to resources/
npm run desktop:dev # Run desktop app

# Desktop build (Windows/Linux/macOS)
npm run desktop:build
```

No tests, no linter.

## Architecture

```
web/ # Core app — the single source of truth
index.html # UI markup, MathJax CDN tag only
styles.css # CSS custom properties; [data-theme="dark"] for dark mode
src/ # ES modules
main.js # Entry point + event wiring + CSS imports
state.js # Shared mutable state + constants
dom.js # DOM element references (initDom pattern)
preprocessors.js # Markdown transformers (ADO TOC, callouts)
mermaid-utils.js # Mermaid init / zoom / drag / export
scroll-sync.js # Anchor cache + scroll interpolation
render.js # Markdown render pipeline + stats
import-export.js # File open/save, SharePoint/ADO import
pdf-export.js # PDF page-break + canvas logic
view-mode.js # View mode toggle + resize divider
vite.config.js # Vite build configuration
dist/ # Vite output (gitignored)
desktop-app/ # Thin Neutralinojs wrapper
prepare.js # Copies dist/ → resources/, injects Neutralino SDK
resources/js/desktop-main.js # Desktop-only: tray menu, window close
```

**Shared code model**: `desktop-app/prepare.js` copies Vite's `dist/` output at build time. Edit `web/src/` for any logic or UI change; never edit `desktop-app/resources/` directly.

## Key Conventions

- **ES modules** — Application logic is split across `web/src/` modules. Shared state in `state.js`, DOM refs in `dom.js`.
- **npm dependencies** — marked, highlight.js, mermaid, bootstrap, etc. are npm packages bundled by Vite.
- **MathJax on CDN** — Only exception; config-based loading is simpler than bundling.
- **Debouncing**: render is debounced 100 ms (`RENDER_DELAY`); scroll sync is debounced 10 ms.
- **View modes**: `'editor'`, `'split'`, `'preview'` — managed in `state.currentViewMode`.
- **Versioning**: Desktop uses CalVer `YYYY.M.P` (e.g., 2026.2.0), tagged as `desktop-vYYYY.M.P`.
- **Mobile breakpoint**: 768 px. Separate mobile hamburger menu; desktop has a toolbar.

## Module Dependencies (no cycles)

```
preprocessors.js ← no internal deps
state.js ← no internal deps
dom.js ← no internal deps

mermaid-utils.js ← state, dom
scroll-sync.js ← state, dom
view-mode.js ← state, dom, scroll-sync
render.js ← state, dom, preprocessors, mermaid-utils, scroll-sync
import-export.js ← state, dom, render
pdf-export.js ← state, dom, preprocessors

main.js ← all of the above
```

## Deployment

| Target | Trigger | Notes |
|--------|---------|-------|
| Vercel (web) | Push to `main` | Runs `npm run build`, serves `dist/` |
| GHCR Docker | Push to `main` | See `Dockerfile` / `docker-compose.yml` |
| GitHub Release | Tag `desktop-v*` | `.github/workflows/build-desktop.yml` |

## Pitfalls

- Neutralinojs binaries are gitignored (`desktop-app/bin/`). Run setup before first desktop build.
- macOS tray menu is intentionally disabled (upstream Neutralinojs bug #615).
- Security headers are set in both `vercel.json` and `Dockerfile` — keep them in sync when adding new CSP directives.
- Vite pre-bundles mermaid and dayjs to handle CommonJS interop — see `optimizeDeps` in `vite.config.js`.
80 changes: 80 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Web development
npm run dev # Vite dev server at http://localhost:5173
npm run build # Vite production build → dist/
npm run preview # Preview production build locally

# Desktop development
npm run desktop:prepare # Build + copy dist/ into desktop-app/resources/
npm run desktop:dev # Run Neutralinojs app with hot-reload
npm run desktop:build # Build cross-platform binaries (Windows .exe, Linux/macOS .tar.gz)
npm run desktop:install # Install desktop-app dependencies (run once after clone)
```

There is no test framework, linter, or TypeScript compiler in this project.

## Architecture

This is a **vanilla JS markdown editor** targeting two platforms from a single shared codebase:

- **`web/`** — The canonical app (HTML + CSS + ES modules), deployed to Vercel
- **`desktop-app/`** — Neutralinojs wrapper; `prepare.js` copies `dist/` into `resources/` and injects the Neutralinojs SDK script tag into `index.html` at build time

### Shared-code model

`desktop-app/resources/` is never edited directly — it is always regenerated from `dist/` (Vite output) by `prepare.js`. All feature work happens in `web/src/`.

### ES Modules structure (`web/src/`)

Application logic is split into ES modules:

| Module | Purpose |
|--------|---------|
| `main.js` | Entry point, event wiring, CSS imports |
| `state.js` | Shared mutable state + constants |
| `dom.js` | DOM element references (`initDom()` pattern) |
| `preprocessors.js` | Pure markdown transformers (ADO TOC, callouts) |
| `mermaid-utils.js` | Mermaid init / zoom / drag / export |
| `scroll-sync.js` | Anchor cache + interpolation + scroll handlers |
| `render.js` | Markdown render pipeline + document stats |
| `import-export.js` | File open/save, SharePoint/ADO import, HTML export |
| `pdf-export.js` | PDF page-break + canvas logic |
| `view-mode.js` | View mode toggle + resize divider + pane widths |

Key subsystems:

- **Rendering pipeline**: marked → highlight.js (syntax) + MathJax (LaTeX) + Mermaid (diagrams) + JoyPixels (emoji) → DOMPurify sanitize → inject into preview pane. Render is debounced 100 ms.
- **View modes**: `'editor'` / `'split'` / `'preview'` toggled via toolbar buttons (desktop) or hamburger menu (mobile).
- **Scroll sync** (split view): Anchor-based line-position cache + piecewise-linear interpolation maps editor scroll position to preview position. Sync is debounced 10 ms.
- **Export**: PDF via jsPDF + html2canvas; HTML/Markdown raw download via file-saver.
- **Platform detection**: Checks for `window.NL_VERSION` to enable desktop-only features (file open/save via Neutralino FS API, system tray via `desktop-app/resources/js/desktop-main.js`).

### Dependencies

Runtime dependencies are npm packages bundled by Vite:
- marked, highlight.js, mermaid, dompurify, file-saver, jspdf, html2canvas
- bootstrap, bootstrap-icons, github-markdown-css, emoji-toolkit

**MathJax** remains on CDN (config-based loading is simpler than bundling).

### Build tool

**Vite** handles development server, ES module bundling, and production builds. Config in `vite.config.js`.

### Deployment

| Target | Trigger | Config |
|--------|---------|--------|
| Vercel (web) | Push to `main` | `vercel.json` |
| Docker | Push to `main` | `Dockerfile`, nginx security headers |
| GitHub Release (desktop) | Tag `desktop-v*` (CalVer, e.g. `desktop-v2026.2.0`) | `.github/workflows/build-desktop.yml` |

### Theming

CSS custom properties (`--bg-color`, `--text-color`, etc.) define the palette. The `[data-theme="dark"]` attribute on `<html>` switches themes; the preference is persisted in `localStorage`.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
<a href="#-license">License</a>
</div>


## 🚀 Overview

Markdown Viewer is a professional, full-featured Markdown editor and preview application that runs entirely in your browser. It provides a GitHub-style rendering experience with a clean split-screen interface, allowing you to write Markdown on one side and instantly preview the formatted output on the other.


## ✨ Features

- **GitHub-style Markdown rendering** - See your Markdown exactly as it would appear on GitHub
Expand Down Expand Up @@ -56,6 +58,24 @@ Markdown Viewer is a professional, full-featured Markdown editor and preview app
5. **Toggle Dark Mode** - Click the moon icon to switch between light and dark themes
6. **Toggle Sync Scrolling** - Enable/disable synchronized scrolling between panels

## 💻 Run With npm

From the project root:

```bash
npm install
npm run dev
```

This starts a local server at `http://localhost:5173`.

Desktop app shortcuts are also available from the root:

```bash
npm run desktop:install
npm run desktop:dev
```

### Mermaid Diagram Toolbar

When a Mermaid diagram is rendered, hover over it to reveal a small toolbar with the following actions:
Expand Down
1 change: 1 addition & 0 deletions desktop-app/.dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Build-generated resources
resources/js/script.js
resources/js/neutralino*
resources/styles.css
resources/assets/
resources/index.html
Expand Down
Loading