Skip to content

Commit bd3db0c

Browse files
committed
Merge remote-tracking branch 'origin/master' into api-exec-process-info
2 parents 58c387a + 5477499 commit bd3db0c

File tree

17 files changed

+353
-97
lines changed

17 files changed

+353
-97
lines changed

src/packages/frontend/account/editor-settings/checkboxes.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
*/
55

66
import { Map } from "immutable";
7-
87
import { Checkbox } from "@cocalc/frontend/antd-bootstrap";
98
import { Component, Rendered } from "@cocalc/frontend/app-framework";
109
import { capitalize, is_different, keys } from "@cocalc/util/misc";
1110
import { JUPYTER_CLASSIC_MODERN } from "@cocalc/util/theme";
11+
import { A } from "@cocalc/frontend/components";
1212

1313
const EDITOR_SETTINGS_CHECKBOXES: { [setting: string]: string | Rendered } = {
1414
extra_button_bar:
@@ -32,10 +32,10 @@ const EDITOR_SETTINGS_CHECKBOXES: { [setting: string]: string | Rendered } = {
3232
ask_jupyter_kernel: "ask which kernel to use for a new Jupyter Notebook",
3333
jupyter_classic: (
3434
<span>
35-
use classical Jupyter notebook{" "}
36-
<a href={JUPYTER_CLASSIC_MODERN} target="_blank">
37-
(DANGER: this can cause trouble...)
38-
</a>
35+
<A href="https://github.com/sagemathinc/cocalc/issues/7706">
36+
<b>DEPRECATED -- will to be removed after Aug 2024</b>
37+
</A>{" "}
38+
(see <A href={JUPYTER_CLASSIC_MODERN}>the docs</A>).
3939
</span>
4040
),
4141
/* commented out since we are never using this.

src/packages/frontend/frame-editors/jupyter-editor/actions.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ export class JupyterEditorActions extends BaseActions<JupyterEditorState> {
372372

373373
// Either show the most recently focused introspect frame, or ceate one.
374374
public async show_introspect(): Promise<void> {
375-
this.show_recently_focused_frame_of_type("introspect", "row", false, 2 / 3);
375+
this.show_recently_focused_frame_of_type("introspect", "col", false, 2 / 3);
376376
}
377377

378378
// Close the most recently focused introspect frame, if there is one.
@@ -537,6 +537,24 @@ export class JupyterEditorActions extends BaseActions<JupyterEditorState> {
537537
compute_server() {
538538
// this is here just so the dropdown gets enabled
539539
}
540+
541+
gotoUser(account_id: string, frameId?: string) {
542+
const cursors = this.jupyter_actions.syncdb
543+
.get_cursors({ maxAge: 0 })
544+
?.toJS();
545+
if (cursors == null) return; // no info
546+
const locs = cursors[account_id]?.locs;
547+
for (const loc of locs) {
548+
if (loc.id != null) {
549+
const frameActions = this.get_frame_actions(frameId);
550+
if (frameActions != null) {
551+
frameActions.set_cur_id(loc.id);
552+
frameActions.scroll("cell visible");
553+
return;
554+
}
555+
}
556+
}
557+
}
540558
}
541559

542560
export { JupyterEditorActions as Actions };

src/packages/frontend/frame-editors/jupyter-editor/cell-notebook/actions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,9 @@ export class NotebookFrameActions {
828828
}
829829
}
830830
this.jupyter_actions._sync();
831+
setTimeout(() => {
832+
this.scroll("cell visible");
833+
}, 0);
831834
}
832835

833836
public toggle_source_hidden(): void {

src/packages/frontend/jupyter/commands.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ export interface KeyboardCommand {
3333
meta?: boolean;
3434
key?: string;
3535
// TODO: key is currently only used for displaying what the shortcut is; however,
36-
// "which" is deprecated and we should switch to using only key!
36+
// "which" is deprecated and we should switch to using only key.
37+
// However, key is also tricky, e.g., key for shift+h is an upper case "H", but
38+
// if you just hit h it is lower case "h", so you can't just switch to using event.key.
3739
// See https://github.com/sagemathinc/cocalc/issues/4020
3840
}
3941

src/packages/frontend/jupyter/keyboard.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ export function evt_to_obj(evt: any, mode: NotebookMode): KeyboardCommand {
5050
if (mode != null) {
5151
obj.mode = mode;
5252
}
53+
if (evt.which == 173) {
54+
// firefox sends 173 for the "-" key but everybody else sends 189
55+
// see https://github.com/sagemathinc/cocalc/issues/4467
56+
// See also https://stackoverflow.com/questions/18177818/why-jquerys-event-which-gives-different-results-in-firefox-and-chrome
57+
// and of course we should rewrite this entire file to use
58+
// evt.key instead of evt.which
59+
evt.which = 189;
60+
}
61+
if (evt.which == 59) {
62+
// firefox sends 59 for the "-" key but everybody else sends 186
63+
evt.which = 186;
64+
}
5365
return obj;
5466
}
5567

src/packages/frontend/jupyter/select-kernel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ export const KernelSelector: React.FC<KernelSelectorProps> = React.memo(
382382
"this compute server"
383383
) : (
384384
<>
385-
the <SiteName /> shared environment
385+
the <SiteName /> Home Base environment
386386
</>
387387
)}
388388
.

src/packages/frontend/purchases/subscriptions.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,11 @@ export default function Subscriptions() {
480480
dataIndex: "cost",
481481
key: "cost",
482482
render: (cost, record) => {
483-
if (record.status == "active") {
483+
// in prod we hit a case where cost was null, hence the if here.
484+
if (record.status == "active" && cost != null) {
484485
return `${currency(round2up(cost))}/${record.interval}`;
486+
} else {
487+
return "-";
485488
}
486489
},
487490
},

src/packages/jupyter/blobs/iframe.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ import { decode } from "he";
1313
//import { getLogger } from "@cocalc/backend/logger";
1414
//const logger = getLogger("jupyter:blobs:iframe");
1515

16+
// see https://github.com/sagemathinc/cocalc/issues/4322
17+
const MAX_HTML_SIZE = 10 ** 6;
18+
1619
// We use iframes to render html in a number of cases:
1720
// - if it starts with iframe
1821
// - if it has a whole page doctype
19-
// - if it has a <script> tag anywhere without a type -- since those are ignored by safe HTML
22+
// - if it has a <script> tag anywhere -- since those are ignored by safe HTML
2023
// rendering; using an iframe is the only way. This e.g., makes mpld3 work uses -- <script>! https://github.com/sagemathinc/cocalc/issues/1934
2124
// and altair -- https://github.com/sagemathinc/cocalc/issues/4468 -- uses <script type="text/javascript"/>
2225
// - do NOT just render all html in an iframe, e.g., this would break bokeh, since one output creates the target elt,
@@ -26,13 +29,28 @@ export function is_likely_iframe(content: string): boolean {
2629
return false;
2730
}
2831
content = content.toLowerCase();
32+
if (
33+
content.includes("https://bokeh.org") &&
34+
content.includes("bk-notebook-logo")
35+
) {
36+
// Do NOT use an iframe for bokeh no matter what, since this won't work properly.
37+
// Hopefully the above heuristic is sufficiently robust to detect but not overdetect.
38+
return false;
39+
}
40+
if (content.length >= MAX_HTML_SIZE) {
41+
// it'll just break anyways if we don't use an iframe -- if we do, there is hope.
42+
return true;
43+
}
2944
return (
3045
content.includes("bk-notebook-logo") ||
3146
content.startsWith("<iframe") ||
3247
content.includes("<!doctype html>") ||
3348
(content.includes("<html>") && content.includes("<head>")) ||
34-
content.includes("<script>") ||
35-
content.includes('<script type="text/javascript">')
49+
// this gets really serious -- we sanitize out script tags in non-iframe html,
50+
// and a LOT of interesting jupyter outputs are self contained html + script tags... so
51+
// by rendering them all in iframes (1) they suddenly all work, which is great, and
52+
// (2) if they are large (which is common) they work even better, by far!
53+
content.includes("<script")
3654
);
3755
}
3856

src/packages/jupyter/ipynb/export-to-ipynb.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ function cell_to_ipynb(id: string, opts: any) {
5050
cell_type: (left = cell.get("cell_type")) != null ? left : "code",
5151
source: diff_friendly((left1 = cell.get("input")) != null ? left1 : ""),
5252
metadata,
53+
id,
5354
};
5455

5556
// Handle any extra metadata (mostly user defined) that we don't handle in a special
@@ -87,7 +88,7 @@ function cell_to_ipynb(id: string, opts: any) {
8788
output,
8889
exec_count,
8990
opts.more_output != null ? opts.more_output[id] : undefined,
90-
opts.blob_store
91+
opts.blob_store,
9192
);
9293
} else if (obj.outputs == null && obj.cell_type === "code") {
9394
obj.outputs = []; // annoying requirement of ipynb file format.
@@ -130,7 +131,7 @@ function process_other_metadata(obj: any, other_metadata: any) {
130131
function process_attachments(
131132
obj: any,
132133
attachments: any,
133-
blob_store: BlobStore | undefined
134+
blob_store: BlobStore | undefined,
134135
) {
135136
if (attachments == null || blob_store == null) {
136137
// don't have to or can't do anything (https://github.com/sagemathinc/cocalc/issues/4272)
@@ -155,7 +156,7 @@ function ipynb_outputs(
155156
output: any,
156157
exec_count: any,
157158
more_output: any,
158-
blob_store: BlobStore | undefined
159+
blob_store: BlobStore | undefined,
159160
) {
160161
// If the last message has the more_output field, then there may be
161162
// more output messages stored, which are not in the cells object.
@@ -170,7 +171,7 @@ function ipynb_outputs(
170171
immutable.fromJS({
171172
text: "WARNING: Some output was deleted.\n",
172173
name: "stderr",
173-
})
174+
}),
174175
);
175176
} else {
176177
// Indeed, the last message has the more_output field.
@@ -204,7 +205,7 @@ function ipynb_outputs(
204205
function process_output_n(
205206
output_n: any,
206207
exec_count: any,
207-
blob_store: BlobStore | undefined
208+
blob_store: BlobStore | undefined,
208209
) {
209210
if (output_n == null) {
210211
return;

src/packages/jupyter/ipynb/import-from-ipynb.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ export class IPynbImporter {
162162
this._ipynb.metadata &&
163163
this._ipynb.metadata.kernelspec &&
164164
this._ipynb.metadata.kernelspec.name;
165+
if (this._kernel != null) {
166+
// kernel names are supposed to be case insensitive
167+
// https://jupyter-client.readthedocs.io/en/latest/kernels.html
168+
// We also make them all lower case when reading them in at
169+
// src/packages/jupyter/kernel/kernel-data.ts
170+
this._kernel = this._kernel.toLowerCase();
171+
}
165172
};
166173

167174
_import_metadata = () => {
@@ -255,7 +262,11 @@ export class IPynbImporter {
255262
);
256263
};
257264

258-
_get_new_id = () => {
265+
_get_new_id = (cell) => {
266+
if (cell?.id && this._id_is_available(cell.id)) {
267+
// attempt to use id in the ipynb file
268+
return cell.id;
269+
}
259270
if (this._new_id != null) {
260271
return this._new_id(this._id_is_available);
261272
} else {
@@ -333,7 +344,7 @@ export class IPynbImporter {
333344
? this._existing_ids != null
334345
? this._existing_ids[n]
335346
: undefined
336-
: this._get_new_id();
347+
: this._get_new_id(cell);
337348
const obj: any = {
338349
type: "cell",
339350
id,
@@ -344,12 +355,12 @@ export class IPynbImporter {
344355
cell.metadata != null && cell.metadata.cocalc != null
345356
? cell.metadata.cocalc.outputs
346357
: undefined,
347-
id
358+
id,
348359
),
349360
cell_type: this._get_cell_type(cell.cell_type),
350361
exec_count: this._get_exec_count(
351362
cell.execution_count,
352-
cell.prompt_number
363+
cell.prompt_number,
353364
),
354365
};
355366

0 commit comments

Comments
 (0)