Skip to content

id(web): detect_mode's .pm.json check is case-sensitive — document.PM.JSON opens Raw instead of Rich (and downloads as doc.PM.JSON.pm.json) #770

Closed

Description

@dezren39

Summary

detect_mode treats every extension case-insensitively except the compound
.pm.json extension: a file named document.PM.JSON (or Document.Pm.Json)
opens in Raw mode instead of the Rich (ProseMirror) editor, and creating it
seeds empty bytes instead of the empty PM document {}.

Evidence

pkgs/id/src/web/content_mode.rs:143-152 — the extension is lowercased before
matching, but the Rich guard compares the raw filename:

let extension = path.extension().and_then(|e| e.to_str()).map(str::to_lowercase);
match extension.as_deref() {
    Some("json") if filename.ends_with(".pm.json") => ContentMode::Rich,

ends_with is case-sensitive, so the guard fails for any non-lowercase
spelling even though the json arm matched. Every other arm is effectively
case-insensitive, and the module's own tests pin that contract for simple
extensions (test_detect_markdown asserts CHANGELOG.markdown → Markdown).

Verified this run with a standalone rustc reproduction of the exact guard
(Path::extension + to_lowercase + ends_with, std semantics):

document.pm.json: rich=true
document.PM.JSON: rich=false
document.Pm.Json: rich=false
notes.MD ext lowercased: Some("md")   // simple extensions ARE case-insensitive

Consequences on current main (15f4885):

  • pkgs/id/src/web/routes.rs:1227 (new_file_handler): creating
    notes.PM.JSON takes the Raw content branch (b"") instead of Rich
    (b"{}").
  • Opening the file routes through detect_mode_with_content
    (routes.rs:452, collab.rs:121) → Raw, so a ProseMirror document is shown
    as raw text and edited without the rich editor.

Second site with the same root cause: pkgs/id/src/web/routes.rs:1676
(download-as-JSON name) — req.name.ends_with(".pm.json") is case-sensitive,
so downloading doc.PM.JSON as JSON names the download
doc.PM.JSON.pm.json.

Precedent in the same codebase: src/world_ssh.rs:985 lowercases before
ends_with(".md") checks.

Why it matters

Filenames with uppercase extensions are common from Windows/macOS exports
and bulk renames. The mode detector's contract is "extensions are
case-insensitive" everywhere else; the one compound extension silently
violates it, and the file then opens in the wrong editor with the wrong
seed content.

Acceptance criteria

  • detect_mode returns ContentMode::Rich for .pm.json in any case
    (document.PM.JSON, Document.Pm.Json, …), with regression tests next
    to test_detect_prosemirror_json in content_mode.rs.
  • Simple-extension behavior unchanged; the existing content_mode tests pass.
  • The download-as-JSON path (routes.rs:1676) treats an existing .pm.json
    suffix case-insensitively too, or the issue is updated noting why that
    site was deliberately left.

Activity

  1. dezren39 commented on Oct 24, 2026

    @dezren39
    MemberAuthor

    id train triage (2026-10-24): needs-decision — Muse: assign to Session A (phases 0-10 review); detect_mode .pm.json check is case-sensitive (content_mode.rs:143-152).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions