diff --git a/.github/workflows/Bundle.yml b/.github/workflows/Bundle.yml
index 665ead2577..ff2c46bcec 100644
--- a/.github/workflows/Bundle.yml
+++ b/.github/workflows/Bundle.yml
@@ -13,7 +13,7 @@ on:
- release
concurrency:
- group: bundle
+ group: bundle-${{ github.ref }}
cancel-in-progress: false
jobs:
diff --git a/.github/workflows/FrontendTest.yml b/.github/workflows/FrontendTest.yml
index a9b5d55961..07e6cf55da 100644
--- a/.github/workflows/FrontendTest.yml
+++ b/.github/workflows/FrontendTest.yml
@@ -63,17 +63,19 @@ jobs:
- name: Run tests
working-directory: ./test/frontend
run: |
- julia -e "import Pkg; Pkg.add(path=\"$GITHUB_WORKSPACE\"); Pkg.instantiate(); import Pluto; Pluto.run(port=$PLUTO_PORT, require_secret_for_access=false)" & # Run Pluto.jl server in the background
+ julia --project=$GITHUB_WORKSPACE -e 'import Pluto
+ # Run Pluto.jl server in the background
+ options = Pluto.Configuration.from_flat_kwargs(;
+ port=parse(Int, ENV["PLUTO_PORT"]),
+ require_secret_for_access=false,
+ )
+ ๐ญ = Pluto.ServerSession(; options)
+ server = Pluto.run!(๐ญ)
- # Wait for Pluto to initialize
- TIMES_TRIED=0
- until [ $TIMES_TRIED -eq 60 ] || $(curl --output /dev/null --silent --fail "http://localhost:$PLUTO_PORT/"); do
- printf '.'
- TIMES_TRIED=$((TIMES_TRIED+1))
- sleep 1
- done
+ run(`npm run test`)
+
+ close(server)'
- npm run test
env:
PLUTO_PORT: 1235
PLUTO_TEST_OFFLINE: ${{ github.ref_name == 'release' }}
diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml
index 37e73f3784..27443f0c5a 100644
--- a/.github/workflows/Test.yml
+++ b/.github/workflows/Test.yml
@@ -27,14 +27,14 @@ on:
jobs:
test:
runs-on: ${{ matrix.os }}
- timeout-minutes: 40
+ timeout-minutes: 50
strategy:
# Without setting this, a failing test cancels all others
fail-fast: false
matrix:
# We test quite a lot of versions because we do some OS and version specific things unfortunately
- julia-version: ["1.6", "1.7", "1.8", "1.9"] #, "nightly"]
+ julia-version: ["1.6", "1.8", "1.9", "~1.10.0-0"]
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
diff --git a/.github/workflows/TypeScriptCheck.yml b/.github/workflows/TypeScriptCheck.yml
index d4830bc6c8..bf8b115bc2 100644
--- a/.github/workflows/TypeScriptCheck.yml
+++ b/.github/workflows/TypeScriptCheck.yml
@@ -26,7 +26,7 @@ jobs:
with:
node-version: "18.x"
- - run: npm install typescript -g
+ - run: npm install typescript@5.0.4 -g
- run: npm install
working-directory: frontend
diff --git a/Project.toml b/Project.toml
index 29c146fc70..7292cf6847 100644
--- a/Project.toml
+++ b/Project.toml
@@ -2,13 +2,12 @@ name = "Pluto"
uuid = "c3e4b0f8-55cb-11ea-2926-15256bba5781"
license = "MIT"
authors = ["Fons van der Plas "]
-version = "0.19.25"
+version = "0.19.27"
[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Configurations = "5218b696-f38b-4ac9-8b61-a12ec717816d"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
-Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
FileWatching = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
FuzzyCompletions = "fb4132e2-a121-4a70-b8a1-d5b831dcdcc2"
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
@@ -17,6 +16,7 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36"
MIMEs = "6c6e2e6c-3030-632d-7369-2d6c69616d65"
+Malt = "36869731-bdee-424d-aa32-cab38c994e3b"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
MsgPack = "99f44e22-a591-53d1-9472-aa23ef4bd671"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
@@ -25,6 +25,7 @@ PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
RegistryInstances = "2792f1a3-b283-48e8-9a74-f99dce5104f3"
RelocatableFolders = "05181044-ff0b-4ac5-8273-598c1e38db00"
+Scratch = "6c6a2e73-6563-6170-7368-637461726353"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
@@ -38,11 +39,13 @@ HTTP = "^1.5.2"
HypertextLiteral = "0.7, 0.8, 0.9"
LoggingExtras = "0.4, 1"
MIMEs = "0.1"
+Malt = "1.0.3"
MsgPack = "1.1"
PrecompileSignatures = "3"
PrecompileTools = "1"
RegistryInstances = "0.1"
RelocatableFolders = "0.1, 0.2, 0.3, 1"
+Scratch = "1.1"
Tables = "1"
URIs = "1.3"
julia = "^1.6"
diff --git a/frontend-bundler/package-lock.json b/frontend-bundler/package-lock.json
index ee443a186a..a9b30f6cff 100644
--- a/frontend-bundler/package-lock.json
+++ b/frontend-bundler/package-lock.json
@@ -76,9 +76,9 @@
}
},
"node_modules/@babel/core/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"bin": {
"semver": "bin/semver.js"
}
@@ -114,9 +114,9 @@
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"bin": {
"semver": "bin/semver.js"
}
@@ -385,6 +385,58 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+ "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
+ "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
"node_modules/@lezer/common": {
"version": "0.15.12",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz",
@@ -6096,9 +6148,9 @@
}
},
"node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"bin": {
"semver": "bin/semver"
}
@@ -6427,12 +6479,13 @@
}
},
"node_modules/terser": {
- "version": "5.9.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz",
- "integrity": "sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==",
+ "version": "5.14.2",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
+ "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
"dependencies": {
+ "@jridgewell/source-map": "^0.3.2",
+ "acorn": "^8.5.0",
"commander": "^2.20.0",
- "source-map": "~0.7.2",
"source-map-support": "~0.5.20"
},
"bin": {
@@ -6442,19 +6495,22 @@
"node": ">=10"
}
},
+ "node_modules/terser/node_modules/acorn": {
+ "version": "8.7.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
+ "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/terser/node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
- "node_modules/terser/node_modules/source-map": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
- "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/timers-browserify": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz",
@@ -6874,9 +6930,9 @@
}
},
"node_modules/word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
+ "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
"engines": {
"node": ">=0.10.0"
}
@@ -7079,9 +7135,9 @@
},
"dependencies": {
"semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
}
}
},
@@ -7107,9 +7163,9 @@
},
"dependencies": {
"semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
}
}
},
@@ -7304,6 +7360,49 @@
"to-fast-properties": "^2.0.0"
}
},
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
+ },
+ "@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
+ },
+ "@jridgewell/source-map": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+ "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
+ "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
"@lezer/common": {
"version": "0.15.12",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz",
@@ -11364,9 +11463,9 @@
}
},
"semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
},
"serve-handler": {
"version": "6.1.5",
@@ -11618,24 +11717,25 @@
"integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="
},
"terser": {
- "version": "5.9.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz",
- "integrity": "sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==",
+ "version": "5.14.2",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
+ "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
"requires": {
+ "@jridgewell/source-map": "^0.3.2",
+ "acorn": "^8.5.0",
"commander": "^2.20.0",
- "source-map": "~0.7.2",
"source-map-support": "~0.5.20"
},
"dependencies": {
+ "acorn": {
+ "version": "8.7.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
+ "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
+ },
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
- },
- "source-map": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
- "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
}
}
},
@@ -11986,9 +12086,9 @@
}
},
"word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
+ "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA=="
},
"wrap-ansi": {
"version": "7.0.0",
diff --git a/frontend/common/PlutoConnection.js b/frontend/common/PlutoConnection.js
index b4c336a04d..4e6fa5660a 100644
--- a/frontend/common/PlutoConnection.js
+++ b/frontend/common/PlutoConnection.js
@@ -265,7 +265,7 @@ const default_ws_address = () => ws_address_from_base(window.location.href)
* @param {{
* on_unrequested_update: (message: PlutoMessage, by_me: boolean) => void,
* on_reconnect: () => boolean,
- * on_connection_status: (connection_status: boolean) => void,
+ * on_connection_status: (connection_status: boolean, hopeless: boolean) => void,
* connect_metadata?: Object,
* ws_address?: String,
* }} options
@@ -364,7 +364,7 @@ export const create_pluto_connection = async ({
on_unrequested_update(update, by_me)
},
on_socket_close: async () => {
- on_connection_status(false)
+ on_connection_status(false, false)
console.log(`Starting new websocket`, new Date().toLocaleTimeString())
await Promises.delay(reconnect_after_close_delay)
@@ -373,7 +373,7 @@ export const create_pluto_connection = async ({
console.log(`Starting state sync`, new Date().toLocaleTimeString())
const accept = on_reconnect()
console.log(`State sync ${accept ? "" : "not "}successful`, new Date().toLocaleTimeString())
- on_connection_status(accept)
+ on_connection_status(accept, false)
if (!accept) {
alert("Connection out of sync ๐ฅ\n\nRefresh the page to continue")
}
@@ -394,14 +394,10 @@ export const create_pluto_connection = async ({
console.log("Client object: ", client)
if (connect_metadata.notebook_id != null && !u.message.notebook_exists) {
- // https://github.com/fonsp/Pluto.jl/issues/55
- if (confirm("A new server was started - this notebook session is no longer running.\n\nWould you like to go back to the main menu?")) {
- window.location.href = "./"
- }
- on_connection_status(false)
+ on_connection_status(false, true)
return {}
}
- on_connection_status(true)
+ on_connection_status(true, false)
const ping = () => {
send("ping", {}, {})
diff --git a/frontend/common/SliderServerClient.js b/frontend/common/SliderServerClient.js
index 49e64ffb19..f93f46efd3 100644
--- a/frontend/common/SliderServerClient.js
+++ b/frontend/common/SliderServerClient.js
@@ -8,6 +8,23 @@ const assert_response_ok = (/** @type {Response} */ r) => (r.ok ? r : Promise.re
const actions_to_keep = ["get_published_object"]
+const get_start = (graph, v) => Object.values(graph).find((node) => Object.keys(node.downstream_cells_map).includes(v))?.cell_id
+const get_starts = (graph, vars) => new Set([...vars].map((v) => get_start(graph, v)))
+const recursive_dependencies = (graph, starts) => {
+ const deps = new Set(starts)
+ const ends = [...starts]
+ while (ends.length > 0) {
+ const node = ends.splice(0, 1)[0]
+ _.flatten(Object.values(graph[node].downstream_cells_map)).forEach((child) => {
+ if (!deps.has(child)) {
+ ends.push(child)
+ deps.add(child)
+ }
+ })
+ }
+ return deps
+}
+
export const nothing_actions = ({ actions }) =>
Object.fromEntries(
Object.entries(actions).map(([k, v]) => [
@@ -23,6 +40,12 @@ export const nothing_actions = ({ actions }) =>
)
export const slider_server_actions = ({ setStatePromise, launch_params, actions, get_original_state, get_current_state, apply_notebook_patches }) => {
+ setStatePromise(
+ immer((state) => {
+ state.slider_server.connecting = true
+ })
+ )
+
const notebookfile_hash = fetch(new Request(launch_params.notebookfile, { integrity: launch_params.notebookfile_integrity }))
.then(assert_response_ok)
.then((r) => r.arrayBuffer())
@@ -36,7 +59,15 @@ export const slider_server_actions = ({ setStatePromise, launch_params, actions,
.then((r) => r.arrayBuffer())
.then((b) => unpack(new Uint8Array(b)))
- bond_connections.then((x) => console.log("Bond connections:", x))
+ bond_connections.then((x) => {
+ console.log("Bond connections:", x)
+ setStatePromise(
+ immer((state) => {
+ state.slider_server.connecting = false
+ state.slider_server.interactive = Object.keys(x).length > 0
+ })
+ )
+ })
const mybonds = {}
const bonds_to_set = {
@@ -47,6 +78,20 @@ export const slider_server_actions = ({ setStatePromise, launch_params, actions,
const hash = await notebookfile_hash
const graph = await bond_connections
+ // compute dependencies and update cell running statuses
+ const dep_graph = get_current_state().cell_dependencies
+ const starts = get_starts(dep_graph, bonds_to_set.current)
+ const running_cells = [...recursive_dependencies(dep_graph, starts)]
+
+ const update_cells_running = async (running) =>
+ await setStatePromise(
+ immer((state) => {
+ running_cells.forEach((cell_id) => (state.notebook.cell_results[cell_id][starts.has(cell_id) ? "running" : "queued"] = running))
+ })
+ )
+
+ await update_cells_running(true)
+
if (bonds_to_set.current.size > 0) {
const to_send = new Set(bonds_to_set.current)
bonds_to_set.current.forEach((varname) => (graph[varname] ?? []).forEach((x) => to_send.add(x)))
@@ -92,6 +137,9 @@ export const slider_server_actions = ({ setStatePromise, launch_params, actions,
)
} catch (e) {
console.error(unpacked, e)
+ } finally {
+ // reset cell running state regardless of request outcome
+ await update_cells_running(false)
}
}
})
diff --git a/frontend/components/Cell.js b/frontend/components/Cell.js
index 618964b72b..dea158dc5f 100644
--- a/frontend/components/Cell.js
+++ b/frontend/components/Cell.js
@@ -136,7 +136,31 @@ export const Cell = ({
const remount = useMemo(() => () => setKey(key + 1))
// cm_forced_focus is null, except when a line needs to be highlighted because it is part of a stack trace
const [cm_forced_focus, set_cm_forced_focus] = useState(/** @type{any} */ (null))
+ const [cm_highlighted_range, set_cm_highlighted_range] = useState(null)
const [cm_highlighted_line, set_cm_highlighted_line] = useState(null)
+ const [cm_diagnostics, set_cm_diagnostics] = useState([])
+
+ useEffect(() => {
+ const diagnosticListener = (e) => {
+ if (e.detail.cell_id === cell_id) {
+ set_cm_diagnostics(e.detail.diagnostics)
+ }
+ }
+ window.addEventListener("cell_diagnostics", diagnosticListener)
+ return () => window.removeEventListener("cell_diagnostics", diagnosticListener)
+ }, [cell_id])
+
+ useEffect(() => {
+ const highlightRangeListener = (e) => {
+ if (e.detail.cell_id == cell_id && e.detail.from != null && e.detail.to != null) {
+ set_cm_highlighted_range({ from: e.detail.from, to: e.detail.to })
+ } else {
+ set_cm_highlighted_range(null)
+ }
+ }
+ window.addEventListener("cell_highlight_range", highlightRangeListener)
+ return () => window.removeEventListener("cell_highlight_range", highlightRangeListener)
+ }, [cell_id])
useEffect(() => {
const focusListener = (e) => {
@@ -278,7 +302,7 @@ export const Cell = ({
pluto_actions.add_remote_cell(cell_id, "before")
}}
class="add_cell before"
- title="Add cell"
+ title="Add cell (Ctrl + Enter)"
>
@@ -309,10 +333,13 @@ export const Cell = ({
set_show_logs=${set_show_logs}
set_cell_disabled=${set_cell_disabled}
cm_highlighted_line=${cm_highlighted_line}
- set_cm_highlighted_line=${set_cm_highlighted_line}
+ cm_highlighted_range=${cm_highlighted_range}
+ cm_diagnostics=${cm_diagnostics}
onerror=${remount}
/>
- ${show_logs ? html`<${Logs} logs=${Object.values(logs)} line_heights=${line_heights} set_cm_highlighted_line=${set_cm_highlighted_line} />` : null}
+ ${show_logs && cell_api_ready
+ ? html`<${Logs} logs=${Object.values(logs)} line_heights=${line_heights} set_cm_highlighted_line=${set_cm_highlighted_line} />`
+ : null}
<${RunArea}
cell_id=${cell_id}
running_disabled=${running_disabled}
@@ -333,7 +360,7 @@ export const Cell = ({
pluto_actions.add_remote_cell(cell_id, "after")
}}
class="add_cell after"
- title="Add cell"
+ title="Add cell (Ctrl + Enter)"
>
@@ -386,8 +413,10 @@ export const IsolatedCell = ({ cell_input: { cell_id, metadata }, cell_result: {
return html`
- ${cell_api_ready ? html`<${CellOutput} ...${output} sanitize_html=${sanitize_html} cell_id=${cell_id} />` : html``}
- ${show_logs ? html`<${Logs} logs=${Object.values(logs)} line_heights=${[15]} set_cm_highlighted_line=${() => {}} />` : null}
+ <<<<<<< HEAD ${cell_api_ready ? html`<${CellOutput} ...${output} sanitize_html=${sanitize_html} cell_id=${cell_id} />` : html``}
+ ${show_logs ? html`<${Logs} logs=${Object.values(logs)} line_heights=${[15]} set_cm_highlighted_line=${() => {}} />` : null} =======
+ ${cell_api_ready ? html`<${CellOutput} ...${output} cell_id=${cell_id} />` : html``}
+ ${show_logs ? html`<${Logs} logs=${Object.values(logs)} line_heights=${[15]} set_cm_highlighted_line=${() => {}} />` : null} >>>>>>> main
`
}
diff --git a/frontend/components/CellInput.js b/frontend/components/CellInput.js
index e3a57c6421..247775d413 100644
--- a/frontend/components/CellInput.js
+++ b/frontend/components/CellInput.js
@@ -48,6 +48,7 @@ import {
pythonLanguage,
syntaxHighlighting,
cssLanguage,
+ setDiagnostics,
} from "../imports/CodemirrorPlutoSetup.js"
import { markdown, html as htmlLang, javascript, sqlLang, python, julia_mixed } from "./CellInput/mixedParsers.js"
@@ -59,7 +60,7 @@ import { cell_movement_plugin, prevent_holding_a_key_from_doing_things_across_ce
import { pluto_paste_plugin } from "./CellInput/pluto_paste_plugin.js"
import { bracketMatching } from "./CellInput/block_matcher_plugin.js"
import { cl } from "../common/ClassTable.js"
-import { HighlightLineFacet, highlightLinePlugin } from "./CellInput/highlight_line.js"
+import { HighlightLineFacet, HighlightRangeFacet, highlightLinePlugin, highlightRangePlugin } from "./CellInput/highlight_line.js"
import { commentKeymap } from "./CellInput/comment_mixed_parsers.js"
import { ScopeStateField } from "./CellInput/scopestate_statefield.js"
import { mod_d_command } from "./CellInput/mod_d_command.js"
@@ -373,8 +374,10 @@ export const CellInput = ({
set_show_logs,
set_cell_disabled,
cm_highlighted_line,
+ cm_highlighted_range,
metadata,
global_definition_locations,
+ cm_diagnostics,
}) => {
let pluto_actions = useContext(PlutoActionsContext)
const { disabled: running_disabled, skip_as_script } = metadata
@@ -384,6 +387,7 @@ export const CellInput = ({
set_error(null)
throw to_throw
}
+
const notebook_id_ref = useRef(notebook_id)
notebook_id_ref.current = notebook_id
@@ -394,6 +398,7 @@ export const CellInput = ({
let nbpkg_compartment = useCompartment(newcm_ref, NotebookpackagesFacet.of(nbpkg))
let global_definitions_compartment = useCompartment(newcm_ref, GlobalDefinitionsFacet.of(global_definition_locations))
let highlighted_line_compartment = useCompartment(newcm_ref, HighlightLineFacet.of(cm_highlighted_line))
+ let highlighted_range_compartment = useCompartment(newcm_ref, HighlightRangeFacet.of(cm_highlighted_range))
let editable_compartment = useCompartment(newcm_ref, EditorState.readOnly.of(disable_input))
let on_change_compartment = useCompartment(
@@ -589,9 +594,11 @@ export const CellInput = ({
// Compartments coming from react state/props
nbpkg_compartment,
highlighted_line_compartment,
+ highlighted_range_compartment,
global_definitions_compartment,
editable_compartment,
highlightLinePlugin(),
+ highlightRangePlugin(),
// This is waaaay in front of the keys it is supposed to override,
// Which is necessary because it needs to run before *any* keymap,
@@ -631,17 +638,25 @@ export const CellInput = ({
// Remove selection on blur
EditorView.domEventHandlers({
blur: (event, view) => {
- // collapse the selection into a single point
- view.dispatch({
- selection: {
- anchor: view.state.selection.main.head,
- },
- scrollIntoView: false,
- })
- // and blur the DOM again (because the previous transaction might have re-focused it)
- view.contentDOM.blur()
-
- set_cm_forced_focus(null)
+ // it turns out that this condition is true *exactly* if and only if the blur event was triggered by blurring the window
+ let caused_by_window_blur = document.activeElement === view.contentDOM
+
+ if (!caused_by_window_blur) {
+ // then it's caused by focusing something other than this cell in the editor.
+ // in this case, we want to collapse the selection into a single point, for aesthetic reasons.
+ setTimeout(() => {
+ view.dispatch({
+ selection: {
+ anchor: view.state.selection.main.head,
+ },
+ scrollIntoView: false,
+ })
+ // and blur the DOM again (because the previous transaction might have re-focused it)
+ view.contentDOM.blur()
+ }, 0)
+
+ set_cm_forced_focus(null)
+ }
},
}),
pluto_paste_plugin({
@@ -707,6 +722,12 @@ export const CellInput = ({
// Wowww this has been enabled for some time now... wonder if there are issues about this yet ;) - DRAL
awesome_line_wrapping,
+ // Reset diagnostics on change
+ EditorView.updateListener.of((update) => {
+ if (!update.docChanged) return
+ update.view.dispatch(setDiagnostics(update.state, []))
+ }),
+
on_change_compartment,
// This is my weird-ass extension that checks the AST and shows you where
@@ -772,6 +793,14 @@ export const CellInput = ({
}
}, [])
+ useEffect(() => {
+ if (newcm_ref.current == null) return
+ const cm = newcm_ref.current
+ const diagnostics = cm_diagnostics
+
+ cm.dispatch(setDiagnostics(cm.state, diagnostics))
+ }, [cm_diagnostics])
+
// Effect to apply "remote_code" to the cell when it changes...
// ideally this won't be necessary as we'll have actual multiplayer,
// or something to tell the user that the cell is out of sync.
diff --git a/frontend/components/CellInput/highlight_line.js b/frontend/components/CellInput/highlight_line.js
index f66346f878..d5938350d4 100644
--- a/frontend/components/CellInput/highlight_line.js
+++ b/frontend/components/CellInput/highlight_line.js
@@ -4,6 +4,10 @@ const highlighted_line = Decoration.line({
attributes: { class: "cm-highlighted-line" },
})
+const highlighted_range = Decoration.mark({
+ attributes: { class: "cm-highlighted-range" },
+})
+
/**
* @param {EditorView} view
*/
@@ -17,6 +21,22 @@ function create_line_decorations(view) {
return Decoration.set([highlighted_line.range(line.from, line.from)])
}
+/**
+ * @param {EditorView} view
+ */
+function create_range_decorations(view) {
+ let range = view.state.facet(HighlightRangeFacet)
+ if (range == null) {
+ return Decoration.set([])
+ }
+ let { from, to } = range
+ if (from < 0 || from == to) {
+ return Decoration.set([])
+ }
+
+ return Decoration.set([highlighted_range.range(from, to)])
+}
+
/**
* @type Facet
*/
@@ -25,6 +45,14 @@ export const HighlightLineFacet = Facet.define({
compare: (a, b) => a === b,
})
+/**
+ * @type Facet<{from: number, to: number}?, {from: number, to: number}?>
+ */
+export const HighlightRangeFacet = Facet.define({
+ combine: (values) => values[0],
+ compare: (a, b) => a === b,
+})
+
export const highlightLinePlugin = () =>
ViewPlugin.fromClass(
class {
@@ -53,3 +81,33 @@ export const highlightLinePlugin = () =>
decorations: (v) => v.decorations,
}
)
+
+
+export const highlightRangePlugin = () =>
+ ViewPlugin.fromClass(
+ class {
+ updateDecos(view) {
+ this.decorations = create_range_decorations(view)
+ }
+
+ /**
+ * @param {EditorView} view
+ */
+ constructor(view) {
+ this.decorations = Decoration.set([])
+ this.updateDecos(view)
+ }
+
+ /**
+ * @param {ViewUpdate} update
+ */
+ update(update) {
+ if (update.docChanged || update.state.facet(HighlightRangeFacet) !== update.startState.facet(HighlightRangeFacet)) {
+ this.updateDecos(update.view)
+ }
+ }
+ },
+ {
+ decorations: (v) => v.decorations,
+ }
+ )
diff --git a/frontend/components/CellInput/pluto_autocomplete.js b/frontend/components/CellInput/pluto_autocomplete.js
index 2130d6a484..94885157fc 100644
--- a/frontend/components/CellInput/pluto_autocomplete.js
+++ b/frontend/components/CellInput/pluto_autocomplete.js
@@ -131,7 +131,11 @@ let update_docs_from_autocomplete_selection = (on_update_doc_query) => {
// The nice thing about this is that we can use the resulting state from the transaction,
// without updating the actual state of the editor.
let result_transaction = update.state.update({
- changes: { from: selected_option.source.from, to: selected_option.source.to, insert: text_to_apply },
+ changes: {
+ from: selected_option.source.from,
+ to: Math.min(selected_option.source.to, update.state.doc.length),
+ insert: text_to_apply,
+ },
})
// So we can use `get_selected_doc_from_state` on our virtual state
diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js
index 1b7dd13369..e5cd3d9d45 100644
--- a/frontend/components/CellOutput.js
+++ b/frontend/components/CellOutput.js
@@ -2,7 +2,7 @@ import { html, Component, useRef, useLayoutEffect, useContext } from "../imports
import DOMPurify from "../imports/DOMPurify.js"
-import { ErrorMessage } from "./ErrorMessage.js"
+import { ErrorMessage, ParseError } from "./ErrorMessage.js"
import { TreeView, TableView, DivElement } from "./TreeView.js"
import {
@@ -151,6 +151,9 @@ export const OutputBody = ({ mime, body, cell_id, persist_js_state = false, last
case "application/vnd.pluto.table+object":
return html`<${TableView} cell_id=${cell_id} body=${body} persist_js_state=${persist_js_state} sanitize_html=${sanitize_html} />`
break
+ case "application/vnd.pluto.parseerror+object":
+ return html`<${ParseError} cell_id=${cell_id} ...${body} />
`
+ break
case "application/vnd.pluto.stacktrace+object":
return html`<${ErrorMessage} cell_id=${cell_id} ...${body} />
`
break
@@ -385,6 +388,21 @@ const execute_scripttags = async ({ root_node, script_nodes, previous_results_ma
delete notebook.metadata[key]
}),
+ ...(cell == null
+ ? {}
+ : {
+ getCellMetadataExperimental: (key, { cell_id = null } = {}) =>
+ pluto_actions.get_notebook()?.cell_inputs?.[cell_id ?? cell.id]?.metadata[key],
+ setCellMetadataExperimental: (key, value, { cell_id = null } = {}) =>
+ pluto_actions.update_notebook((notebook) => {
+ notebook.cell_inputs[cell_id ?? cell.id].metadata[key] = value
+ }),
+ deleteCellMetadataExperimental: (key, { cell_id = null } = {}) =>
+ pluto_actions.update_notebook((notebook) => {
+ delete notebook.cell_inputs[cell_id ?? cell.id].metadata[key]
+ }),
+ }),
+
...observablehq_for_cells,
},
code,
diff --git a/frontend/components/Editor.js b/frontend/components/Editor.js
index 60085c8c64..42d17cb8e9 100644
--- a/frontend/components/Editor.js
+++ b/frontend/components/Editor.js
@@ -4,7 +4,7 @@ import immer, { applyPatches, produceWithPatches } from "../imports/immer.js"
import _ from "../imports/lodash.js"
import { empty_notebook_state, set_disable_ui_css } from "../editor.js"
-import { create_pluto_connection } from "../common/PlutoConnection.js"
+import { create_pluto_connection, ws_address_from_base } from "../common/PlutoConnection.js"
import { init_feedback } from "../common/Feedback.js"
import { serialize_cells, deserialize_cells, detect_deserializer } from "../common/Serialization.js"
@@ -18,7 +18,7 @@ import { RecentlyDisabledInfo, UndoDelete } from "./UndoDelete.js"
import { SlideControls } from "./SlideControls.js"
import { Scroller } from "./Scroller.js"
import { ExportBanner } from "./ExportBanner.js"
-import { Popup } from "./Popup.js"
+import { open_pluto_popup, Popup } from "./Popup.js"
import { slice_utf8, length_utf8 } from "../common/UnicodeTools.js"
import { has_ctrl_or_cmd_pressed, ctrl_or_cmd_name, is_mac_keyboard, in_textarea_or_input } from "../common/KeyboardShortcuts.js"
@@ -343,6 +343,11 @@ export class Editor extends Component {
is_recording: false,
recording_waiting_to_start: false,
+
+ slider_server: {
+ connecting: false,
+ interactive: false,
+ },
}
this.setStatePromise = (fn) => new Promise((r) => this.setState(fn, r))
@@ -842,7 +847,30 @@ patch: ${JSON.stringify(
setTimeout(init_feedback, 2 * 1000) // 2 seconds - load feedback a little later for snappier UI
}
- const on_connection_status = (val) => this.setState({ connected: val })
+ const on_connection_status = (val, hopeless) => {
+ this.setState({ connected: val })
+ if (hopeless) {
+ // https://github.com/fonsp/Pluto.jl/issues/55
+ // https://github.com/fonsp/Pluto.jl/issues/2398
+ open_pluto_popup({
+ type: "warn",
+ source_element: null,
+ body: html`A new server was started - this notebook session is no longer running.
+ Would you like to go back to the main menu?
+
+ Go back
+
+ {
+ e.preventDefault()
+ window.dispatchEvent(new CustomEvent("close pluto popup"))
+ }}
+ >Stay here`,
+ })
+ }
+ }
const on_reconnect = () => {
console.warn("Reconnected! Checking states")
@@ -858,7 +886,7 @@ patch: ${JSON.stringify(
/** @type {import('../common/PlutoConnection').PlutoConnection} */
this.client = /** @type {import('../common/PlutoConnection').PlutoConnection} */ ({})
- this.connect = (ws_address = undefined) =>
+ this.connect = (/** @type {string | undefined} */ ws_address = undefined) =>
create_pluto_connection({
ws_address: ws_address,
on_unrequested_update: on_update,
@@ -1164,13 +1192,13 @@ patch: ${JSON.stringify(
// if (e.defaultPrevented) {
// return
// }
- if (e.key.toLowerCase() === "q" && has_ctrl_or_cmd_pressed(e)) {
+ if (e.key?.toLowerCase() === "q" && has_ctrl_or_cmd_pressed(e)) {
// This one can't be done as cmd+q on mac, because that closes chrome - Dral
if (Object.values(this.state.notebook.cell_results).some((c) => c.running || c.queued)) {
this.actions.interrupt_remote()
}
e.preventDefault()
- } else if (e.key.toLowerCase() === "s" && has_ctrl_or_cmd_pressed(e)) {
+ } else if (e.key?.toLowerCase() === "s" && has_ctrl_or_cmd_pressed(e)) {
const some_cells_ran = this.actions.set_and_run_all_changed_remote_cells()
if (!some_cells_ran) {
// all cells were in sync allready
@@ -1218,8 +1246,8 @@ patch: ${JSON.stringify(
}
if (this.state.disable_ui && this.state.backend_launch_phase === BackendLaunchPhase.wait_for_user) {
- // const code = e.key.charCodeAt(0)
- if (e.key === "Enter" || e.key.length === 1) {
+ // const code = e.key?.charCodeAt(0)
+ if (e.key === "Enter" || e.key?.length === 1) {
if (!document.body.classList.contains("wiggle_binder")) {
document.body.classList.add("wiggle_binder")
setTimeout(() => {
@@ -1308,7 +1336,7 @@ patch: ${JSON.stringify(
count_stat(`article-view`)
}
} else {
- this.connect()
+ this.connect(this.props.launch_params.pluto_server_url ? ws_address_from_base(this.props.launch_params.pluto_server_url) : undefined)
}
}
diff --git a/frontend/components/ErrorMessage.js b/frontend/components/ErrorMessage.js
index 173243ee9b..60e83a1515 100644
--- a/frontend/components/ErrorMessage.js
+++ b/frontend/components/ErrorMessage.js
@@ -1,5 +1,8 @@
import { PlutoActionsContext } from "../common/PlutoContext.js"
-import { html, useContext, useState } from "../imports/Preact.js"
+import { EditorState, EditorView, julia_andrey, lineNumbers, syntaxHighlighting } from "../imports/CodemirrorPlutoSetup.js"
+import { html, useContext, useEffect, useLayoutEffect, useRef, useState } from "../imports/Preact.js"
+import { pluto_syntax_colors } from "./CellInput.js"
+import { Editor } from "./Editor.js"
const StackFrameFilename = ({ frame, cell_id }) => {
const sep_index = frame.file.indexOf("#==#")
@@ -38,6 +41,43 @@ const Funccall = ({ frame }) => {
const insert_commas_and_and = (/** @type {any[]} */ xs) => xs.flatMap((x, i) => (i === xs.length - 1 ? [x] : i === xs.length - 2 ? [x, " and "] : [x, ", "]))
+export const ParseError = ({ cell_id, diagnostics }) => {
+ useEffect(() => {
+ window.dispatchEvent(
+ new CustomEvent("cell_diagnostics", {
+ detail: {
+ cell_id,
+ diagnostics,
+ },
+ })
+ )
+ return () => window.dispatchEvent(new CustomEvent("cell_diagnostics", { detail: { cell_id, diagnostics: [] } }))
+ }, [diagnostics])
+
+ return html`
+
+
+
+
+ ${diagnostics.map(
+ ({ message, from, to, line }) =>
+ html`- // NOTE: this could be moved move to `StackFrameFilename`
+ window.dispatchEvent(new CustomEvent("cell_highlight_range", { detail: { cell_id, from, to }}))
+ }
+ onmouseleave=${() =>
+ window.dispatchEvent(new CustomEvent("cell_highlight_range", { detail: { cell_id, from: null, to: null }}))
+ }
+ >
+ ${message}@
+ <${StackFrameFilename} frame=${{file: "#==#" + cell_id, line}} cell_id=${cell_id} />
+
`)
+ }
+
+
+
+ `;
+}
+
export const ErrorMessage = ({ msg, stacktrace, cell_id }) => {
let pluto_actions = useContext(PlutoActionsContext)
const default_rewriter = {
@@ -62,9 +102,9 @@ export const ErrorMessage = ({ msg, stacktrace, cell_id }) => {
{
- e.preventDefault()
- pluto_actions.split_remote_cell(cell_id, boundaries, true)
- }}
+ e.preventDefault()
+ pluto_actions.split_remote_cell(cell_id, boundaries, true)
+ }}
>Split this cell into ${boundaries.length} cells, or
`
@@ -137,6 +177,11 @@ export const ErrorMessage = ({ msg, stacktrace, cell_id }) => {
}
}),
},
+ {
+ pattern: /^syntax: (.*)$/,
+ display: default_rewriter.display,
+ show_stacktrace: () => false,
+ },
{
pattern: /^UndefVarError: (.*) not defined\.?$/,
display: (/** @type{string} */ x) => {
@@ -183,14 +228,14 @@ export const ErrorMessage = ({ msg, stacktrace, cell_id }) => {
: html`
${stacktrace.map(
- (frame) =>
- html`-
+ (frame) =>
+ html`
-
<${Funccall} frame=${frame} />
@
<${StackFrameFilename} frame=${frame} cell_id=${cell_id} />
${frame.inlined ? html`[inlined]` : null}
`
- )}
+ )}
`}
`
diff --git a/frontend/components/ExportBanner.js b/frontend/components/ExportBanner.js
index 0c1bac5383..2188763d9b 100644
--- a/frontend/components/ExportBanner.js
+++ b/frontend/components/ExportBanner.js
@@ -26,8 +26,23 @@ const Square = ({ fill }) => html`
`
-//@ts-ignore
-window.enable_secret_pluto_recording = true
+export const WarnForVisisblePasswords = () => {
+ if (
+ Array.from(document.querySelectorAll("bond")).some((bond_el) =>
+ Array.from(bond_el.querySelectorAll(`input[type="password"]`)).some((input) => {
+ // @ts-ignore
+ if (input?.value !== "") {
+ input.scrollIntoView()
+ return true
+ }
+ })
+ )
+ ) {
+ alert(
+ "Warning: this notebook includes a password input with something typed in it. The contents of this password field will be included in the exported file in an unsafe way. \n\nClear the password field and export again to avoid this problem."
+ )
+ }
+}
export const ExportBanner = ({ notebook_id, onClose, notebookfile_url, notebookexport_url, start_recording }) => {
// @ts-ignore
@@ -49,7 +64,16 @@ export const ExportBanner = ({ notebook_id, onClose, notebookfile_url, notebooke
<${Triangle} fill="#a270ba" /> Notebook file
Download a copy of the .jl script.
- exportNotebook(e, 1)}>
+ {
+ WarnForVisisblePasswords()
+ exportNotebook(e, 1)
+ }}
+ >
<${Square} fill="#E86F51" /> Static HTML
An .html file for your web page, or to share online.
@@ -57,26 +81,22 @@ export const ExportBanner = ({ notebook_id, onClose, notebookfile_url, notebooke
<${Square} fill="#619b3d" /> PDF
A static .pdf file for print or email.
- ${
- //@ts-ignore
- window.enable_secret_pluto_recording
- ? html`
- record
- {
- start_recording()
- onClose()
- e.preventDefault()
- }}
- class="export_card"
- >
- <${Circle} fill="#E86F51" /> Record (preview)
- Capture the entire notebook, and any changes you make.
-
- `
- : null
- }
+ ${html`
+ record
+ {
+ WarnForVisisblePasswords()
+ start_recording()
+ onClose()
+ e.preventDefault()
+ }}
+ class="export_card"
+ >
+ <${Circle} fill="#E86F51" /> Record (preview)
+ Capture the entire notebook, and any changes you make.
+
+ `}