Skip to content
Merged
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Pre-compiled binaries are automatically downloaded for Linux and macOS (x86_64 a
-- port = 0, -- Optional: server port (default: random)
-- debug = false, -- Optional: enable debug logging
-- sync_scroll = true, -- Optional: sync scroll with nvim (default: true)
-- theme = "light/dark", -- Optional: set theme (default: dark)
})
end,
keys = {
Expand Down
24 changes: 22 additions & 2 deletions lua/penview/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ M.server_job = nil
M.debug = false
M.sync_scroll = true
M.headless = false
M.theme = "dark"

function M.setup(opts)
opts = opts or {}
Expand All @@ -55,6 +56,13 @@ function M.setup(opts)
M.debug = opts.debug or false
M.sync_scroll = opts.sync_scroll ~= false -- default true

if opts.theme then
if opts.theme ~= "light" and opts.theme ~= "dark" then
error('[penview] \'theme\' must be "light" or "dark", got: ' .. tostring(opts.theme))
end
M.theme = opts.theme
end

if M.headless then
-- Headless mode requires a port
if not opts.port or opts.port == 0 then
Expand Down Expand Up @@ -111,10 +119,22 @@ function M.start()
local cmd
if M.headless then
-- Headless mode: bind to 0.0.0.0, no browser open
cmd = { binary, "serve", "-q", "-p", tostring(M.port), "-a", "0.0.0.0" }
cmd = { binary, "serve", "-q", "-p", tostring(M.port), "-a", "0.0.0.0", "--theme", M.theme }
else
-- Normal mode: --open tells server to launch browser with file path
cmd = { binary, "serve", "-q", "-p", tostring(M.port), "--open", path, "--browser", M.browser }
cmd = {
binary,
"serve",
"-q",
"-p",
tostring(M.port),
"--open",
path,
"--browser",
M.browser,
"--theme",
M.theme,
}
end

log("Command: " .. table.concat(cmd, " "))
Expand Down
9 changes: 7 additions & 2 deletions rust/penview/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ enum Args {
/// Only used when --open is also provided.
#[arg(short, long)]
browser: Option<String>,

/// Initial theme for the preview page ("light" or "dark").
#[arg(short = 't', long, default_value_t = ("light").to_string())]
theme: String,
},
Render {
/// The location of the Markdown file to render.
Expand All @@ -65,12 +69,13 @@ async fn main() {
address,
open,
browser,
theme,
} => {
if !quiet {
tracing_subscriber::fmt::init();
}

let state = state::AppState::new();
let state = state::AppState::new(theme);
let app = construct_router(state);

let listener = tokio::net::TcpListener::bind((address, port))
Expand Down Expand Up @@ -101,7 +106,7 @@ async fn main() {
mut in_file,
out_file,
} => {
let html = render_doc(&in_file, false)
let html = render_doc(&in_file, false, "light")
.await
.expect("Failed to render document.");

Expand Down
1 change: 1 addition & 0 deletions rust/penview/src/page_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ pub struct PageTemplate {
pub title: String,
pub body: String,
pub use_websocket: bool,
pub theme: String,
}
7 changes: 6 additions & 1 deletion rust/penview/src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ fn generate_message_data_url(message: impl AsRef<str>, color: impl AsRef<str>) -
///
/// `use_websocket` determines whether to include code for automatically updating the document with a
/// WebSocket connection.
pub async fn render_doc(path: impl AsRef<Path>, use_websocket: bool) -> anyhow::Result<String> {
pub async fn render_doc(
path: impl AsRef<Path>,
use_websocket: bool,
theme: &str,
) -> anyhow::Result<String> {
let path = path.as_ref().canonicalize()?;
let file = read_to_string(&path).await?;
let title = path.as_os_str().to_string_lossy().to_string();
Expand All @@ -54,6 +58,7 @@ pub async fn render_doc(path: impl AsRef<Path>, use_websocket: bool) -> anyhow::
body,
title,
use_websocket,
theme: theme.to_string(),
};

Ok(template.render().unwrap())
Expand Down
18 changes: 14 additions & 4 deletions rust/penview/src/routes/index.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
use std::path::PathBuf;

use axum::{extract::Query, response::Html};
use axum::{
extract::{Query, State},
response::Html,
};
use resolve_path::PathResolveExt;
use serde::Deserialize;
use tracing::info;

use crate::render::render_doc;
use crate::{render::render_doc, state::AppState};

#[derive(Debug, Deserialize)]
pub struct IndexParams {
path: PathBuf,
}

pub async fn index(Query(IndexParams { path }): Query<IndexParams>) -> Html<String> {
pub async fn index(
Query(IndexParams { path }): Query<IndexParams>,
State(state): State<AppState>,
) -> Html<String> {
info!("Rendering document {}", path.to_string_lossy());

Html(render_doc(path.resolve(), true).await.unwrap())
Html(
render_doc(path.resolve(), true, &state.theme)
.await
.unwrap(),
)
}
4 changes: 3 additions & 1 deletion rust/penview/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ use tokio::sync::{Mutex, broadcast};
#[derive(Clone)]
pub struct AppState {
pub channels: Arc<Mutex<HashMap<PathBuf, broadcast::Sender<String>>>>,
pub theme: String,
}

impl AppState {
pub fn new() -> Self {
pub fn new(theme: String) -> Self {
Self {
channels: Arc::new(Mutex::new(HashMap::new())),
theme,
}
}

Expand Down
10 changes: 5 additions & 5 deletions rust/penview/templates/page.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" {% if theme == "dark" %}data-theme="dark"{% endif %}>

<head>
<meta charset="utf-8">
<title>{{ title }}</title>
<link id="theme-light" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css/github-markdown-light.css">
<link id="theme-dark" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css/github-markdown-dark.css" disabled>
<link id="theme-light" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css/github-markdown-light.css" {% if theme == "dark" %}disabled{% endif %}>
<link id="theme-dark" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css/github-markdown-dark.css" {% if theme != "dark" %}disabled{% endif %}>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css"
integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.7.1.slim.min.js" crossorigin="anonymous"></script>
Expand Down Expand Up @@ -246,8 +246,8 @@
window.mermaidRender?.();
}

// Load saved preference or default to light
const saved = localStorage.getItem('penview-theme') || 'light';
// Load saved preference or use server-configured default
const saved = localStorage.getItem('penview-theme') || '{{ theme }}';
setTheme(saved);

toggle.addEventListener('click', function() {
Expand Down
Loading