prolog-notebook 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.1] — 2026-08-31
4
+
5
+ ### Fixed
6
+
7
+ - **`build` could not find the engine when the tool was installed.** It looked for swipl-wasm at
8
+ `<this package>/node_modules/swipl-wasm/…`, and npm **hoists** — the dependency is installed as
9
+ a *sibling* of `prolog-notebook`, not inside it. Nesting is what npm falls back to on a version
10
+ conflict, so the one layout that path assumed is the one npm avoids:
11
+
12
+ ```
13
+ Error: ENOENT: no such file or directory, copyfile
14
+ '.../node_modules/prolog-notebook/node_modules/swipl-wasm/dist/swipl/swipl-bundle.js'
15
+ ```
16
+
17
+ This was never a 0.7.0 regression — 0.6.5 fails identically from a real install. `build` has
18
+ only ever worked from a checkout, where that path happens to exist, which is the only way we
19
+ had ever run it. The engine is now located through Node's own resolution, which finds the
20
+ package wherever it was put: hoisted, nested, in pnpm's store, or in a workspace.
21
+
22
+ ### Added
23
+
24
+ - **A test that uses the package the way somebody else receives it** — `npm run test:packaged`
25
+ packs the tarball, installs it elsewhere, and builds and runs a chapter with it, asserting
26
+ first that swipl-wasm really was hoisted so the test cannot pass vacuously. Every other test
27
+ runs against the checkout, where `node_modules` sits exactly where a relative path expects it,
28
+ which is precisely why this class of bug was invisible twice. CI runs it on every push and
29
+ before every publish.
30
+
31
+ ### Fixed
32
+
33
+ - **A chapter with no saved answers no longer offers a disabled *Restore outputs*.** Run a cell in
34
+ such a chapter, clear it, and the panel offered to put back answers that never existed — greyed
35
+ out, because there was nothing to put back. A disabled control says *this could happen, but not
36
+ now*, and there was no now. The button now reads *Clear all outputs*, disabled, which is the
37
+ page's opening state and the truth: nothing on screen, nothing to clear. The count still says
38
+ *1 output cleared*, because that part was never a lie — it is why the page looks empty.
39
+
40
+ Most chapters ship without saved answers, since `hold` is how an author keeps them from
41
+ students, so this was the common chapter's common path rather than an edge case.
42
+
3
43
  ## [0.7.0] — 2026-08-31
4
44
 
5
45
  A site of notebooks, rather than a folder of unrelated pages.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prolog-notebook",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Jupyter-style notebooks for Prolog. Runs in the browser, installs nothing.",
5
5
  "type": "module",
6
6
  "main": "./src/node.js",
@@ -30,7 +30,8 @@
30
30
  "scripts": {
31
31
  "test": "node --test test/*.test.mjs",
32
32
  "dev": "python3 -m http.server 8777",
33
- "prepublishOnly": "npm test"
33
+ "prepublishOnly": "npm test",
34
+ "test:packaged": "node --test test/packaged.mjs"
34
35
  },
