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
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
PORT=8890
TZ=America/Chicago

# Dev HTTP bind host. Defaults to loopback (localhost) so the dev server isn't
# exposed to the LAN. On WSL2 the Windows browser can't reliably reach loopback
# inside the VM, so bind 0.0.0.0. For Docker/script launches (which read .env),
# set ORCPUB_HTTP_HOST here; for `lein fig:*` use the +wsl profile instead
# (fig:* does not read .env): lein with-profile +wsl fig:dev
# ORCPUB_HTTP_HOST=0.0.0.0

# Docker image names. Set these if you tag your own builds
# (e.g., docker build -t dmv:2.6.0.0 .) so compose/swarm finds them.
# Defaults: orcpub-app, orcpub-datomic
Expand Down
31 changes: 29 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
# Changelog

All notable changes to this project will be documented in this file.
Format: per-commit entries grouped by category, newest first.
## [staging/june-bug-patches-01] — June bug-patch bundle (2026-07-05)

### Fixed
- **Homebrew class source no longer poisons spell-selection keys** — the source label was folded into the class `:name`, so the name-derived selection keys broke whenever it changed and saved spells/cantrips vanished; keys now come from a stable class identity, and orphaned saves repair on load (`9a709c0d`, `fe549631`, `a3e26155`).
- **Hunter's Evasion no longer blanks the Features tab** — the Superior Hunter's Defense → Evasion trait shipped with no name, and a nil name crashed the feature-name sort; it's now named "Evasion" (`dd65d66a`).
- **Character sheets no longer go blank** — an unrenderable section shows a recovery message; the rest of the sheet stays usable (`565c33c0`).
- **The Features tab loads for every character** — fixed a rendering bug that blanked it for all (`2a6fde93`).
- **A nameless trait no longer crashes the Features tab** — shown as "[Unnamed feature]" instead of throwing on the name sort (`5c3b073f`).
- **Boolean toggles no longer corrupt data** — they can't wipe an underlying map, and self-heal damage from the old bug (`1e9f27ec`).
- **Keyword-trap imports no longer silently vanish** — caught on import and routed to repair instead of a class that never appears (`d9b23021`).
- **Unreadable storage is preserved** for recovery instead of deleted (`eedffc08`).
- **localStorage quota failures warn and offer a backup** instead of silently dropping the save.
- **Readable import/export errors** — plain-English console messages instead of garbled output; import dedup is shown as a log line, not raw EDN (`eba28a9c`, `e512dc45`).
- **Post-save export fixed** — the "export here" link passed a stringified plugin instead of the map (`e3c9a9ee`).
- **Autosave no longer crashes on a not-ready template** — an empty template reached the builder and threw; now guarded (`e3c9a9ee`).
- **Import dedup no longer skips a top-level Selection** — de-duplication now covers a Selection at the top level, not just nested options (`d9b23021`).
- **Non-ASCII name detection works in the browser** — `count-non-ascii` was miscounting under ClojureScript (`d9b23021`).

### Added
- **Resilient homebrew loading** — a bad source is quarantined for repair (self-clearing once fixed) instead of dropping the whole library, with a My Content panel to rename, re-key, and restore it (`eedffc08`).
- **Builder escape hatches** — Export draft, refresh-safe WIP restore, "Save anyway" with placeholders, emergency raw export, and Export & Auto-Fix, so imperfect work is never trapped or lost (`eac350d0`, `e3c9a9ee`).
- **"Show homebrew source on class names" toggle** — without affecting saved spell selections (`8f94a94c`).
- **Fill-in dialog on export** — supply or auto-fill missing required fields instead of writing a broken file, with live field-level guidance in the builders (`1547cd69`, `22172adb`).

### Changed
- **Save validation covers every required field** — dropdowns and multi-selects (spell class-lists, monster hit dice, parent class/race), not just text (`e512dc45`).
- **Save and load share one spec registry** — so they can't drift and wrongly quarantine already-saved content (`ca977e0a`).
- **Normal exports strip meaningless blank flags** (false/nil/empty); raw, draft, and emergency exports are untouched.
- **Invalid-key errors are element-specific** instead of a generic "Name" error.

## [breaking/2026-stack-modernization]

Expand Down
6 changes: 3 additions & 3 deletions docs/CONFLICT_RESOLUTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Scans imported content for duplicate keys before import.

