Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/codemirror/extensions/insert-image.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Button, Form, Input, Modal } from "antd";6import * as CodeMirror from "codemirror";78import { alert_message } from "@cocalc/frontend/alerts";9import { Icon } from "@cocalc/frontend/components";10import { show_react_modal } from "@cocalc/frontend/misc";1112export interface Options {13url: string;14title: string;15height: string;16width: string;17}1819function insert_image(mode: string, opts: Options): string {20let { url, title, height, width } = opts;21let s = "";2223if (mode === "rst") {24// .. image:: picture.jpeg25// :height: 100px26// :width: 200 px27// :alt: alternate text28// :align: right29s = `\n.. image:: ${url}\n`;30if (height.length > 0) {31s += ` :height: ${height}px\n`;32}33if (width.length > 0) {34s += ` :width: ${width}px\n`;35}36if (title.length > 0) {37s += ` :alt: ${title}\n`;38}39} else if (mode === "md" && width.length === 0 && height.length === 0) {40// use markdown's funny image format if width/height not given41if (title.length > 0) {42title = ` \"${title}\"`;43}44s = `![](${url}${title})`;45} else if (mode === "tex") {46//cm.tex_ensure_preamble("\\usepackage{graphicx}");47const w = parseInt(width);48if (isNaN(w)) {49width = "0.8";50} else {51width = `${w / 100.0}`;52}53if (title.length > 0) {54s = `\55\\begin{figure}[p]56\\centering57\\includegraphics[width=${width}\\textwidth]{${url}}58\\caption{${title}}59\\end{figure}\60`;61} else {62s = `\\includegraphics[width=${width}\\textwidth]{${url}}`;63}64} else if (mode === "mediawiki") {65// https://www.mediawiki.org/wiki/Help:Images66// [[File:Example.jpg|<width>[x<height>]px]]67let size = "";68if (width.length > 0) {69size = `|${width}`;70if (height.length > 0) {71size += `x${height}`;72}73size += "px";74}75s = `[[File:${url}${size}]]`;76} else {77// fallback for mode == "md" but height or width is given78if (title.length > 0) {79title = ` title='${title}'`;80}81if (width.length > 0) {82width = ` width=${width}`;83}84if (height.length > 0) {85height = ` height=${height}`;86}87s = `<img src='${url}'${width}${height}${title} />`;88}89return s;90}9192export async function get_insert_image_opts_from_user(93note = "",94): Promise<undefined | Options> {95const opts = await show_react_modal((cb) => {96return (97<Modal98title={99<div>100<h3>101<Icon name="image" /> Insert Image102</h3>103<div style={{ fontWeight: 300 }}>{note}</div>104</div>105}106open107footer={<Button onClick={() => cb()}>Cancel</Button>}108onCancel={() => cb()}109>110<Form111labelCol={{ span: 8 }}112wrapperCol={{ span: 16 }}113name="options"114initialValues={{115url: "",116width: "",117height: "",118title: "",119}}120onFinish={(values) => cb(undefined, values)}121onFinishFailed={(err) => cb(err)}122>123<Form.Item124label="URL"125name="url"126rules={[127{128required: true,129message: "You must enter the URL of the link.",130},131]}132>133<Input placeholder="URL..." />134</Form.Item>135<Form.Item label="Width" name="width">136<Input placeholder="Width..." />137</Form.Item>138<Form.Item label="Height" name="height">139<Input placeholder="Height..." />140</Form.Item>141<Form.Item label="Title" name="title">142<Input placeholder="Title..." />143</Form.Item>144<Form.Item wrapperCol={{ offset: 8, span: 16 }}>145<Button type="primary" htmlType="submit">146Submit147</Button>148</Form.Item>149</Form>150</Modal>151);152});153if (opts != null) {154// if width or height are numbers, append pixel units.155for (const x of ["width", "height"]) {156if (opts[x] && opts[x].match(/^[\.0-9]+$/)) {157opts[x] += "px";158}159}160}161return opts;162}163164CodeMirror.defineExtension("insert_image", async function (): Promise<void> {165// @ts-ignore166const cm = this;167let opts: Options | undefined = undefined;168try {169opts = await get_insert_image_opts_from_user();170} catch (err) {171alert_message({ type: "error", message: err.errorFields[0]?.errors });172return;173}174175if (opts == null) {176return; // user canceled177}178179const selections = cm.listSelections();180selections.reverse();181for (const sel of selections) {182const link = insert_image(cm.get_edit_mode(sel.head), opts);183if (sel.empty()) {184cm.replaceRange(link, sel.head);185} else {186cm.replaceRange(link, sel.from(), sel.to());187}188}189});190191192