Skip to content

Commit dd41fd3

Browse files
authored
Merge pull request #29 from mhersson/feature/presentation-mode
Feature/presentation mode
2 parents 95c5885 + b64511d commit dd41fd3

73 files changed

Lines changed: 4596 additions & 695 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ assets/*
66
cover.out
77
# Added by goreleaser init:
88
dist/
9+
docs/plans

.golangci.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,14 @@ linters:
2424
- unconvert # Remove unnecessary type conversions
2525
- unparam
2626
- whitespace
27-
- wsl
27+
- wsl_v5
2828
settings:
2929
nakedret:
3030
max-func-lines: 5
31+
wsl_v5:
32+
allow-first-in-block: true
33+
allow-whole-block: false
34+
branch-max-lines: 2
3135
exclusions:
3236
generated: lax
3337
presets:

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ fmt: ## Run go fmt against code.
5454
vet: ## Run go vet
5555
go vet ./...
5656

57+
lint: fmt vet ## Run golangci-lint
58+
golangci-lint run
59+
5760
build: fmt vet ## Build the binary.
5861
@go build -ldflags $(LDFLAGS) .
5962

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ preview feature that works alongside these editors. Additionally, `mpls` is
2222
compatible with any editor that supports the Language Server Protocol (LSP),
2323
making it a versatile tool for Markdown editing across various platforms. For
2424
users of Visual Studio Code and Zed, there are also dedicated extensions
25-
available at [mpls-vscode-client](https://github.com/mhersson/mpls-vscode-client)
26-
and [mpls for Zed](https://zed.dev/extensions/mpls),
25+
available at
26+
[mpls-vscode-client](https://github.com/mhersson/mpls-vscode-client) and
27+
[mpls for Zed](https://zed.dev/extensions/mpls),
2728

2829
![demo](screenshots/demo.gif)
2930

@@ -45,6 +46,10 @@ and [mpls for Zed](https://zed.dev/extensions/mpls),
4546
perfect for focused editing.
4647
- **Multi-tab mode** (`--tabs`): Each file opens in its own browser tab for
4748
side-by-side viewing.
49+
- **Presentation Mode**: Automatically transform your markdown into a slideshow
50+
presentation, or use explicit markers for full control over slide boundaries
51+
and layout.
52+
- Read more in the [presentation mode documentation](presentation-mode.md)
4853

4954
### Built with Goldmark
5055

@@ -61,9 +66,6 @@ rendering experience:
6166

6267
- Github Flavored Markdown: Goldmark's built in GFM extension ensures Table,
6368
Strikethrough, Linkify and TaskList elements are displayed correctly.
64-
- Image Rendering: The [img64](https://github.com/tenkoh/goldmark-img64)
65-
extension allows for seamless integration of images within your Markdown
66-
files.
6769
- Math Rendering: The [katex](https://github.com/FurqanSoftware/goldmark-katex)
6870
extension enables the rendering of LaTeX-style mathematical expressions using
6971
KaTeX. _Please note that the KaTeX extension requires `cgo` and will only be
@@ -74,6 +76,8 @@ rendering experience:
7476
- Syntax highlighting: The
7577
[highlighting](https://github.com/yuin/goldmark-highlighting) extension adds
7678
syntax-highlighting to the fenced code blocks.
79+
- GitHub-style Alerts: Built-in support for `[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`,
80+
`[!WARNING]`, and `[!CAUTION]` blockquotes, rendered as styled alert boxes.
7781

7882
**Optional**
7983

@@ -256,12 +260,8 @@ The following options can be used when starting `mpls`:
256260
With `--tabs`, each file opens in its own browser tab with a unique URL. In
257261
single-page mode, link clicks update the preview; in multi-tab mode, they
258262
open new tabs.
259-
5. Built-in themes include: `catppuccin-mocha`, `catppuccin-frappe`,
260-
`catppuccin-macchiato`, `catppuccin-latte`, `tokyonight`, `tokyonight-storm`,
261-
`tokyonight-moon`, `tokyonight-day`, `gruvbox-dark`, `gruvbox-light`,
262-
`solarized-dark`, `solarized-light`, `nord`, `dracula`, `rose-pine`,
263-
`ayu-dark`, `ayu-light`, `everforest-dark`, and more. Use `--list-themes` to
264-
see all available themes. Default theme is `light`.
263+
5. See the [theme gallery](screenshots/themes/README.md) for screenshots of all
264+
available themes, or use `--list-themes` to list them. Default is `light`.
265265
266266
## Configuration examples
267267

cmd/command.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ var command = &cobra.Command{
5252
previewserver.EnableTabs = enableTabs
5353

5454
cmd.Printf("mpls %s - press Ctrl+D to quit.\n", cmd.Version)
55+
5556
previewserver.OpenBrowserOnStartup = !noAuto
57+
5658
mpls.Run()
5759
},
5860
}
@@ -92,22 +94,28 @@ func Execute() {
9294
}
9395

9496
func init() {
95-
command.Flags().StringVar(&previewserver.Browser, "browser", "", "Specify the web browser to use for the preview")
97+
// Persistent flags available to all subcommands
98+
command.PersistentFlags().StringVar(&previewserver.Browser, "browser", "", "Specify the web browser to use for the preview")
99+
command.PersistentFlags().StringVar(&previewserver.Theme, "theme", "light", "Set the preview theme (light, dark, or any of the provided themes)")
100+
command.PersistentFlags().IntVar(&previewserver.FixedPort, "port", 0, "Set a fixed port for the preview server")
101+
102+
// Local flags for main LSP command only
96103
command.Flags().StringVar(&parser.CodeHighlightingStyle, "code-style", "catppuccin-mocha", "Higlighting style for code blocks")
97104
command.Flags().BoolVar(&darkMode, "dark-mode", false, "Enable dark mode (deprecated: use --theme dark instead)")
98105
command.Flags().BoolVar(&listThemes, "list-themes", false, "List all available themes and exit")
99-
command.Flags().StringVar(&previewserver.Theme, "theme", "light", "Set the preview theme (light, dark, or any of the provided themes)")
100106
command.Flags().BoolVar(&parser.EnableEmoji, "enable-emoji", false, "Enable emoji support")
101107
command.Flags().BoolVar(&parser.EnableFootnotes, "enable-footnotes", false, "Enable footnotes")
102108
command.Flags().BoolVar(&parser.EnableWikiLinks, "enable-wikilinks", false, "Enable [[wiki]] style links")
103109
command.Flags().BoolVar(&mpls.TextDocumentUseFullSync, "full-sync", false, "Sync entire document for every change")
104110
command.Flags().BoolVar(&noAuto, "no-auto", false, "Don't open preview automatically")
105-
command.Flags().IntVar(&previewserver.FixedPort, "port", 0, "Set a fixed port for the preview server")
106111
command.Flags().StringVar(&plantuml.BasePath, "plantuml-path", "plantuml", "Specify the base path for the plantuml server")
107112
command.Flags().StringVar(&plantuml.Server, "plantuml-server", "www.plantuml.com", "Specify the host for the plantuml server")
108113
command.Flags().BoolVar(&plantuml.DisableTLS, "plantuml-disable-tls", false, "Disable encryption on requests to the plantuml server")
109114
command.Flags().BoolVar(&enableTabs, "tabs", false, "Enable multi-tab preview mode (default: single-page)")
110115

111116
// Mark deprecated flags
112-
command.Flags().MarkDeprecated("dark-mode", "use --theme dark instead")
117+
_ = command.Flags().MarkDeprecated("dark-mode", "use --theme dark instead")
118+
119+
// Add subcommands
120+
command.AddCommand(demoCmd)
113121
}

cmd/demo.go

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/signal"
7+
"syscall"
8+
"time"
9+
10+
"github.com/mhersson/mpls/internal/previewserver"
11+
"github.com/mhersson/mpls/pkg/parser"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
var (
16+
demoWait time.Duration
17+
demoNoAuto bool
18+
)
19+
20+
const demoMarkdown = `# Theme and Feature Showcase
21+
22+
Demonstrating some of the **mpls** markdown rendering features.
23+
24+
> [!IMPORTANT]
25+
> GitHub-style alerts supported - emojis work too! :smile: :rocket:
26+
27+
<img src="./assets/yoshi.png" alt="yoshi" style="float:right; width:250px; margin-right: 100px; margin-top:80px;">
28+
29+
## Tables & Images
30+
31+
> [!TIP]
32+
> Images support both ` + "`![]()`" + ` and ` + "`<img style=\"...\">`" + ` syntax.
33+
34+
| Feature | Status | Notes |
35+
|---------|:------:|----------:|
36+
| Tables | ✓ | GFM style |
37+
| Mermaid | ✓ | Diagrams |
38+
| KaTeX | ✓ | Math |
39+
40+
## Code
41+
42+
` + "```go\nfunc main() {\n fmt.Println(\"Hello, mpls!\")\n}\n```" + `
43+
44+
<div style="display:flex; gap:40px;">
45+
<div>
46+
47+
## Lists, Tasks & Math :notebook:
48+
49+
- Unordered item
50+
- Another with **bold**
51+
52+
1. First ordered
53+
2. Second ordered
54+
55+
- [x] Completed task
56+
- [ ] Pending task
57+
58+
</div>
59+
<div style="flex:1; display:flex; justify-content:center; align-items:center;">
60+
61+
$$\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}$$
62+
63+
</div>
64+
</div>
65+
66+
## Mermaid Diagrams
67+
68+
` + "```mermaid\n" +
69+
"flowchart LR\n" +
70+
" subgraph Editor\n" +
71+
" E[📝 Edit Markdown]\n" +
72+
" end\n" +
73+
" subgraph mpls\n" +
74+
" L[LSP Server]\n" +
75+
" P[Parser]\n" +
76+
" W[WebSocket]\n" +
77+
" L --> P --> W\n" +
78+
" end\n" +
79+
" subgraph Browser\n" +
80+
" V[🌐 Live Preview]\n" +
81+
" end\n" +
82+
" E -->|didChange| L\n" +
83+
" W -->|update| V\n" +
84+
" style E fill:#6c9,stroke:#485,color:#333\n" +
85+
" style L fill:#69c,stroke:#458,color:#333\n" +
86+
" style P fill:#c6c,stroke:#848,color:#333\n" +
87+
" style W fill:#69c,stroke:#458,color:#333\n" +
88+
" style V fill:#f96,stroke:#b64,color:#333\n" +
89+
"```" + `
90+
`
91+
92+
var demoCmd = &cobra.Command{
93+
Use: "demo",
94+
Short: "Start a demo preview server with sample content",
95+
Long: `Start a standalone preview server with sample markdown content.
96+
Useful for taking screenshots of different themes.
97+
98+
Example:
99+
mpls demo --theme catppuccin-mocha --wait 10s
100+
mpls demo --theme dark --port 9999 --no-auto`,
101+
Run: func(_ *cobra.Command, _ []string) {
102+
// Get current working directory for workspace root
103+
cwd, err := os.Getwd()
104+
if err != nil {
105+
fmt.Fprintf(os.Stderr, "Failed to get working directory: %v\n", err)
106+
os.Exit(1)
107+
}
108+
109+
// Set code highlighting style based on theme
110+
if chromaStyle := previewserver.GetChromaStyleForTheme(previewserver.Theme); chromaStyle != "" {
111+
parser.CodeHighlightingStyle = chromaStyle
112+
}
113+
114+
// Enable emoji support for demo
115+
parser.EnableEmoji = true
116+
117+
// Set workspace root for relative path resolution
118+
parser.WorkspaceRoot = cwd
119+
120+
// Render demo markdown
121+
demoURI := "file://" + cwd + "/demo.md"
122+
html, meta := parser.HTML(demoMarkdown, demoURI, 0)
123+
124+
// Create and configure preview server
125+
server := previewserver.New()
126+
server.SetWorkspaceRoot(cwd)
127+
128+
// Pre-populate content so it's available when browser connects
129+
server.Update("demo.md", html, meta)
130+
131+
// Start server in background
132+
go server.Start()
133+
134+
url := fmt.Sprintf("http://localhost:%d", server.Port)
135+
fmt.Printf("Demo server running at %s (theme: %s)\n", url, previewserver.Theme)
136+
137+
// Open browser unless --no-auto
138+
if !demoNoAuto {
139+
if err := previewserver.Openbrowser(url, previewserver.Browser); err != nil {
140+
fmt.Printf("Failed to open browser: %v\n", err)
141+
}
142+
}
143+
144+
// Wait for specified duration
145+
if demoWait > 0 {
146+
fmt.Printf("Waiting %s before exit...\n", demoWait)
147+
time.Sleep(demoWait)
148+
server.Stop()
149+
} else {
150+
fmt.Println("Press Ctrl+C to stop.")
151+
152+
sigChan := make(chan os.Signal, 1)
153+
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
154+
<-sigChan
155+
156+
fmt.Println("\nShutting down...")
157+
server.Stop()
158+
}
159+
},
160+
}
161+
162+
func init() {
163+
demoCmd.Flags().DurationVar(&demoWait, "wait", 0, "Duration to keep server running (0 = run until interrupted)")
164+
demoCmd.Flags().BoolVar(&demoNoAuto, "no-auto", false, "Don't open browser automatically")
165+
166+
// Theme, port, and browser flags are defined on root command
167+
// They work via the global previewserver variables
168+
}

go.mod

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@ require (
77
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
88
github.com/mhersson/glsp v0.2.3
99
github.com/spf13/cobra v1.10.1
10-
github.com/tenkoh/goldmark-img64 v0.1.2
10+
github.com/stretchr/testify v1.11.1
1111
github.com/tliron/commonlog v0.2.21
1212
github.com/yuin/goldmark v1.7.13
1313
github.com/yuin/goldmark-emoji v1.0.6
1414
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
1515
github.com/yuin/goldmark-meta v1.1.0
1616
go.abhg.dev/goldmark/wikilink v0.6.0
17+
golang.org/x/net v0.46.0
1718
)
1819

1920
require (
2021
github.com/alecthomas/chroma/v2 v2.20.0 // indirect
2122
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
2223
github.com/bluele/gcache v0.0.2 // indirect
24+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
2325
github.com/dlclark/regexp2 v1.11.5 // indirect
24-
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
2526
github.com/iancoleman/strcase v0.3.0 // indirect
2627
github.com/inconshreveable/mousetrap v1.1.0 // indirect
2728
github.com/lithdew/quickjs v0.0.0-20200714182134-aaa42285c9d2 // indirect
@@ -30,15 +31,16 @@ require (
3031
github.com/muesli/termenv v0.16.0 // indirect
3132
github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 // indirect
3233
github.com/pkg/errors v0.9.1 // indirect
34+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
3335
github.com/rivo/uniseg v0.4.7 // indirect
3436
github.com/sasha-s/go-deadlock v0.3.6 // indirect
3537
github.com/segmentio/ksuid v1.0.4 // indirect
3638
github.com/sourcegraph/jsonrpc2 v0.2.1 // indirect
3739
github.com/spf13/pflag v1.0.10 // indirect
3840
github.com/tliron/go-kutil v0.4.0 // indirect
3941
golang.org/x/crypto v0.43.0 // indirect
40-
golang.org/x/net v0.46.0 // indirect
4142
golang.org/x/sys v0.37.0 // indirect
4243
golang.org/x/term v0.36.0 // indirect
4344
gopkg.in/yaml.v2 v2.4.0 // indirect
45+
gopkg.in/yaml.v3 v3.0.1 // indirect
4446
)

go.sum

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k
2121
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
2222
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
2323
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
24-
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
25-
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
2624
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
2725
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
2826
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -69,10 +67,8 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A
6967
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7068
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
7169
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
72-
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
73-
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
74-
github.com/tenkoh/goldmark-img64 v0.1.2 h1:z2LX9PycENbXdAUQKwk0fyKGxuQ04wWnKHLnYOtZSVU=
75-
github.com/tenkoh/goldmark-img64 v0.1.2/go.mod h1:m3Z5ytQWSf1Lcdv2cXWXSRB7epGz5ka7kUeXTDSgDwk=
70+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
71+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
7672
github.com/tliron/commonlog v0.2.21 h1:V1v+6opmzuOqDxxnxxM5RWtlHZmqZlDxkKeZGs6DpPg=
7773
github.com/tliron/commonlog v0.2.21/go.mod h1:W6XVoS/zo7mHXv2Kz8HKnBq+U34dFysJ2KUh2Aboibw=
7874
github.com/tliron/go-kutil v0.4.0 h1:5JwcBacgnqS3XyhwCWZKvq8ftlbVttNXnt+kfCH+Y2E=

internal/mpls/mpls.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,7 @@ func EditorDidChangeFocus(ctx *glsp.Context, params *protocol.EditorDidChangeFoc
4242
return nil
4343
}
4444

45-
// Lock document state for mutation
46-
docState.Lock()
47-
defer docState.Unlock()
48-
49-
html, meta := parser.HTML(docState.Content, uri)
45+
html, meta := parser.HTML(docState.Content, uri, 0)
5046

5147
html, docState.PlantUMLs, err = plantuml.InsertPlantumlDiagram(html, true, docState.PlantUMLs)
5248
if err != nil {

0 commit comments

Comments
 (0)