Skip to content

Commit c2a7e2e

Browse files
committed
Integrate java-runner for interactive Quarto HTML snippets.
Replace discontinued Trinket embeds with client-side java-runner (feature parity for 17 former trinkets, plus extra editors/REPLs), and document the Quarto/conda build environment for local testing.
1 parent 57c08a5 commit c2a7e2e

16 files changed

Lines changed: 875 additions & 113 deletions

File tree

‎Makefile‎

Lines changed: 198 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,220 @@
1-
F=thinkjava2
2-
3-
all:
4-
pdflatex $(F).tex
5-
pdflatex $(F).tex
6-
pdflatex $(F).tex
7-
8-
clean:
9-
rm -f comment.cut $(F).aux $(F).idx $(F).ilg $(F).ind $(F).log $(F).out $(F).toc
10-
11-
plastex:
12-
# Before running plastex, we need the current directory in PYTHONPATH
13-
# export PYTHONPATH=$PYTHONPATH:.
14-
latexpand --keep-comments $(F).tex > $(F).expand
15-
python2 preprocess.py $(F).expand > $(F).plastex
16-
plastex --renderer=DocBook --theme=book --image-resolution=300 --filename=$(F).xml $(F).plastex
17-
cd $(F); python2 ../postprocess.py $(F).xml > temp; mv temp $(F).xml
18-
cd $(F); python ../xmlsplit.py $(F).xml
19-
20-
xxe:
21-
xmlcopyeditor ~/ThinkJava2/$(F)/$(F).xml &
22-
23-
lint:
24-
xmllint -noout $(F)/$(F).xml
25-
26-
#oreilly:
27-
# rsync -a $(F)/*.xml atlas/
28-
# rsync -a figs/*.pdf atlas/figs/
29-
# rsync -a figs/*.png atlas/figs/
30-
# rsync -a figs/*.jpg atlas/figs/
31-
# cd atlas; git add *.xml figs/*
32-
# cd atlas; git commit -m "Automated check in."
33-
# cd atlas; git push
34-
35-
# if a bug (in ocaml?) causes "make hevea" to fail; use "make -i hevea" instead
1+
# Think Java 2e - Build System
2+
# =============================
3+
4+
# Configuration
5+
PROJECT_NAME = ThinkJava2
6+
BOOK_NAME = thinkjava2
7+
MAIN_FILE = $(BOOK_NAME).tex
8+
BUILD_DIR = build
9+
DIST_DIR = dist
10+
11+
# Tools and commands
12+
PDFLATEX = pdflatex
13+
PYTHON = python3
14+
PYTHON2 = python2
15+
HEVEA = hevea
16+
IMAGEN = imagen
17+
HACHA = hacha
18+
RSYNC = rsync
19+
20+
# Default target
21+
.DEFAULT_GOAL := help
22+
23+
# Help target
24+
.PHONY: help
25+
help: ## Show this help message
26+
@echo "Think Java 2e - Available build targets:"
27+
@echo ""
28+
@echo "📚 Book Generation:"
29+
@echo " pdf - Generate PDF version (default)"
30+
@echo " html - Generate modern HTML with Quarto"
31+
@echo " hevea - Generate legacy HTML with HeVeA"
32+
@echo " trinket - Generate interactive HTML version"
33+
@echo ""
34+
@echo "🧹 Maintenance:"
35+
@echo " clean - Remove build artifacts"
36+
@echo " distclean - Remove all generated files"
37+
@echo ""
38+
@echo "📦 Distribution:"
39+
@echo " distrib - Package for distribution"
40+
@echo ""
41+
@echo "🔧 Development:"
42+
@echo " plastex - Generate DocBook XML"
43+
@echo " lint - Validate generated XML"
44+
@echo ""
45+
@echo "🐍 Environment (conda/mamba):"
46+
@echo " create_environment - Create conda env from environment.yml"
47+
@echo " update_environment - Update env (mamba --prune)"
48+
@echo " delete_environment - Remove conda env"
49+
@echo ""
50+
@echo "Usage: make [target]"
51+
52+
# =============================================================================
53+
# Conda / mamba environment
54+
# =============================================================================
55+
56+
.PHONY: create_environment
57+
create_environment: ## Create conda environment from environment.yml
58+
mamba env create -f environment.yml
59+
@echo ""
60+
@echo ">>> Environment created successfully!"
61+
@echo ">>> Activate with: conda activate $(PROJECT_NAME)"
62+
@echo ">>> Also install Quarto separately: https://quarto.org/docs/get-started/"
63+
64+
.PHONY: update_environment
65+
update_environment: ## Update environment from environment.yml (with --prune)
66+
mamba env update -f environment.yml --prune
67+
@echo ">>> Environment updated successfully!"
68+
69+
.PHONY: delete_environment
70+
delete_environment: ## Remove conda environment
71+
mamba env remove --name $(PROJECT_NAME)
72+
@echo ">>> Environment $(PROJECT_NAME) removed"
73+
74+
# =============================================================================
75+
# Book Generation Targets
76+
# =============================================================================
77+
78+
.PHONY: pdf
79+
pdf: ## Generate PDF version (requires 3 passes for references)
80+
@echo "📖 Generating PDF version..."
81+
$(PDFLATEX) $(MAIN_FILE)
82+
$(PDFLATEX) $(MAIN_FILE) # Second pass for references
83+
$(PDFLATEX) $(MAIN_FILE) # Third pass for final layout
84+
@echo "✅ PDF generated: $(BOOK_NAME).pdf"
85+
86+
.PHONY: html
87+
html: ## Generate modern HTML with Quarto
88+
@echo "🌐 Generating modern HTML with Quarto..."
89+
cd quarto && quarto render
90+
@echo "✅ HTML generated in quarto/_book/"
3691

