Skip to content

Commit

Permalink
Feature/sharing entrypoint change (#1061)
Browse files Browse the repository at this point in the history
* Implement per-file dropdown and add delete button and set-as-entrypoint button

* Implement StliteKernel.reboot() and use it from Sharing

* Allow to rename the entrypoint file

* Show the entrypoint marker and improve the file tab style

* Add a tooltip to the entrypoint marker

* Add comment

* Fix comment

* Fix comment

* Fix

* Fix

* Fix

* Fix styling

* Fix

* Better styling and markup

* Fix

* Fix

* Fix markup

* Fix
  • Loading branch information
whitphx committed Aug 10, 2024
1 parent e304167 commit 10af884
Show file tree
Hide file tree
Showing 14 changed files with 375 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,12 @@ export class StliteKernelWithToast {
error: "Failed to install",
});
}

public reboot(...args: Parameters<StliteKernel["reboot"]>) {
return stliteStyledPromiseToast<void>(this.kernel.reboot(...args), {
pending: "Rebooting",
success: "Successfully rebooted",
error: "Failed to reboot",
});
}
}
11 changes: 11 additions & 0 deletions packages/kernel/py/stlite-lib/stlite_lib/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Callable, Final, cast

import pyodide.ffi
from streamlit import source_util
from streamlit.proto.BackMsg_pb2 import BackMsg
from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
from streamlit.runtime import Runtime, RuntimeConfig, SessionClient
Expand Down Expand Up @@ -210,7 +211,17 @@ def callback(future: asyncio.Future):

def stop(self):
self._websocket_handler.on_close()

# `Runtime.stop()` doesn't stop the running tasks immediately,
# but we don't need to wait for them to finish for the current use case,
# e.g. booting up a new server and replacing the old one.
self._runtime.stop()
Runtime._instance = None

# `source_util.get_pages()`, which is used from `PagesStrategyV1.get_initial_active_script`
# to resolve the pages info, caches the pages in the module-level variable `source_util._cached_pages`.
# We need to invalidate this cache to avoid using the old pages info when booting up a new server.
source_util.invalidate_pages_cache()


class WebSocketHandler(SessionClient):
Expand Down
14 changes: 14 additions & 0 deletions packages/kernel/src/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,20 @@ export class StliteKernel {
});
}

/**
* Reboot the Streamlit server.
* Note that we also need to refresh (rerender) the frontend app after calling this method
* to reflect the changes on the user-facing side.
*/
public reboot(entrypoint: string): Promise<void> {
return this._asyncPostMessage({
type: "reboot",
data: {
entrypoint,
},
});
}

