Skip to content

Commit

Permalink
🩺 Process status (#2399)
Browse files Browse the repository at this point in the history
  • Loading branch information
fonsp authored Dec 13, 2022
1 parent d57b223 commit febc398
Show file tree
Hide file tree
Showing 22 changed files with 1,050 additions and 190 deletions.
47 changes: 47 additions & 0 deletions frontend/common/clock sync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useContext, useState, useEffect } from "../imports/Preact.js"
import { PlutoActionsContext } from "./PlutoContext.js"

/** Request the current time from the server, compare with the local time, and return the current best estimate of our time difference. Updates regularly.
* @param {{connected: boolean}} props
*/
export const useMyClockIsAheadBy = ({ connected }) => {
let pluto_actions = useContext(PlutoActionsContext)

const [my_clock_is_ahead_by, set_my_clock_is_ahead_by] = useState(0)

useEffect(() => {
console.error({ connected })
if (connected) {
let f = async () => {
let getserver = () => pluto_actions.send("current_time").then((m) => m.message.time)
let getlocal = () => Date.now() / 1000

// once to precompile and to make sure that the server is not busy with other tasks
// console.log("getting server time warmup")
for (let i = 0; i < 16; i++) await getserver()
// console.log("getting server time warmup done")

let t1 = await getlocal()
let s1 = await getserver()
let s2 = await getserver()
let t2 = await getlocal()
// console.log("getting server time done")

let mytime = (t1 + t2) / 2
let servertime = (s1 + s2) / 2

let diff = mytime - servertime
// console.info("My clock is ahead by ", diff, " s")
if (!isNaN(diff)) set_my_clock_is_ahead_by(diff)
}

f()

let handle = setInterval(f, 60 * 1000)

return () => clearInterval(handle)
}
}, [connected])

return my_clock_is_ahead_by
}
95 changes: 75 additions & 20 deletions frontend/components/BottomRightPanel.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { html, useState, useRef, useEffect } from "../imports/Preact.js"
import { html, useState, useRef, useEffect, useMemo } from "../imports/Preact.js"
import { cl } from "../common/ClassTable.js"

import { LiveDocsTab } from "./LiveDocsTab.js"
import { is_finished, ProcessTab, total_done, total_tasks } from "./ProcessTab.js"
import { useMyClockIsAheadBy } from "../common/clock sync.js"

export const ENABLE_PROCESS_TAB = window.localStorage.getItem("ENABLE_PROCESS_TAB") === "true"

Expand All @@ -20,27 +22,63 @@ window.PLUTO_TOGGLE_PROCESS_TAB = () => {
window.location.reload()
}