**External** - Between existing and imported content:
```
Already loaded: :artificer from "Player's Handbook"
Already loaded: :artificer from "Core Rulebook"
Importing: :artificer from "Homebrew Classes"
→ CONFLICT: Same key, different sources
```
Expand Down Expand Up @@ -116,15 +116,15 @@ Checks against all existing content (loaded + importing) to guarantee uniqueness

### Single Conflict

Importing `:artificer` when PHB `:artificer` already exists:
Importing `:artificer` when the core `:artificer` already exists:

```
┌─ Conflict Resolution ─────────────────────────┐
│ Found 1 duplicate key: │
│ │
│ Classes: │
│ :artificer │
│ Existing: "Player's Handbook"
│ Existing: "Core Rulebook"
│ Importing: "Homebrew Classes" │
│ │
│ ○ Rename to: :artificer-2 │
Expand Down
6 changes: 0 additions & 6 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ Guides for developers and power users working with OrcPub's homebrew content sys
- [🔍 Missing Content Detection](CONTENT_RECONCILIATION.md) - Find/fix missing content references
- [📋 Required Fields Guide](HOMEBREW_REQUIRED_FIELDS.md) - Required fields per content type

**Agent Knowledge Base:**
- [📚 KB Index](kb/README.md) - Verified findings from deep investigations
- [💥 Datomic Crash Analysis](kb/datomic-crash-analysis.md) - Root cause, frequency, fix options

**For Developers:**
- [ClojureScript Type Tolerance](clojurescript-type-tolerance.md) - What cljs silently tolerates vs. what crashes (and why); the string-op crash pattern behind render black screens
- [🚨 Error Handling](ERROR_HANDLING.md) - Error handling utilities
Expand Down Expand Up @@ -48,8 +44,6 @@ Guides for developers and power users working with OrcPub's homebrew content sys

**Decision:** Multiple fuzzy matching strategies (Levenshtein, prefix, name similarity) to catch typos and versioning.

**Gotcha:** Must exclude built-in content (PHB, Xanathar's) or system suggests switching from homebrew Artificer to PHB Artificer (which doesn't exist in 5e).

→ [CONTENT_RECONCILIATION.md](CONTENT_RECONCILIATION.md)

### Why a Fallback Chain for Language Selection?
Expand Down
18 changes: 0 additions & 18 deletions docs/kb/README.md

This file was deleted.

204 changes: 0 additions & 204 deletions docs/kb/datomic-crash-analysis.md

This file was deleted.

7 changes: 7 additions & 0 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,13 @@
;; Composite: includes cljsbuild-config (build definitions) + dev-config
;; (deps, devtools overlays). No inline maps — lein warns against those.
:dev [:cljsbuild-config :dev-config]
;; Opt-in for WSL2 (and any host reaching the dev server by IP): binds
;; ::http/host to 0.0.0.0 via environ so a Windows browser can reach the
;; WSL VM (localhost forwarding is flaky over WSL2). The default stays
;; loopback for everyone else. Activate per-command, e.g.
;; lein with-profile +wsl fig:dev
;; lein with-profile +wsl fig:build
:wsl {:env {:orcpub-http-host "0.0.0.0"}}
;; NOTE: :native-dev was for React Native builds (legacy, may be unused)
:native-dev {:dependencies [[cider/piggieback "0.5.3"]]
:source-paths ["src/cljs" "native/cljs" "src/cljc" "env/dev"]
Expand Down
22 changes: 22 additions & 0 deletions src/cljc/orcpub/common.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,28 @@
(and (keyword? kw)
(-> kw name starts-with-letter?)))

(defn toggle-flag
"Flip a boolean flag, but leave a collection untouched instead of collapsing it.
Use in place of bare `not` for builder toggles whose path could land on a MAP:
`(not {…})` is `false`, which DESTROYS the map so every child read returns nil
(the 'true/false/nil from clicking a lot' corruption)."
[v]
(if (coll? v) v (not v)))

(defn toggle-in
"Toggle a boolean flag at path `ks` in `m` (like `update-in` with `not`), with
two safeguards: the LEAF uses `toggle-flag` so it never collapses a map; a
non-associative INTERMEDIATE (a stray `false` from the old collapse bug, or an
absent slot) is healed to a fresh map instead of crashing on `(assoc false …)`,
so a click on a previously-corrupted spot self-heals."
[m ks]
(let [[k & more] ks]
(if (seq more)
(let [child (get m k)
child (if (associative? child) child {})] ; heal a collapsed node
(assoc m k (toggle-in child more)))
(assoc m k (toggle-flag (get m k))))))

(defn remove-at-index [v index]
(vec
(keep-indexed
Expand Down
Loading
Loading