diff --git a/frontend/components/PkgTerminalView.js b/frontend/components/PkgTerminalView.js
index 6c13c6538f..070806342d 100644
--- a/frontend/components/PkgTerminalView.js
+++ b/frontend/components/PkgTerminalView.js
@@ -11,9 +11,11 @@ const TerminalViewAnsiUp = ({ value }) => {
if (parent) parent.scrollTop = 1e5
}, [node_ref.current, value])
- return html``
+ return !!value
+ ? html``
+ : null
}
export const PkgTerminalView = TerminalViewAnsiUp
diff --git a/frontend/components/ProcessTab.js b/frontend/components/ProcessTab.js
index 03bee6491c..fad12c5f1b 100644
--- a/frontend/components/ProcessTab.js
+++ b/frontend/components/ProcessTab.js
@@ -3,6 +3,7 @@ import { html, useEffect, useRef, useState } from "../imports/Preact.js"
import { cl } from "../common/ClassTable.js"
import { prettytime, useMillisSinceTruthy } from "./RunArea.js"
import { DiscreteProgressBar } from "./DiscreteProgressBar.js"
+import { PkgTerminalView } from "./PkgTerminalView.js"
/**
* @param {{
@@ -13,7 +14,7 @@ import { DiscreteProgressBar } from "./DiscreteProgressBar.js"
export let ProcessTab = ({ notebook, my_clock_is_ahead_by }) => {
return html`
- <${StatusItem} status_tree=${notebook.status_tree} my_clock_is_ahead_by=${my_clock_is_ahead_by} path=${[]} />
+ <${StatusItem} status_tree=${notebook.status_tree} path=${[]} my_clock_is_ahead_by=${my_clock_is_ahead_by} nbpkg=${notebook.nbpkg} />
`
}
@@ -76,9 +77,10 @@ const to_ns = (x) => x * 1e9
* status_tree: import("./Editor.js").StatusEntryData?,
* path: string[],
* my_clock_is_ahead_by: number,
+ * nbpkg: import("./Editor.js").NotebookPkgData?,
* }} props
*/
-const StatusItem = ({ status_tree, path, my_clock_is_ahead_by }) => {
+const StatusItem = ({ status_tree, path, my_clock_is_ahead_by, nbpkg }) => {
if (status_tree == null) return null
const mystatus = path.reduce((entry, key) => entry.subtasks[key], status_tree)
if (!mystatus) return null
@@ -129,7 +131,13 @@ const StatusItem = ({ status_tree, path, my_clock_is_ahead_by }) => {
.map(([key, _subtask]) =>
blocklist.includes(key)
? null
- : html`<${StatusItem} key=${key} status_tree=${status_tree} my_clock_is_ahead_by=${my_clock_is_ahead_by} path=${[...path, key]} />`
+ : html`<${StatusItem}
+ key=${key}
+ status_tree=${status_tree}
+ my_clock_is_ahead_by=${my_clock_is_ahead_by}
+ path=${[...path, key]}
+ nbpkg=${nbpkg}
+ />`
)
const render_child_progress = () => {
@@ -179,7 +187,7 @@ const StatusItem = ({ status_tree, path, my_clock_is_ahead_by }) => {
${friendly_name(mystatus.name)}${inner_progress}
${finished ? prettytime(to_ns(end - start)) : busy ? prettytime(to_ns(busy_time)) : null}
- ${inner}
+ ${inner}${is_open && mystatus.name === "pkg" ? html`<${PkgTerminalView} value=${nbpkg?.terminal_outputs?.nbpkg_sync} />` : undefined}
`
}
diff --git a/frontend/editor.css b/frontend/editor.css
index d86fa0415d..7c4ee7da17 100644
--- a/frontend/editor.css
+++ b/frontend/editor.css
@@ -2040,6 +2040,12 @@ pluto-helpbox {
border-top-right-radius: 9px;
box-shadow: 0 0 11px 0px var(--helpbox-box-shadow-color);
}
+pluto-helpbox > section {
+ height: 100%;
+ overflow: auto;
+ padding: 10px;
+}
+
pluto-helpbox > header {
display: flex;
padding: 0.6em;
@@ -2273,54 +2279,49 @@ pluto-helpbox.hidden > section {
/* https://docs.julialang.org/en/v1/assets/themes/documenter-light.css */
/* see https://github.com/JuliaDocs/Documenter.jl for author information */
-pluto-helpbox {
+.helpbox-docs {
font-family: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
line-height: 1.5;
font-size: 0.9rem;
}
-pluto-helpbox pre,
-pluto-helpbox code,
-pluto-helpbox .cm-line {
+.helpbox-docs pre,
+.helpbox-docs code,
+.helpbox-docs .cm-line {
/* from https://docs.julialang.org/en/v1/assets/themes/documenter-light.css */
- font-family: "JuliaMono", "SFMono-Regular", "Menlo", "Consolas", "Liberation Mono", "DejaVu Sans Mono", monospace;
+ font-family: "Roboto Mono", "SFMono-Regular", "Menlo", "Consolas", "Liberation Mono", "DejaVu Sans Mono", monospace;
font-size: 0.95em;
line-height: initial;
}
-pluto-helpbox pre code {
+.helpbox-docs pre code {
font-size: 1em;
}
-pluto-helpbox pre code.hljs {
+.helpbox-docs pre code.hljs {
padding: 0;
}
-pluto-helpbox code .cm-editor .cm-content {
+.helpbox-docs code .cm-editor .cm-content {
padding: 0px;
}
-pluto-helpbox img {
+.helpbox-docs img {
max-width: 100%;
}
-pluto-helpbox > section {
- height: 100%;
- overflow: auto;
- padding: 10px;
-}
-pluto-helpbox > section h1,
-pluto-helpbox > section h2,
-pluto-helpbox > section h3,
-pluto-helpbox > section h4,
-pluto-helpbox > section h5,
-pluto-helpbox > section h6 {
+.helpbox-docs > section h1,
+.helpbox-docs > section h2,
+.helpbox-docs > section h3,
+.helpbox-docs > section h4,
+.helpbox-docs > section h5,
+.helpbox-docs > section h6 {
font-family: inherit;
border-bottom: none;
font-size: 1rem;
}
-pluto-helpbox > section h1 {
+.helpbox-docs > section h1 {
font-size: 1.3rem;
}
-pluto-helpbox > section pre {
+.helpbox-docs > section pre {
padding: 0.7rem 0.5rem;
-webkit-overflow-scrolling: touch;
overflow-x: auto;
@@ -2330,11 +2331,11 @@ pluto-helpbox > section pre {
white-space: pre;
word-wrap: normal;
}
-/* pluto-helpbox > section code {
+/* .helpbox-docs > section code {
background-color: whitesmoke;
padding: 0.1em;
} */
-pluto-helpbox > section hr {
+.helpbox-docs > section hr {
border: none;
border-top: 3px solid var(--rule-color);
}
@@ -2379,11 +2380,13 @@ pluto-helpbox > section hr {
pl-status {
--status-color: var(--process-undefined);
font-family: var(--system-ui-font-stack);
+ font-size: 0.9rem;
display: flex;
flex-direction: column;
border-radius: 0.2em;
/* margin: 0.3em; */
- margin-left: 0.7em;
+ --indent: 0.7rem;
+ margin-left: var(--indent);
border-left: 3px solid transparent;
margin-top: 0.4em;
overflow: hidden;
@@ -2522,6 +2525,10 @@ pl-status .status-time {
gap: 0.5px;
}
+pl-status pkg-terminal {
+ margin-left: var(--indent);
+}
+
/* FOOTER */
footer {
diff --git a/src/packages/Packages.jl b/src/packages/Packages.jl
index 866c072146..0672d6752d 100644
--- a/src/packages/Packages.jl
+++ b/src/packages/Packages.jl
@@ -101,31 +101,34 @@ function sync_nbpkg_core(
removed = setdiff(old_packages, new_packages)
added = setdiff(new_packages, old_packages)
+ can_skip = isempty(removed) && isempty(added) && notebook.nbpkg_ctx_instantiated
iolistener = let
busy_packages = notebook.nbpkg_ctx_instantiated ? added : new_packages
- IOListener(callback=(s -> on_terminal_output(busy_packages, s)))
+ report_to = ["nbpkg_sync", busy_packages...]
+ IOListener(callback=(s -> on_terminal_output(report_to, s)))
end
cleanup[] = () -> stoplistening(iolistener)
-
- # We remember which Pkg.Types.PreserveLevel was used. If it's too low, we will recommend/require a notebook restart later.
- local used_tier = Pkg.PRESERVE_ALL
-
- if !isready(pkg_token)
- println(iolistener.buffer, "Waiting for other notebooks to finish Pkg operations...")
- trigger(iolistener)
- end
- can_skip = isempty(removed) && isempty(added) && notebook.nbpkg_ctx_instantiated
+
Status.report_business_finished!(pkg_status, :analysis)
+ # We remember which Pkg.Types.PreserveLevel was used. If it's too low, we will recommend/require a notebook restart later.
+ local used_tier = Pkg.PRESERVE_ALL
if !can_skip
+ # We have a global lock, `pkg_token`, on Pluto-managed Pkg operations, which is shared between all notebooks. If this lock is not ready right now then that means that we are going to wait at the `withtoken(pkg_token)` line below.
+ # We want to report that we are waiting, with a best guess of why.
wait_business = if !isready(pkg_token)
+ reg = !PkgCompat._updated_registries_compat[]
+
+ # Print something in the terminal logs
+ println(iolistener.buffer, "Waiting for $(reg ? "the package registry to update" : "other notebooks to finish Pkg operations")...")
+ trigger(iolistener) # manual trigger because we did not start listening yet
+
+ # Create a business item
Status.report_business_started!(pkg_status,
- !PkgCompat._updated_registries_compat[] ?
- :registry_update :
- :waiting_for_others
+ reg ? :registry_update : :waiting_for_others
)
end
@@ -206,6 +209,8 @@ function sync_nbpkg_core(
Status.report_business_started!(pkg_status, :add)
start_time = time_ns()
with_io_setup(notebook, iolistener) do
+ println(iolistener.buffer, "\nAdding packages...")
+
# We temporarily clear the "semver-compatible" [deps] entries, because Pkg already respects semver, unless it doesn't, in which case we don't want to force it.
PkgCompat.clear_auto_compat_entries!(notebook.nbpkg_ctx)
@@ -380,8 +385,8 @@ end
function instantiate(notebook::Notebook, iolistener::IOListener)
start_time = time_ns()
- startlistening(iolistener)
with_io_setup(notebook, iolistener) do
+ println(iolistener.buffer, "\nInstantiating...")
@debug "PlutoPkg: Instantiating" notebook.path
# update registries if this is the first time
@@ -411,6 +416,7 @@ end
function resolve(notebook::Notebook, iolistener::IOListener)
startlistening(iolistener)
with_io_setup(notebook, iolistener) do
+ println(iolistener.buffer, "\nResolving...")
@debug "PlutoPkg: Instantiating" notebook.path
Pkg.resolve(notebook.nbpkg_ctx)
end
@@ -488,6 +494,7 @@ function update_nbpkg_core(
iolistener = let
# we don't know which packages will be updated, so we send terminal output to all installed packages
+ report_to = ["nbpkg_update", old_packages...]
IOListener(callback=(s -> on_terminal_output(old_packages, s)))
end
cleanup[] = () -> stoplistening(iolistener)
diff --git a/src/webserver/Status.jl b/src/webserver/Status.jl
index 0cc196aa9d..ec3ea910cd 100644
--- a/src/webserver/Status.jl
+++ b/src/webserver/Status.jl
@@ -6,6 +6,7 @@ Base.@kwdef mutable struct Business
started_at::Union{Nothing,Float64}=nothing
finished_at::Union{Nothing,Float64}=nothing
subtasks::Dict{Symbol,Business}=Dict{Symbol,Business}()
+
update_listener_ref::Ref{Union{Nothing,Function}}=Ref{Union{Nothing,Function}}(nothing)
lock::Threads.SpinLock=Threads.SpinLock()
end