Skip to content

Launch directory for runs and exports; Molden file check - #56

Merged
dnkats merged 9 commits into
mainfrom
launch-dir-exports
Sep 22, 2026
Merged

dnkats merged 9 commits into
mainfrom
launch-dir-exports

Conversation

@dnkats

@dnkats dnkats commented Sep 22, 2026

Copy link
Copy Markdown
Member

Summary

  • ElemCo.jl and xtb runs use the directory jlmol was started from as their working directory (--workdir from the npm start scripts, or a terminal's current directory), so exported files (orbitals.molden, xtbopt.xyz, ...) stay there instead of vanishing with a temp dir. Works from WSL running the Windows binary (POSIX path mapped to \\wsl.localhost\<distro>\...).
  • Image and XYZ exports open a save dialog defaulting to that directory; the status line shows the saved path.
  • "Open File…" button with a native open dialog starting in the same directory (replaces the file input's stale "No file chosen").
  • Molden files are checked before loading: wrong atomic-number column (tblite/g-xTB writes valence electron counts) is reported with "Fix and load" / "Fix, load and save…" / "Load as is"; an MO-count vs basis-size mismatch is reported.

Test plan

  • npm run smoke and --bridge-probe green locally (Linux, Xvfb), the probe also under a pty (launch dir detected, download lands there).
  • node launchdir.js self-test.
  • Daniel verified export dialog, Open File and Molden check on WSL.

🤖 Generated with Claude Code

dnkats and others added 9 commits September 22, 2026 08:25
…minal

Every run used a mkdtemp work dir under the OS temp dir that was deleted
when the run finished, so exported files (orbitals.molden from an Export
step, xtbopt.xyz, ...) vanished silently.

When jlmol is started from a terminal (stdin is a TTY), main registers
process.cwd() as a work-dir token that is never removed, and the ElemCo
and xtb runners use it: calculation.jl, coord.xyz and everything the
calculation writes stay there. Launches without a terminal keep the temp
dir. The output panel prints which directory is used. xtb removes a stale
xtbopt.xyz before a run, since success is detected from that file.

New bridge calls: launchDir(), removeFile(). The bridge probe checks the
removal and that removeWorkDir on the launch dir is a no-op.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Browser-style downloads (image export, XYZ export) landed silently in
the download folder. They now go through Electron's save dialog via
setSaveDialogOptions on the download item. The default is the launch
directory when started from a terminal, the download folder otherwise.
Main reports the outcome to the renderer (jlmol-download-done) and the
status line shows the saved path or that the export was cancelled.

In --bridge-probe mode the file is written to the default path without
a dialog (a native dialog cannot be driven headless); the probe checks
the default location and the done message under a pty.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`npm start` chdirs to the package root before running the script, so
process.cwd() was ~/projects/jlmol whatever directory the command was
typed in. npm exports the original directory as INIT_CWD; use it when
set. Terminal detection now accepts a TTY on stdin, stdout or stderr,
so a redirected stdin alone no longer disables the launch dir.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ripts

Under WSL `npm start` may run the Windows Electron binary, which gets
neither a TTY (WSL interop hands it pipes) nor npm's INIT_CWD (not in
WSLENV), so the terminal heuristic never fires and exports defaulted to
Downloads.

The start scripts now pass --workdir="$INIT_CWD" (the directory the
command was typed in) and --wsl-distro="$WSL_DISTRO_NAME". launchdir.js
resolves it: on win32 a POSIX path is mapped to C:\... for /mnt/c/...
or to \\wsl.localhost\<distro>\... (then \\wsl$\...), the first that
exists wins; an invalid value is logged and ignored. Without --workdir
the terminal heuristic (INIT_CWD, else cwd) remains. The module is
pure and carries a self-test (`node launchdir.js`).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… for WSL Julia

Daniel's log from WSL: "Launch directory: /home/kats/projects/jlmol
[--workdir]" and the dialog got "\home\kats\projects\jlmol\molecule.png".
On win32 Node calls a slash-rooted path absolute, and stat() resolved it
against the current drive, the \\wsl.localhost share, so it even
existed. The save dialog then fell back to the home directory. A POSIX
--workdir on win32 now only yields its mapped spellings (C:\ for /mnt/c,
\\wsl.localhost\<distro>\..., \\wsl$\...). Self-test covers the trap.

The Julia runner's WSL path translation now also maps
\\wsl.localhost\<distro>\rest to /rest, so calculation.jl in a launch
dir on the distro's own tree reaches `wsl julia` as a valid path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The browser file picker behind <input type="file"> cannot be pointed at
a directory. In the desktop app the input's click now opens
dialog.showOpenDialog with defaultPath = launch dir (when there is one;
otherwise the OS picks its usual folder), main reads the file and hands
{name, content} to the renderer. handleFileSelect's loading body is
extracted into loadStructureContent(name, content) and shared with the
browser build, which keeps the input.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The file input's "No file chosen" label never learns about files opened
through the native dialog. The desktop app now hides the input and shows
a button plus a label with the loaded file's name; the browser build
keeps the input. The #structureFile id rule (display: block) beat the
hidden attribute, hence the explicit [hidden] rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
tblite (the g-xTB binary's molden writer) puts the valence electron
count in the atomic-number column of [Atoms]: N 5, C 4, O 6. JSmol
trusts that column over the label and draws boron, beryllium and carbon
with garbled bonds. Daniel's caffeine file loads cleanly once the column
is corrected.

js/molden-check.js compares the column with the element label and
builds a corrected text; it also reports, without fixing, an MO
coefficient count that does not match the basis size (missing [5D]).
loadStructureContent runs the check for .molden files and shows a
dialog: "Fix and load", "Fix, load and save…" (a <name>-fixed.molden
copy through the save dialog) or "Load as is". Command-line files go
through loadStructureContent too, so they get the same check.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 22, 2026 08:33
@dnkats
dnkats merged commit e22ea6a into main Sep 22, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Five unresolved findings remain, including two critical concurrency issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 High severity · 3 Medium severity

Open (5)
What changed in this PR

Adds persistent launch-directory handling, native file dialogs, export status reporting, and Molden validation.

Changes:

  • Routes Julia and xTB outputs to launch directories.
  • Adds native open/save dialogs and WSL path support.
  • Validates Molden atomic numbers and basis sizes.
File Summary
README.md Documents launch-directory behavior.
preload.js Exposes file, download, and launch-directory APIs.
package.json Updates startup scripts; native Windows expansion remains problematic (moderate, 3 votes).
main.js Implements directory handling, dialogs, downloads, and probes.
launchdir.js Resolves launch directories; unnormalized paths can be rejected incorrectly (moderate, 2 votes).
js/​xtb.js Uses persistent xTB directories; concurrent runs can collide on shared files (critical, 3 votes).
js/​search.js Adds native file opening and Molden loading.
js/​molden-check.js Validates Molden files; [7F] and [9G] shells are ignored (moderate, 3 votes).
js/​image-export.js Reports export outcomes.
js/​elemco.js Runs Julia calculations in launch directories; concurrent runs can overwrite shared files (critical, 3 votes).
index.html Adds native file-open controls and Molden integration.
css/​styles.css Styles new controls and dialogs.
CHANGELOG.md Documents the new functionality.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread js/elemco.js
Comment on lines +1281 to +1286
const launch = await native.launchDir();
if (launch) {
workDir = launch.token;
} else {
workDir = await native.mkWorkDir('jlmol_julia_');
ownsWorkDir = true;
Comment thread js/xtb.js
Comment on lines +150 to +152
const launch = await native.launchDir();
const workDir = launch ? launch.token : await native.mkWorkDir('jlmol_xtb_');
const ownsWorkDir = !launch;
Comment thread js/molden-check.js
Comment on lines +95 to +96
const useSph = flags['5d'];
const expected = useSph ? sph : cart;
Comment thread launchdir.js
Comment on lines +45 to +53
for (const c of candidates) {
if (P.isAbsolute(c) && isDir(c)) return { dir: c, source: '--workdir' };
}
return { dir: null, source: `--workdir rejected (${explicit})` };
}
if (!isTTY()) return { dir: null, source: 'no terminal' };
const init = env.INIT_CWD;
if (init && P.isAbsolute(init) && isDir(init)) return { dir: init, source: 'terminal, INIT_CWD' };
return { dir: cwd(), source: 'terminal, cwd' };
Comment thread package.json
Comment on lines +7 to +8
"start": "electron . --workdir=\"$INIT_CWD\" --wsl-distro=\"$WSL_DISTRO_NAME\"",
"start-verbose": "electron . --verbose --workdir=\"$INIT_CWD\" --wsl-distro=\"$WSL_DISTRO_NAME\"",
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants