diff --git a/src/packages/frontend/i18n/common.ts b/src/packages/frontend/i18n/common.ts
index ee3d5a838f..9aff2417b0 100644
--- a/src/packages/frontend/i18n/common.ts
+++ b/src/packages/frontend/i18n/common.ts
@@ -306,7 +306,7 @@ export const labels = defineMessages({
},
latex_document: {
id: "labels.latex_document",
- defaultMessage: "LaTeX Document",
+ defaultMessage: "LaTeX",
description:
"Indicating a LaTeX Documents on a button label or frame title",
},
@@ -578,6 +578,10 @@ export const labels = defineMessages({
id: "labels.color",
defaultMessage: "Color",
},
+ create: {
+ id: "labels.create",
+ defaultMessage: "Create",
+ },
});
export const menu = defineMessages({
diff --git a/src/packages/frontend/project/new/add-ai-gen-btn.tsx b/src/packages/frontend/project/new/add-ai-gen-btn.tsx
new file mode 100644
index 0000000000..e2fec495dd
--- /dev/null
+++ b/src/packages/frontend/project/new/add-ai-gen-btn.tsx
@@ -0,0 +1,55 @@
+/*
+ * This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
+ * License: MS-RSL – see LICENSE.md for details
+ */
+
+import { Col, Flex, Space } from "antd";
+
+import { AIGenerateDocumentButton } from "@cocalc/frontend/project/page/home-page/ai-generate-document";
+import { Ext } from "@cocalc/frontend/project/page/home-page/ai-generate-examples";
+
+interface Props {
+ btn: JSX.Element;
+ grid: [number | { flex: string }, number | { flex: string }];
+ filename: string | undefined;
+ filenameChanged?: boolean;
+ mode: "full" | "flyout";
+ ext: Ext;
+}
+
+export function AiDocGenerateBtn({ btn, grid, ext, filename, mode }: Props) {
+ const isFlyout = mode === "flyout";
+ const [sm, md] = grid;
+
+ if (isFlyout) {
+ return (
+
+
+ {btn}
+
+
+
+
+
+ );
+ } else {
+ return (
+ //
+
+ {btn}
+
+
+
+
+ );
+ }
+}
diff --git a/src/packages/frontend/project/new/file-type-selector.tsx b/src/packages/frontend/project/new/file-type-selector.tsx
index c06a015d98..a156130f4c 100644
--- a/src/packages/frontend/project/new/file-type-selector.tsx
+++ b/src/packages/frontend/project/new/file-type-selector.tsx
@@ -3,22 +3,26 @@
* License: MS-RSL – see LICENSE.md for details
*/
-import { Col, Flex, Modal, Row, Tag } from "antd";
+import { Col, Modal, Row, Tag } from "antd";
import { Gutter } from "antd/es/grid/row";
+import { throttle } from "lodash";
import type { ReactNode } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { Available } from "@cocalc/comm/project-configuration";
-import { CSS } from "@cocalc/frontend/app-framework";
+import {
+ CSS,
+ useEffect,
+ useRef,
+ useState,
+} from "@cocalc/frontend/app-framework";
import { A } from "@cocalc/frontend/components/A";
import { Icon } from "@cocalc/frontend/components/icon";
import { Tip } from "@cocalc/frontend/components/tip";
import { computeServersEnabled } from "@cocalc/frontend/compute/config";
import { labels } from "@cocalc/frontend/i18n";
-import { useProjectContext } from "@cocalc/frontend/project/context";
-import { Ext } from "@cocalc/frontend/project/page/home-page/ai-generate-examples";
import { ProjectActions } from "@cocalc/frontend/project_actions";
-import { AIGenerateDocumentButton } from "../page/home-page/ai-generate-document";
+import { AiDocGenerateBtn } from "./add-ai-gen-btn";
import { DELAY_SHOW_MS, NEW_FILETYPE_ICONS } from "./consts";
import { JupyterNotebookButtons } from "./jupyter-buttons";
import { NewFileButton } from "./new-file-button";
@@ -50,57 +54,112 @@ interface Props {
// Could be changed to auto adjust to a list of pre-defined button names.
export function FileTypeSelector({
create_file,
- create_folder,
projectActions,
availableFeatures,
disabledFeatures,
mode = "full",
selectedExt,
- children,
filename,
makeNewFilename,
filenameChanged,
}: Props) {
- const { project_id } = useProjectContext();
const intl = useIntl();
+ const mainDivRef = useRef(null);
+ const [width, setWidth] = useState(0);
+ const btnWidth = Math.max(100, (width - 50) / 5);
+
+ useEffect(() => {
+ const main = mainDivRef.current;
+ if (main == null) return;
+ const resizeObserver = new ResizeObserver(
+ throttle(
+ (entries) => {
+ if (entries.length > 0) {
+ setWidth(entries[0].contentRect.width);
+ }
+ },
+ 10,
+ { leading: false, trailing: true },
+ ),
+ );
+ resizeObserver.observe(main);
+ return () => {
+ resizeObserver.disconnect();
+ };
+ }, []);
+
if (!create_file) {
return null;
}
const isFlyout = mode === "flyout";
const btnSize = isFlyout ? "small" : "large";
-
- // col width of Antd's 24 grid system
- const base = 6;
- const md = isFlyout ? 24 : base;
- const sm = isFlyout ? 24 : 2 * base;
- const doubleMd = isFlyout ? 24 : 2 * base;
- const doubleSm = isFlyout ? 24 : 4 * base;
+ const tipStyle: CSS = isFlyout ? { flex: "1 1 auto" } : { width: "100%" }; //{ flex: "1 1 auto" };
+
+ // Usually, there are supposed to be 5 columns, but it changes if the layout is tighter to 3
+ const base = (n = 1) => {
+ // return { flex: `${n * 20}%` };
+ return 5 * n;
+ };
+ const md = isFlyout ? 24 : base(1);
+ const sm = isFlyout ? 24 : base(2);
+ const doubleMd = isFlyout ? 24 : base(2);
+ const doubleSm = isFlyout ? 24 : base(4);
const y: Gutter = isFlyout ? 15 : 30;
const gutter: [Gutter, Gutter] = [20, y / 2];
- const newRowStyle = { marginTop: `${y}px` };
+ const newRowStyle: CSS = { marginTop: `${y}px` } as const;
function btnActive(ext: string): boolean {
if (!isFlyout) return false;
return ext === selectedExt;
}
- function renderJupyterNotebook() {
- if (
- !availableFeatures.jupyter_notebook &&
- !availableFeatures.sage &&
- !availableFeatures.latex
- ) {
- return;
- }
+ function wrapBtn(btn: ReactNode) {
+ return (
+
+ {btn}
+
+ );
+ //return {btn};
+ // return (
+ //
+ // {btn}
+ //
+ // );
+ }
+
+ function makeRow(btns: ReactNode) {
+ return (
+
+ {btns}
+
+ );
+ // return (
+ //
+ // {btns}
+ //
+ // //
+ // // {btns}
+ // //
+ // );
+ }
+ function renderPopular() {
return (
<>
- Popular Documents
+ Popular
-
+ {makeRow(
makeNewFilename?.("ipynb")}
after={
/* Those come after the main button, then the additional jupyter notebooks – to avoid jumpyness */
- [renderLaTeX(), renderQuarto(), renderMD()]
+ [
+ renderLinuxTerminal(),
+ renderLaTeX(),
+ renderQuarto(),
+ renderTeaching(),
+ ]
}
- />
-
+ />,
+ )}
>
);
}
- function renderLinux() {
+ function renderLinuxTerminal() {
+ return wrapBtn(
+
+
+ ,
+ );
+ }
+
+ function renderX11() {
+ if (!availableFeatures.x11) return null;
+
+ return wrapBtn(
+
+
+ ,
+ );
+ }
+
+ function renderUtilities() {
if (disabledFeatures?.linux) return;
+
return (
<>
-
-
-
-
-
-
- {availableFeatures.x11 && (
-
-
-
-
-
- )}
- {create_folder != null && (
-
-
-
-
-
- )}
-
- {children}
-
-
+ {makeRow(
+ <>
+ {renderChat()}
+ {renderX11()}
+ {renderStopwatchTimer()}
+ {renderTaskList()}
+ {/* {children} */}
+ >,
+ )}
>
);
}
@@ -229,6 +275,7 @@ export function FileTypeSelector({
placement="left"
icon={"cloud-server"}
tip={"Affordable GPUs and high-end dedicated virtual machines."}
+ style={tipStyle}
>
-
+ return wrapBtn(
+ documentation to learn more.`}
+ values={{
+ A: (c) => (
+
+ {c}
+
+ ),
+ }}
+ />
+ }
+ >
+
+ ,
+ );
+ }
-
- {!disabledFeatures?.course && (
-
- documentation to learn more.`}
- values={{
- A: (c) => (
-
- {c}
-
- ),
- }}
- />
- }
- >
-
-
-
- )}
- {!disabledFeatures?.chat && (
-
- documentation to learn more.`}
- values={{
- A: (c) => (
- {c}
- ),
- }}
- />
- }
- >
-
-
-
- )}
+ function renderChat() {
+ if (disabledFeatures?.chat) return;
- {/* {!disabledFeatures?.timers && (
-
-
-
-
-
- )} */}
-
- >
+ return wrapBtn(
+ documentation to learn more.`}
+ values={{
+ A: (c) => {c},
+ }}
+ />
+ }
+ >
+
+ ,
);
}
@@ -396,62 +415,29 @@ export function FileTypeSelector({
});
}
- return (
-
-
-
-
-
+ return wrapBtn(
+
+
+ ,
);
}
- function addAiDocGenerate(btn: JSX.Element, ext: Ext) {
- if (isFlyout) {
- return (
-
-
- {btn}
-
-
-
-
-
- );
- } else {
- return (
-
- {btn}
-
-
- );
- }
- }
-
function renderQuarto() {
if (mode !== "flyout") return null;
if (!availableFeatures.qmd) return null;
@@ -463,7 +449,7 @@ export function FileTypeSelector({
title="Quarto File"
icon={NEW_FILETYPE_ICONS.qmd}
tip="Quarto document with real-time preview."
- style={mode === "flyout" ? { flex: "1 1 auto" } : undefined}
+ style={tipStyle}
>
);
+
return addAiDocGenerate(btn, "tex");
}
- function renderMD() {
+ function addAiDocGenerate(btn, ext) {
+ return wrapBtn(
+ ,
+ );
+ }
+
+ function renderMarkdown() {
const btn = (
+
+ ,
+ "rmd",
+ );
+ }
+
+ function renderWhiteboard() {
+ return wrapBtn(
+
+
+ ,
+ );
+ }
+ function renderSlides() {
const labelSlides = intl.formatMessage({
id: "new.file-type-selector.slides.title",
defaultMessage: "Slides",
description: "Short label on a buton to create a new slideshow file",
});
+ return wrapBtn(
+
+
+ ,
+ );
+ }
+
+ function renderMiscellaneous() {
+ if (disabledFeatures?.md) return;
+
return (
<>
- Miscellaneous Documents
+ Miscellaneous
-
- {availableFeatures.rmd &&
- addAiDocGenerate(
-
-
- ,
- "rmd",
- )}
-
-
-
-
-
-
-
-
-
-
-
- {renderSageWS()}
-
+ {makeRow(
+ <>
+ {renderMarkdown()}
+ {renderRMarkdown()}
+ {renderWhiteboard()}
+ {renderSlides()}
+ {renderSageWS()}
+ >,
+ )}
>
);
}
- function renderUtilities() {
- const labelTaskList = intl.formatMessage({
- id: "new.file-type-selector.tasks.label",
- defaultMessage: "Task List",
- });
+ function renderStopwatchTimer() {
+ if (disabledFeatures?.timers) return;
const labelStopWatchTimer = intl.formatMessage({
id: "project.new.file-type-selector.timers.label",
defaultMessage: "Stopwatch and Timer",
});
- return (
- <>
-
-
-
-
-
-
-
- {!disabledFeatures?.timers && (
-
-
-
-
-
- )}
-
- >
+ return wrapBtn(
+
+
+ ,
+ );
+ }
+
+ function renderTaskList() {
+ const labelTaskList = intl.formatMessage({
+ id: "new.file-type-selector.tasks.label",
+ defaultMessage: "Task List",
+ });
+
+ return wrapBtn(
+
+
+ ,
);
}
return (
-
- {renderJupyterNotebook()}
- {renderLinux()}
- {renderMarkdown()}
- {renderTeachingSocial()}
- {renderServers()}
+
+ {renderPopular()}
{renderUtilities()}
+ {renderMiscellaneous()}
+ {renderServers()}
);
}
diff --git a/src/packages/frontend/project/new/jupyter-buttons.tsx b/src/packages/frontend/project/new/jupyter-buttons.tsx
index 2a443f9e43..3ad74ef11e 100644
--- a/src/packages/frontend/project/new/jupyter-buttons.tsx
+++ b/src/packages/frontend/project/new/jupyter-buttons.tsx
@@ -18,6 +18,7 @@ import { AIGenerateDocumentButton } from "../page/home-page/ai-generate-document
import { ensure_project_running } from "../project-start-warning";
import { DELAY_SHOW_MS, NEW_FILETYPE_ICONS } from "./consts";
import { NewFileButton } from "./new-file-button";
+import { AiDocGenerateBtn } from "./add-ai-gen-btn";
/**
* An incomplete mapping of Jupyter Kernel "language" names to a display name and file extension.
@@ -51,7 +52,7 @@ interface JupyterNotebookButtonsProps {
create_file: (ext: string) => void;
btnSize: "small" | "large";
btnActive: (name: string) => boolean;
- grid: [number, number];
+ grid: [number | { flex: string }, number | { flex: string }];
filename: string;
filenameChanged?: boolean;
mode: "full" | "flyout";
@@ -259,23 +260,24 @@ export function JupyterNotebookButtons({
btns.push({
lang,
btn: (
-
- handleClick(kernelName)}
- ext={ext}
- size={btnSize}
- active={btnActive("ipynb-sagemath")}
- // mode={isFlyout ? "secondary" : undefined}
- />
-
+
+
+ handleClick(kernelName)}
+ ext={ext}
+ size={btnSize}
+ active={btnActive("ipynb-sagemath")}
+ // mode={isFlyout ? "secondary" : undefined}
+ />
+
+
),
});
}
@@ -287,7 +289,6 @@ export function JupyterNotebookButtons({
{btn}
{btn}
-
- {btn}
-
-
-
-
-
- );
- } else {
- return (
-
- {btn}
-
-
- );
- }
+ return (
+
+ );
}
return (
diff --git a/src/packages/frontend/project/new/new-file-button.tsx b/src/packages/frontend/project/new/new-file-button.tsx
index 442b8c660b..305254aa04 100644
--- a/src/packages/frontend/project/new/new-file-button.tsx
+++ b/src/packages/frontend/project/new/new-file-button.tsx
@@ -5,25 +5,26 @@
import { Button } from "antd";
+import { CSS } from "@cocalc/frontend/app-framework";
import { Icon, IconName } from "@cocalc/frontend/components/icon";
import { unreachable } from "@cocalc/util/misc";
import { COLORS } from "@cocalc/util/theme";
import { NEW_FILETYPE_ICONS, isNewFiletypeIconName } from "./consts";
-export const STYLE = {
- marginRight: "5px",
+export const STYLE: CSS = {
marginBottom: "5px",
whiteSpace: "normal",
padding: "10px",
height: "auto",
+ width: "100%",
} as const;
-const ICON_STYLE = {
+const ICON_STYLE: CSS = {
color: COLORS.FILE_ICON,
fontSize: "125%",
} as const;
-const ICON_STYLE_LARGE = {
+const ICON_STYLE_LARGE: CSS = {
...ICON_STYLE,
fontSize: "200%",
};
@@ -66,7 +67,7 @@ export function NewFileButton({
);
- const style = {
+ const style: CSS = {
...STYLE,
...(active
? {
@@ -76,7 +77,7 @@ export function NewFileButton({
: {}),
...(mode === "secondary" ? { padding: "5px" } : { width: "100%" }),
...(active && mode === "secondary" ? {} : undefined),
- ...(size == "large" ? { minHeight: "125px" } : undefined),
+ ...(size === "large" ? { minHeight: "125px" } : undefined),
};
function renderBody() {
diff --git a/src/packages/frontend/project/new/new-file-page.tsx b/src/packages/frontend/project/new/new-file-page.tsx
index ae2158bc1a..3be80853bf 100644
--- a/src/packages/frontend/project/new/new-file-page.tsx
+++ b/src/packages/frontend/project/new/new-file-page.tsx
@@ -6,13 +6,14 @@
import { Button, Input, Modal, Space } from "antd";
import { useEffect, useRef, useState } from "react";
import { defineMessage, FormattedMessage, useIntl } from "react-intl";
+
import { default_filename } from "@cocalc/frontend/account";
import { Alert, Col, Row } from "@cocalc/frontend/antd-bootstrap";
import { filenameIcon } from "@cocalc/frontend/file-associations";
import {
ProjectActions,
useActions,
- useRedux,
+ // useRedux,
useTypedRedux,
} from "@cocalc/frontend/app-framework";
import {
@@ -29,13 +30,13 @@ import ComputeServer from "@cocalc/frontend/compute/inline";
import { FileUpload, UploadLink } from "@cocalc/frontend/file-upload";
import { labels } from "@cocalc/frontend/i18n";
import { special_filenames_with_no_extension } from "@cocalc/frontend/project-file";
-import { ProjectMap } from "@cocalc/frontend/todo-types";
+// import { ProjectMap } from "@cocalc/frontend/todo-types";
import { filename_extension, is_only_downloadable } from "@cocalc/util/misc";
import { COLORS } from "@cocalc/util/theme";
import { PathNavigator } from "../explorer/path-navigator";
import { useAvailableFeatures } from "../use-available-features";
import { FileTypeSelector } from "./file-type-selector";
-import { NewFileButton } from "./new-file-button";
+// import { NewFileButton } from "./new-file-button";
import { NewFileDropdown } from "./new-file-dropdown";
const CREATE_MSG = defineMessage({
@@ -84,15 +85,15 @@ export default function NewFilePage(props: Props) {
{ project_id },
"file_creation_error",
);
- const downloading_file = useTypedRedux({ project_id }, "downloading_file");
- const project_map: ProjectMap | undefined = useRedux([
- "projects",
- "project_map",
- ]);
- const get_total_project_quotas = useRedux([
- "projects",
- "get_total_project_quotas",
- ]);
+ // const downloading_file = useTypedRedux({ project_id }, "downloading_file");
+ // const project_map: ProjectMap | undefined = useRedux([
+ // "projects",
+ // "project_map",
+ // ]);
+ // const get_total_project_quotas = useRedux([
+ // "projects",
+ // "get_total_project_quotas",
+ // ]);
if (actions == null) {
return ;
@@ -164,16 +165,16 @@ export default function NewFilePage(props: Props) {
);
}
- function blocked() {
- if (project_map == null) {
- return "";
- }
- if (get_total_project_quotas(project_id)?.network) {
- return "";
- } else {
- return " (access blocked -- see project settings)";
- }
- }
+ // function blocked() {
+ // if (project_map == null) {
+ // return "";
+ // }
+ // if (get_total_project_quotas(project_id)?.network) {
+ // return "";
+ // } else {
+ // return " (access blocked -- see project settings)";
+ // }
+ // }
function createFolder() {
getActions().create_folder({
@@ -506,8 +507,8 @@ export default function NewFilePage(props: Props) {
availableFeatures={availableFeatures}
filename={filename}
filenameChanged={filenameChanged}
- >
-
+ {/* createFile()}
loading={downloading_file}
/>
-
-
+ */}
{renderUpload()}
diff --git a/src/packages/frontend/project/page/home-page/ai-generate-document.tsx b/src/packages/frontend/project/page/home-page/ai-generate-document.tsx
index 74a0743500..b7a92a857c 100644
--- a/src/packages/frontend/project/page/home-page/ai-generate-document.tsx
+++ b/src/packages/frontend/project/page/home-page/ai-generate-document.tsx
@@ -62,6 +62,7 @@ import getKernelSpec from "@cocalc/frontend/jupyter/kernelspecs";
import { splitCells } from "@cocalc/frontend/jupyter/llm/split-cells";
import NBViewer from "@cocalc/frontend/jupyter/nbviewer/nbviewer";
import { LLMCostEstimation } from "@cocalc/frontend/misc/llm-cost-estimation";
+import { useProjectContext } from "@cocalc/frontend/project/context";
import { LLMEvent } from "@cocalc/frontend/project/history/types";
import { DELAY_SHOW_MS } from "@cocalc/frontend/project/new/consts";
import { STYLE as NEW_FILE_STYLE } from "@cocalc/frontend/project/new/new-file-button";
@@ -1102,18 +1103,17 @@ export function AIGenerateDocumentModal({
}
export function AIGenerateDocumentButton({
- project_id,
style,
mode = "full",
ext,
filename,
}: {
- project_id: string;
style?: CSS;
mode?: "full" | "flyout";
ext: Props["ext"];
filename?: string;
}) {
+ const { project_id } = useProjectContext();
const intl = useIntl();
const [show, setShow] = useState(false);
@@ -1122,10 +1122,11 @@ export function AIGenerateDocumentButton({
}
const btnStyle: CSS = {
- width: "100%",
overflowX: "hidden",
overflow: "hidden",
whiteSpace: "nowrap",
+ width: "100%",
+ textOverflow: "ellipsis",
...(mode === "flyout"
? { ...NEW_FILE_STYLE, marginRight: "0", marginBottom: "0" }
: {}),
@@ -1159,7 +1160,13 @@ export function AIGenerateDocumentButton({
style={btnStyle}
size={mode === "flyout" ? "small" : undefined}
>
-
+
{mode === "full"
? ` ${intl.formatMessage(labels.ai_generate_label)}`