3792
.PHONY: hevea
38-
hevea:
39-
cp $(F).tex $(F)_.tex
40-
rm -rf heveahtml
41-
mkdir heveahtml
42-
hevea -O -exec xxdate.exe -e latexonly.tex hevea/htmlonly.tex $(F)_
43-
hevea -O -exec xxdate.exe -e latexonly.tex hevea/htmlonly.tex $(F)_
44-
imagen -png -pdf $(F)_
45-
imagen -png -pdf $(F)_
46-
hacha $(F)_.html
47-
cp hevea/*.png heveahtml
48-
cat custom.css >> $(F)_.css
49-
mv index.html $(F)_?*.html $(F)_*.png $(F)_.css heveahtml
50-
rm *motif.gif $(F)_.*
93+
hevea: ## Generate legacy HTML with HeVeA
94+
@echo "🔄 Generating legacy HTML with HeVeA..."
95+
@mkdir -p heveahtml
96+
cp $(MAIN_FILE) $(BOOK_NAME)_.tex
97+
$(HEVEA) -O -exec xxdate.exe -e latexonly.tex hevea/htmlonly.tex $(BOOK_NAME)_
98+
$(HEVEA) -O -exec xxdate.exe -e latexonly.tex hevea/htmlonly.tex $(BOOK_NAME)_
99+
$(IMAGEN) -png -pdf $(BOOK_NAME)_
100+
$(HACHA) $(BOOK_NAME)_.html
101+
cp hevea/*.png heveahtml/
102+
cat custom.css >> $(BOOK_NAME)_.css
103+
mv index.html $(BOOK_NAME)_?*.html $(BOOK_NAME)_*.png $(BOOK_NAME)_.css heveahtml/
104+
rm -f *motif.gif $(BOOK_NAME)_.*
105+
@echo "🧹 Cleaning up HTML files..."
51106
sed -i 's/\\%/%/g' heveahtml/*.html
52107
sed -i 's/\\{/{/g' heveahtml/*.html
53108
sed -i 's/\\}/}/g' heveahtml/*.html
54109
sed -i 's/\\\\n/\\n/g' heveahtml/*.html
55110
sed -i 's/\\\\t/\\t/g' heveahtml/*.html
56-
python3 hevea/rename.py heveahtml
111+
$(PYTHON) hevea/rename.py heveahtml
112+
@echo "✅ Legacy HTML generated in heveahtml/"
57113

58114
.PHONY: trinket
59-
trinket:
60-
cp $(F).tex $(F)_.tex
61-
rm -rf trinkethtml
62-
mkdir trinkethtml
63-
hevea -O -exec xxdate.exe -e latexonly.tex trinket/htmlonly.tex $(F)_
64-
hevea -O -exec xxdate.exe -e latexonly.tex trinket/htmlonly.tex $(F)_
65-
imagen -png -pdf $(F)_
66-
imagen -png -pdf $(F)_
67-
hacha $(F)_.html
68-
cp trinket/*.css trinket/*.js trinkethtml
69-
mv index.html $(F)_.css $(F)_?*.html $(F)_*.png trinkethtml
70-
rm *motif.gif $(F)_.*
115+
trinket: ## Generate interactive HTML version
116+
@echo "🎮 Generating interactive HTML with Trinket..."
117+
@mkdir -p trinkethtml
118+
cp $(MAIN_FILE) $(BOOK_NAME)_.tex
119+
$(HEVEA) -O -exec xxdate.exe -e latexonly.tex trinket/htmlonly.tex $(BOOK_NAME)_
120+
$(HEVEA) -O -exec xxdate.exe -e latexonly.tex trinket/htmlonly.tex $(BOOK_NAME)_
121+
$(IMAGEN) -png -pdf $(BOOK_NAME)_
122+
$(IMAGEN) -png -pdf $(BOOK_NAME)_
123+
$(HACHA) $(BOOK_NAME)_.html
124+
cp trinket/*.css trinket/*.js trinkethtml/
125+
mv index.html $(BOOK_NAME)_.css $(BOOK_NAME)_?*.html $(BOOK_NAME)_*.png trinkethtml/
126+
rm -f *motif.gif $(BOOK_NAME)_.*
127+
@echo "🧹 Cleaning up HTML files..."
71128
sed -i 's/\\%/%/g' trinkethtml/*.html
72129
sed -i 's/\\{/{/g' trinkethtml/*.html
73130
sed -i 's/\\}/}/g' trinkethtml/*.html
74131
sed -i 's/\\\\n/\\n/g' trinkethtml/*.html
75132
sed -i 's/\\\\t/\\t/g' trinkethtml/*.html
76-
77-
# perl postprocessing (woot) seems easier than escaping through Latex and Hevea
133+
@echo "🔧 Running post-processing..."
78134
perl -i -pe 's/\[\[\[\[\s?(\S*?)\s?\]\]\]\]/----{\1}----/g' trinkethtml/*.html
79135
perl -i -pe 's/\<a .*? ALT\=\"(Previous|Up|Next)\"\>\<\/a\>//g' trinkethtml/*.html
80136
perl -0777 -i -pe 's/\<hr\>//' trinkethtml/*.html
137+
@echo "📝 Generating Nunjucks templates..."
138+
mkdir -p trinkethtml/nunjucks
139+
$(PYTHON) trinket/maketemplates.py
140+
@echo "🖼️ Organizing images..."
141+
mkdir -p trinkethtml/img
142+
cp trinkethtml/*.png trinkethtml/img/
143+
@echo "✅ Interactive HTML generated in trinkethtml/"
144+
145+
# =============================================================================
146+
# Development Tools
147+
# =============================================================================
148+
149+
.PHONY: plastex
150+
plastex: ## Generate DocBook XML with plasTeX
151+
@echo "📄 Generating DocBook XML..."
152+
latexpand --keep-comments $(MAIN_FILE).tex > $(BOOK_NAME).expand
153+
$(PYTHON2) preprocess.py $(BOOK_NAME).expand > $(BOOK_NAME).plastex
154+
plastex --renderer=DocBook --theme=book --image-resolution=300 --filename=$(BOOK_NAME).xml $(BOOK_NAME).plastex
155+
cd $(BOOK_NAME) && $(PYTHON2) ../postprocess.py $(BOOK_NAME).xml > temp && mv temp $(BOOK_NAME).xml
156+
cd $(BOOK_NAME) && $(PYTHON) ../xmlsplit.py $(BOOK_NAME).xml
157+
@echo "✅ DocBook XML generated in $(BOOK_NAME)/"
158+
159+
.PHONY: lint
160+
lint: ## Validate generated XML
161+
@echo "🔍 Validating XML..."
162+
xmllint -noout $(BOOK_NAME)/$(BOOK_NAME).xml
163+
@echo "✅ XML validation passed"
164+
165+
# =============================================================================
166+
# Maintenance Targets
167+
# =============================================================================
81168

82-
# produce nunjucks templates for our app
83-
mkdir trinkethtml/nunjucks
84-
python trinket/maketemplates.py
169+
.PHONY: clean
170+
clean: ## Remove build artifacts
171+
@echo "🧹 Cleaning build artifacts..."
172+
rm -f comment.cut $(BOOK_NAME).aux $(BOOK_NAME).idx $(BOOK_NAME).ilg $(BOOK_NAME).ind
173+
rm -f $(BOOK_NAME).log $(BOOK_NAME).out $(BOOK_NAME).toc $(BOOK_NAME).dvi
174+
rm -f $(BOOK_NAME).4ct $(BOOK_NAME).4tc $(BOOK_NAME).idv $(BOOK_NAME).lg
175+
rm -f $(BOOK_NAME).xref $(BOOK_NAME).tmp $(BOOK_NAME).css $(BOOK_NAME).html
176+
rm -f $(BOOK_NAME)_*.tex $(BOOK_NAME)_*.html $(BOOK_NAME)_*.css $(BOOK_NAME)_*.png
177+
@echo "✅ Cleanup complete"
85178

86-
# gather images for ease of uploading to CDN
87-
mkdir trinkethtml/img
88-
cp trinkethtml/*.png trinkethtml/img
179+
.PHONY: distclean
180+
distclean: clean ## Remove all generated files
181+
@echo "🧹 Deep cleaning..."
182+
rm -rf $(BUILD_DIR) $(DIST_DIR) heveahtml trinkethtml
183+
rm -rf $(BOOK_NAME)/
184+
rm -f $(BOOK_NAME).pdf $(BOOK_NAME).expand $(BOOK_NAME).plastex
185+
@echo "✅ Deep cleanup complete"
89186

90-
DEST = /home/downey/public_html/greent/thinkjava7
187+
# =============================================================================
188+
# Distribution
189+
# =============================================================================
91190

92-
distrib:
93-
rm -rf dist
94-
mkdir dist
95-
rsync -a $(F).pdf dist
96-
rsync -a heveahtml/ dist/html/
97-
rsync -a dist/* $(DEST)
191+
DEST = /home/downey/public_html/greenteapress/thinkjava7
192+
193+
.PHONY: distrib
194+
distrib: ## Package for distribution
195+
@echo "📦 Packaging for distribution..."
196+
rm -rf $(DIST_DIR)
197+
mkdir -p $(DIST_DIR)
198+
$(RSYNC) -a $(BOOK_NAME).pdf $(DIST_DIR)/
199+
$(RSYNC) -a heveahtml/ $(DIST_DIR)/html/
200+
$(RSYNC) -a $(DIST_DIR)/* $(DEST)/
98201
chmod -R o+r $(DEST)/*
99-
cd $(DEST)/..; sh back
202+
cd $(DEST)/.. && sh back
203+
@echo "✅ Distribution complete"
204+
205+
# =============================================================================
206+
# Legacy targets (kept for compatibility)
207+
# =============================================================================
208+
209+
.PHONY: all
210+
all: pdf ## Alias for pdf target
211+
212+
.PHONY: xxe
213+
xxe: ## Open XML in XML Copy Editor (legacy)
214+
xmlcopyeditor ~/ThinkJava2/$(BOOK_NAME)/$(BOOK_NAME).xml &
215+
216+
# =============================================================================
217+
# Special notes
218+
# =============================================================================
219+
# Note: If HeVeA fails due to OCaml bugs, use: make -i hevea
220+
# Note: Most users should use 'make html' for the modern Quarto version

‎PROJECT_BOARD.md‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# ThinkJava2 Project Board
2+
3+
Numbered tasks for tracking work. Each task has a permanent number; add new tasks at the end. Update status as work progresses.
4+
5+
### Current focus (2026-08-20)
6+
7+
- **Task 1–3:** Done on branch `java-runner` (see [issue #31](https://github.com/ChrisMayfield/ThinkJava2/issues/31)).
8+
- Next: commit / PR when ready.
9+
10+
---
11+
12+
## Task 1: Dev environment for Quarto HTML
13+
14+
**Status:** Done (2026-08-20)
15+
16+
**Context:** Interactive book HTML is built with Quarto (`make html` → `quarto/_book/`). [Issue #31](https://github.com/ChrisMayfield/ThinkJava2/issues/31) replaces Trinket with client-side [java-runner](https://github.com/ChrisMayfield/java-runner). That work needs a reproducible local HTML build. This is **not** a JDK project: java-runner runs in the browser; conda only covers Python tooling.
17+
18+
**Goal:** Document and modernize the toolchain so `make html` / `quarto preview` work reliably.
19+
20+
### Done
21+
22+
- [x] Modernize `environment.yml` (Python 3.12, conda-forge; no Pillow — unused)
23+
- [x] Makefile targets: `create_environment`, `update_environment`, `delete_environment`
24+
- [x] README: Quarto install (external) + conda activate + `make html`; note no JDK needed
25+
- [x] Smoke-check: `make html` → `quarto/_book/index.html`; `mamba env create` succeeded
26+
27+
### Out of scope
28+
29+
- JDK / OpenJDK in conda
30+
- Building java-runner from npm (vendored `dist/` in Task 2)
31+
- HeVeA / legacy Trinket path changes
32+
33+
---
34+
35+
## Task 2: Integrate java-runner (feature parity)
36+
37+
**Status:** Done (2026-08-20)
38+
39+
**Context:** 17 former `\begin{trinket}` programs in chapters 1–5, 7, 10. Quarto had flattened them to ordinary `{.java language="Java"}` fences.
40+
41+
**Goal:** Those 17 snippets become interactive editors in Quarto HTML via `<script type="text/x-java">`.
42+
43+
### Done
44+
45+
- [x] Vendor `javarunner.css` + `javarunner.js` into `quarto/assets/javarunner/`
46+
- [x] Wire `_quarto.yml`: CSS, `include-in-header` JS, `resources:`, Lua filter
47+
- [x] `quarto/filters/javarunner.lua` — `.javarunner` → `text/x-java` (HTML only)
48+
- [x] Annotate all 17 former-trinket fences
49+
- [x] Rebuild: 17 `<script type="text/x-java">` across ch01–05, 07, 10; assets in `_book/assets/javarunner/`; not wrapped in `code-fold` details
50+
51+
### Former trinket map
52+
53+
| Chapter | Programs |
54+
|---------|----------|
55+
| ch01 | Hello, Hello2, Goodbye×3, Hello3 |
56+
| ch02 | Hello×2 |
57+
| ch03 | Echo, Convert, GuessStarter |
58+
| ch04 | NewLine, PrintTwice, PrintTime |
59+
| ch05 | Logarithm |
60+
| ch07 | Doubloon |
61+
| ch10 | Surprise |
62+
63+
### Out of scope
64+
65+
- Removing legacy `make trinket`
66+
67+
---
68+
69+
## Task 3: Expand interactive coverage (extra mile)
70+
71+
**Status:** Done (initial pass, 2026-08-20)
72+
73+
**Context:** java-runner can run incomplete snippets and supports REPL via `text/x-java-repl`.
74+
75+
**Goal:** After Task 2 parity, selectively add more interactives and optional REPL blocks.
76+
77+
### Added `.javarunner` (incomplete / statement snippets)
78+
79+
- ch01: bare `println("Hello, World!")`; escaped-quote println
80+
- ch02: print current time; minutes since midnight; floating-point `0.1` sum; string `+` associativity
81+
- ch06: countdown `while`; for-loop “appreciate”; multiplication table
82+
83+
### Added `.javarunner-repl`
84+
85+
- ch02: `message` / `hour` / `minute` declarations (REPL init)
86+
- ch06: `fruit` / `charAt(0)` (REPL init)
87+
88+
### Kept static (intentionally)
89+
90+
- Compiler-error demos, infinite-loop examples, fragments that depend on undeclared prior state, wrong/`==` string comparisons meant as cautionary text
91+
92+
### Deliverables
93+
94+
1. Candidate judgment recorded above
95+
2. Annotated additional fences + 2 REPL examples
96+
3. Re-test HTML build after Task 3 edits

0 commit comments

Comments
 (0)