35
36
  "keywords": [
36
37
  "prolog",
package/src/browser.js CHANGED
@@ -8,6 +8,17 @@ import { ConsultLog, defaultCellName, unconsult } from './session.js';
8
8
  export * from './engine.js';
9
9
  export { ConsultLog } from './session.js';
10
10
 
11
+ /**
12
+ * WHERE A PAGE SERVED OUT OF THIS REPO FINDS THE ENGINE — and only such a page.
13
+ *
14
+ * The same-looking literal in build.js was a bug: it guessed at a node_modules
15
+ * layout npm does not use, so `build` could not find the engine when the tool was
16
+ * installed (869erzf1j). This one is NOT that bug, and must not be "fixed" into a
17
+ * new one. There is no module resolution in a browser to ask instead, and every
18
+ * caller that ships — the app.js of a built page — passes `swiplUrl` outright. So
19
+ * this default is reached in exactly one situation: a page served from the repo
20
+ * root by `npm run dev`, where `../node_modules/…` from `/src/` is the truth.
21
+ */
11
22
  const DEFAULT_SWIPL_URL = new URL(
12
23
  '../node_modules/swipl-wasm/dist/swipl/swipl-bundle.js',
13
24
  import.meta.url
@@ -1,4 +1,4 @@
1
1
  {
2
- "commit": "7e40763",
3
- "built": "2026-08-31 10:59:41 UTC"
2
+ "commit": "b988a61",
3
+ "built": "2026-08-31 11:22:28 UTC"
4
4
  }
package/src/build.js CHANGED
@@ -16,6 +16,7 @@
16
16
  // `view` can serve it, and a test can read it, without any of the three
17
17
  // disagreeing about what a page is.
18
18
  import { createRequire } from 'node:module';
19
+ import { pathToFileURL } from 'node:url';
19
20
  import { parse } from './format.js';
20
21
  import { renderNotebook } from './render.js';
21
22
 
@@ -31,6 +32,25 @@ import { renderNotebook } from './render.js';
31
32
  const require = createRequire(import.meta.url);
32
33
  export const ENGINE_VERSION = require('swipl-wasm/package.json').version;
33
34
 
35
+ /**
36
+ * WHERE THE ENGINE ACTUALLY IS — asked for, never guessed (869erzf1j).
37
+ *
38
+ * This was `new URL('../node_modules/swipl-wasm/dist/swipl/', import.meta.url)`,
39
+ * which is true in a checkout and false in every install: npm HOISTS, so
40
+ * swipl-wasm lands as a SIBLING of this package rather than inside it, and
41
+ * `build` died on a missing file for anybody who had installed the tool rather
42
+ * than cloned it. Nested is the exception npm resorts to on a version conflict —
43
+ * the one layout that literal assumed was the one npm avoids.
44
+ *
45
+ * Node's own resolution finds the package wherever it was put: hoisted, nested,
46
+ * inside pnpm's store, or in a workspace. The line above already asked properly
47
+ * for the version; this one now asks properly for the bytes.
48
+ */
49
+ export const ENGINE_HOME = new URL(
50
+ 'dist/swipl/',
51
+ pathToFileURL(require.resolve('swipl-wasm/package.json')),
52
+ );
53
+
34
54
  /** The runtime a page needs. Copied side by side, so their relative imports hold. */
35
55
  export const RUNTIME = [
36
56
  'notebook.js', 'browser.js', 'session.js', 'engine.js', 'worker.js',
@@ -86,7 +106,7 @@ export function buildFiles(notebook, source, options = {}) {
86
106
  const {
87
107
  filename = 'notebook.prolog.md',
88
108
  src = new URL('./', import.meta.url),
89
- engine = new URL('../node_modules/swipl-wasm/dist/swipl/', import.meta.url),
109
+ engine = ENGINE_HOME,
90
110
  prefix = './',
91
111
  engineVersion = ENGINE_VERSION,
92
112
  } = options;
package/src/notebook.js CHANGED
@@ -502,14 +502,25 @@ function mountPageBar(root, options, bus, programs, queries) {
502
502
  refreshOutputs = () => {
503
503
  const gone = queries.filter((q) => q.isCleared());
504
504
  const left = queries.filter((q) => q.hasOutput());
505
- // Restore only once there is nothing left to clear, which is the rule the
506
- // hide/show button already follows: in a half-cleared page the useful move
507
- // is to finish, and one vocabulary is cheaper to learn than two.
508
- const back = left.length === 0 && gone.length > 0;
509
505
  // A cleared cell that never had saved answers has nothing of the chapter's
510
- // to give back, so a page of only those leaves restore with no work.
506
+ // to give back: restore is the way back to the FILE, and the file is empty.
511
507
  const restorable = gone.filter((q) => q.hasSaved).length;
512
- wipe.disabled = back ? restorable === 0 : left.length === 0;
508
+ // Restore only once there is nothing left to clear — the rule the hide/show
509
+ // button already follows, since in a half-cleared page the useful move is to
510
+ // finish — AND only when there is something to put back.
511
+ //
512
+ // That second half was missing (869erzhe7): a chapter published without
513
+ // answers, run by the reader and then cleared, offered them a DISABLED
514
+ // "Restore outputs". The Captain: "it shows the incorrect 'Restore output' -
515
+ // disabled … dont show restore when there's nothing to restore."
516
+ //
517
+ // He is right, and it is the doctrine rather than a detail. A disabled
518
+ // control says "this could happen, but not now"; there is no now in which
519
+ // that chapter's answers come back, because it never had any. The true
520
+ // reading of that state is the one the page started in — no outputs, nothing
521
+ // to clear — so the button says so.
522
+ const back = left.length === 0 && restorable > 0;
523
+ wipe.disabled = !back && left.length === 0;
513
524
  // COUNTED FROM THE PAGE, which is the only count that stays true for both
514
525
  // kinds of chapter: one published with its answers in it, and one still
515
526
  // being written where every output on screen is the reader's own.
package/src/version.js CHANGED
@@ -10,7 +10,7 @@
10
10
  export const NAME = 'Prolog Notebook';
11
11
 
12
12
  /** Must equal package.json's `version` — test/run.test.mjs enforces it. */
13
- export const VERSION = '0.7.0';
13
+ export const VERSION = '0.7.1';
14
14
 
15
15
  /** The two facts a licence notice is actually made of. */
16
16
  export const YEAR = '2026';