export let BottomRightPanel = ({ desired_doc_query, on_update_doc_query, notebook }) => {
/**
* @typedef PanelTabName
* @type {"docs" | "process" | null}
*/

export const open_bottom_right_panel = (/** @type {PanelTabName} */ tab) => window.dispatchEvent(new CustomEvent("open_bottom_right_panel", { detail: tab }))

/**
* @param {{
* notebook: import("./Editor.js").NotebookData,
* desired_doc_query: string?,
* on_update_doc_query: (query: string?) => void,
* connected: boolean,
* }} props
*/
export let BottomRightPanel = ({ desired_doc_query, on_update_doc_query, notebook, connected }) => {
let container_ref = useRef()

const focus_docs_on_open_ref = useRef(false)
const [open_tab, set_open_tab] = useState(/** @type { "docs" | "process" | null} */ (null))
const [open_tab, set_open_tab] = useState(/** @type { PanelTabName} */ (null))
const hidden = open_tab == null

// Open docs when "open_live_docs" event is triggered
// Open panel when "open_bottom_right_panel" event is triggered
useEffect(() => {
let handler = () => {
let handler = (/** @type {CustomEvent} */ e) => {
console.log(e.detail)
// https://github.com/fonsp/Pluto.jl/issues/321
focus_docs_on_open_ref.current = false
set_open_tab("docs")
set_open_tab(e.detail)
if (window.getComputedStyle(container_ref.current).display === "none") {
alert("This browser window is too small to show docs.\n\nMake the window bigger, or try zooming out.")
}
}
window.addEventListener("open_live_docs", handler)
return () => window.removeEventListener("open_live_docs", handler)
window.addEventListener("open_bottom_right_panel", handler)
return () => window.removeEventListener("open_bottom_right_panel", handler)
}, [])

const [status_total, status_done] = useMemo(
() =>
!ENABLE_PROCESS_TAB || notebook.status_tree == null
? [0, 0]
: [
// total_tasks minus 1, to exclude the notebook task itself
total_tasks(notebook.status_tree) - 1,
// the notebook task should never be done, but lets be sure and subtract 1 if it is:
total_done(notebook.status_tree) - (is_finished(notebook.status_tree) ? 1 : 0),
],
[notebook.status_tree]
)

const busy = status_done < status_total

const show_business_outline = useDelayedTruth(busy, 700)
const show_business_counter = useDelayedTruth(busy, 3000)

const my_clock_is_ahead_by = useMyClockIsAheadBy({ connected })

return html`
<aside id="helpbox-wrapper" ref=${container_ref}>
<pluto-helpbox class=${cl({ hidden, [`helpbox-${open_tab ?? hidden}`]: true })}>
Expand All @@ -58,7 +96,8 @@ export let BottomRightPanel = ({ desired_doc_query, on_update_doc_query, noteboo
// TODO: focus the docs input
}}
>
<span>Live Docs</span>
<span class="tabicon"></span>
<span class="tabname">Live Docs</span>
</button>
<button
disabled=${!ENABLE_PROCESS_TAB}
Expand All @@ -67,12 +106,20 @@ export let BottomRightPanel = ({ desired_doc_query, on_update_doc_query, noteboo
"helpbox-tab-key": true,
"helpbox-process": true,
"active": open_tab === "process",
"busy": ENABLE_PROCESS_TAB && show_business_outline,
})}
onClick=${() => {
set_open_tab(open_tab === "process" ? null : "process")
}}
>
<span>${ENABLE_PROCESS_TAB ? "Status" : "Coming soon"}</span>
<span class="tabicon"></span>
<span class="tabname"
>${ENABLE_PROCESS_TAB
? open_tab === "process" || !show_business_counter
? "Status"
: html`Status${" "}<span class="subprogress-counter">(${status_done}/${status_total})</span>`
: "Coming soon"}</span
>
</button>
${hidden
? null
Expand All @@ -93,18 +140,26 @@ export let BottomRightPanel = ({ desired_doc_query, on_update_doc_query, noteboo
notebook=${notebook}
/>`
: open_tab === "process"
? html`<section>
<p>Congratulations, you found the secret!</p>
<p>
Have you considered becoming a Pluto.jl open source contributor? We are always looking for creative people with JavaScript
experience! Take a look at our${" "}
<a href="https://github.com/fonsp/Pluto.jl/issues?q=is%3Aopen+label%3A%22good+first+issue%22+sort%3Aupdated-desc"
>good first issues</a
>, and our ${" "}<a href="https://juliapluto.github.io/weekly-call-notes/">weekly community call</a>.
</p>
</section>`
? html`<${ProcessTab} notebook=${notebook} my_clock_is_ahead_by=${my_clock_is_ahead_by} />`
: null}
</pluto-helpbox>
</aside>
`
}

const useDelayedTruth = (/** @type {boolean} */ x, /** @type {number} */ timeout) => {
const [output, set_output] = useState(false)

useEffect(() => {
if (x) {
let handle = setTimeout(() => {
set_output(true)
}, timeout)
return () => clearTimeout(handle)
} else {
set_output(false)
}
}, [x])

return output
}
3 changes: 2 additions & 1 deletion frontend/components/CellInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import { HighlightLineFacet, highlightLinePlugin } from "./CellInput/highlight_l
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"
import { open_bottom_right_panel } from "./BottomRightPanel.js"

export const ENABLE_CM_MIXED_PARSER = window.localStorage.getItem("ENABLE_CM_MIXED_PARSER") === "true"

Expand Down Expand Up @@ -647,7 +648,7 @@ export const CellInput = ({
EditorView.updateListener.of((update) => {
if (!update.docChanged) return
if (update.state.doc.length > 0 && update.state.sliceDoc(0, 1) === "?") {
window.dispatchEvent(new CustomEvent("open_live_docs"))
open_bottom_right_panel("docs")
}
}),
EditorState.tabSize.of(4),
Expand Down
3 changes: 2 additions & 1 deletion frontend/components/CellInput/pluto_autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { get_selected_doc_from_state } from "./LiveDocsFromCursor.js"
import { cl } from "../../common/ClassTable.js"
import { ScopeStateField } from "./scopestate_statefield.js"
import { open_bottom_right_panel } from "../BottomRightPanel.js"

let { autocompletion, completionKeymap, completionStatus, acceptCompletion } = autocomplete

Expand Down Expand Up @@ -90,7 +91,7 @@ const tab_completion_command = (cm) => {
let open_docs_if_autocomplete_is_open_command = (cm) => {
let autocompletion_open = cm.state.field(completionState, false)?.open ?? false
if (autocompletion_open) {
window.dispatchEvent(new CustomEvent("open_live_docs"))
open_bottom_right_panel("docs")
return true
}
return false
Expand Down
55 changes: 55 additions & 0 deletions frontend/components/DiscreteProgressBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { cl } from "../common/ClassTable.js"
import { html, useEffect, useRef, useState } from "../imports/Preact.js"

export const DiscreteProgressBar = ({ total, done, busy }) => {
total = Math.max(1, total)

return html`
<div
class=${cl({
"discrete-progress-bar": true,
"small": total < 8,
"mid": total >= 8 && total < 48,
"big": total >= 48,
})}
data-total=${total}
>
${[...Array(total)].map((_, i) => {
return html`<div
class=${cl({
done: i < done,
busy: i >= done && i < done + busy,
})}
></div>`
})}
</div>
`
}

export const DiscreteProgressBarTest = () => {
const [done_total, set_done_total] = useState([0, 0, 0])

const done_total_ref = useRef(done_total)
done_total_ref.current = done_total

useEffect(() => {
let handle = setInterval(() => {
const [done, busy, total] = done_total_ref.current

if (Math.random() < 0.3) {
if (done < total) {
if (Math.random() < 0.1) {
set_done_total([done, 1, total + 5])
} else {
set_done_total([done + 1, 1, total])
}
} else {
set_done_total([0, 1, Math.ceil(Math.random() * Math.random() * 100)])
}
}
}, 100)
return () => clearInterval(handle)
}, [])

return html`<${DiscreteProgressBar} total=${done_total[2]} busy=${done_total[1]} done=${done_total[0]} />`
}
12 changes: 12 additions & 0 deletions frontend/components/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ const first_true_key = (obj) => {
* }}
*/

/**
* @typedef StatusEntryData
* @type {{
* name: string,
* started_at: number?,
* finished_at: number?,
* subtasks: Record<string,StatusEntryData>,
* }}
*/

/**
* @typedef CellResultData
* @type {{
Expand Down Expand Up @@ -233,6 +243,7 @@ const first_true_key = (obj) => {
* bonds: BondValuesDict,
* nbpkg: NotebookPkgData?,
* metadata: object,
* status_tree: StatusEntryData?,
* }}
*/

Expand Down Expand Up @@ -1533,6 +1544,7 @@ patch: ${JSON.stringify(
<${BottomRightPanel}
desired_doc_query=${this.state.desired_doc_query}
on_update_doc_query=${this.actions.set_doc_query}
connected=${this.state.connected}
notebook=${this.state.notebook}
/>
<${Popup}
Expand Down
Loading

0 comments on commit febc398

Please sign in to comment.