private _asyncPostMessage(
message: InMessage,
): Promise<ReplyMessageGeneralReply["data"]>;
Expand Down
7 changes: 7 additions & 0 deletions packages/kernel/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ export interface InMessageInitData extends InMessageBase {
type: "initData";
data: WorkerInitialData;
}
export interface InMessageReboot extends InMessageBase {
type: "reboot";
data: {
entrypoint: string;
};
}
export interface InMessageWebSocketConnect extends InMessageBase {
type: "websocket:connect";
data: {
Expand Down Expand Up @@ -117,6 +123,7 @@ export interface InMessageInstall extends InMessageBase {
}
export type InMessage =
| InMessageInitData
| InMessageReboot
| InMessageWebSocketConnect
| InMessageWebSocketSend
| InMessageHttpRequest
Expand Down
35 changes: 25 additions & 10 deletions packages/kernel/src/worker-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,8 @@ AppSession._on_scriptrunner_event = wrap_app_session_on_scriptrunner_event(AppSe
}

postProgressMessage("Booting up the Streamlit server.");
console.debug("Booting up the Streamlit server");
// The following Python code is based on streamlit.web.cli.main_run().
console.debug("Setting up the Streamlit configuration");
self.__streamlitFlagOptions__ = {
// gatherUsageStats is disabled as default, but can be enabled explicitly by setting it to true.
"browser.gatherUsageStats": false,
Expand All @@ -380,7 +380,6 @@ AppSession._on_scriptrunner_event = wrap_app_session_on_scriptrunner_event(AppSe
};
await pyodide.runPythonAsync(`
from stlite_lib.bootstrap import load_config_options, prepare
from stlite_lib.server import Server
from js import __streamlitFlagOptions__
flag_options = __streamlitFlagOptions__.to_py()
Expand All @@ -390,16 +389,14 @@ main_script_path = "${entrypoint}"
args = []
prepare(main_script_path, args)
server = Server(main_script_path)
server.start()
`);
console.debug("Booted up the Streamlit server");
console.debug("Set up the Streamlit configuration");

console.debug("Setting up the HTTP server");
// Pull the http server instance from Python world to JS world and set up it.
httpServer = pyodide.globals.get("server").copy();
console.debug("Set up the HTTP server");
console.debug("Booting up the Streamlit server");
const Server = pyodide.pyimport("stlite_lib.server.Server");
httpServer = Server(entrypoint);
httpServer.start();
console.debug("Booted up the Streamlit server");

postMessage({
type: "event:loaded",
Expand Down Expand Up @@ -438,6 +435,24 @@ server.start()

try {
switch (msg.type) {
case "reboot": {
console.debug("Reboot the Streamlit server", msg.data);

const { entrypoint } = msg.data;

httpServer.stop();

console.debug("Booting up the Streamlit server");
const Server = pyodide.pyimport("stlite_lib.server.Server");
httpServer = Server(entrypoint);
httpServer.start();
console.debug("Booted up the Streamlit server");

messagePort.postMessage({
type: "reply",
});
break;
}
case "websocket:connect": {
console.debug("websocket:connect", msg.data);

Expand Down
7 changes: 7 additions & 0 deletions packages/sharing-common/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ export interface ForwardMessageBase {
type: string;
data?: unknown;
}
export interface RebootMessage extends ForwardMessageBase {
type: "reboot";
data: {
entrypoint: string;
};
}
export interface FileWriteMessage extends ForwardMessageBase {
type: "file:write";
data: {
Expand Down Expand Up @@ -34,6 +40,7 @@ export interface InstallMessage extends ForwardMessageBase {
};
}
export type ForwardMessage =
| RebootMessage
| FileWriteMessage
| FileRenameMessage
| FileUnlinkMessage
Expand Down
20 changes: 20 additions & 0 deletions packages/sharing-editor/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,25 @@ function App() {
[updateAppData],
);

const handleEntrypointChange = useCallback<EditorProps["onEntrypointChange"]>(
(entrypoint) => {
iframeRef.current?.postMessage({
type: "reboot",
data: {
entrypoint,
},
});

updateAppData((cur) => {
return {
...cur,
entrypoint,
};
});
},
[updateAppData],
);

const handleIframeMessage = useCallback<
StliteSharingIFrameProps["onMessage"]
>(
Expand Down Expand Up @@ -272,6 +291,7 @@ function App() {
onFileRename={handleFileRename}
onFileDelete={handleFileDelete}
onRequirementsChange={handleRequirementsChange}
onEntrypointChange={handleEntrypointChange}
/>
}
right={
Expand Down
162 changes: 103 additions & 59 deletions packages/sharing-editor/src/Editor/components/Tab.module.scss
Original file line number Diff line number Diff line change
@@ -1,90 +1,134 @@
@use "variables" as var;
@use "mixins";

.tabFrame {
.tab {
position: relative;
display: inline-flex;
align-items: center;
box-sizing: border-box;
margin-bottom: 4px;
font-size: 0.8rem;
background: rgba(0, 0, 0, 0.05);
border: rgba(0, 0, 0, 0.1) 1px solid;
border: rgba(0, 0, 0, 0.1) var.$border-width solid;
height: var.$tab-height;
line-height: normal;

&:hover {
background: initial;
}

&:has([role="tab"][aria-selected=true]) {
background: initial;
border-bottom: rgba(255,255,255,0) var.$border-width solid;

&::before {
content: '';
position: absolute;
top: - var.$border-width;
left: 0;
width: 100%;
height: var.$tab-highlight-height;
background-color: var(--c-primary);
}
}
}

.tabFrame:hover {
background: initial;
}

.tabFrameSelected {
background: initial;
border-top: var(--c-primary) var.$tab-highlight-height solid;
margin-top: -(var.$tab-highlight-height);
border-bottom: none;
position: relative;
}

$tabPaddingLeft: 0.5rem;
$deleteButtonSpaceWidth: 1.2rem;

.tabButton {
@include mixins.reset-button;

display: inline-flex;
align-items: center;
width: 100%;
height: 100%;
padding-left: $tabPaddingLeft;
padding-right: $deleteButtonSpaceWidth;
padding: 0 0.5rem;
}

.editableTabBody {
display: inline-block;
position: relative;

.fileNameForm {
position: absolute;
width: 100%;
left: 0;
top: 0;
}

.fileNameInput {
@include mixins.reset-input;

display: inline-block;
width: 100%;
}
.fileNameInputError {
border: red 1px solid;
}
}

.deleteButtonContainer {
position: absolute;
right: 0;
top: 0;
bottom: 0;
.entrypointIndicator {
display: flex;
align-items: center;
padding-top: var.$tab-highlight-height;
pointer-events: none;
justify-content: center;
margin-right: 0.3rem;
position: relative;

.tooltip {
display: none;

position: absolute;
top: 0;
transform: translate(0, -50%);
left: 100%;
z-index: 1;
font-size: 0.7rem;
text-align: center;
border-radius: 6px;
padding: 0.3rem 0.5rem;
background-color: rgba(0,0,0,0.6);
color: #fff;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
pointer-events: none;
}

&:hover .tooltip {
display: block;
}
}

.deleteButton {
.dropdownButton {
@include mixins.reset-button;

font-size: 0.6rem;

padding: 0.2rem;

pointer-events: initial;
}
.deleteButton:not([disabled]):hover {
color: var(--c-primary);
}

.selectedTab {
display: inline-block;
padding-left: $tabPaddingLeft;
padding-right: $deleteButtonSpaceWidth;
}
display: flex;
width: 1rem;
height: 100%;
align-items: center;
justify-content: center;
margin-left: -0.5rem;

.selectedTabInner {
position: relative;
}
font-size: 0.6rem;
cursor: pointer;

.fileNameForm {
display: inline-block;
position: absolute;
width: 100%;
left: 0;
top: 0;
&:hover {
color: var(--c-primary);
}
}

.fileNameInput {
@include mixins.reset-input;

display: inline-block;
width: 100%;
}
.fileNameInputError {
border: red 1px solid;
.dropdownContent {
display: flex;
flex-direction: column;
background-color: var(--c-background);
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);

button {
background: none;
border: none;
padding: 8px 16px;
text-align: left;
cursor: pointer;
width: 100%;

&:hover {
background-color: var(--c-background-hover);
}
}
}
Loading

0 comments on commit 10af884

Please sign in to comment.