Improved editor
This commit is contained in:
parent
cedc25862d
commit
2a799cf9e0
|
@ -38,8 +38,8 @@ export function Button(props: Immutable<Props>): JSX.Element {
|
||||||
draggable={draggable}
|
draggable={draggable}
|
||||||
id={id}
|
id={id}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={`--opacityBadge:100 grid select-none place-content-center
|
className={`--opacityBadge:100 grid select-none grid-flow-col place-content-center
|
||||||
place-items-center rounded-full border-[1px] border-dark px-4 pt-[0.4rem]
|
place-items-center gap-2 rounded-full border-[1px] border-dark px-4 pt-[0.4rem]
|
||||||
pb-[0.5rem] text-dark transition-all hover:[--opacityBadge:0] ${className} ${
|
pb-[0.5rem] text-dark transition-all hover:[--opacityBadge:0] ${className} ${
|
||||||
active
|
active
|
||||||
? "cursor-not-allowed !border-black bg-black text-light drop-shadow-black-lg"
|
? "cursor-not-allowed !border-black bg-black text-light drop-shadow-black-lg"
|
||||||
|
@ -56,7 +56,7 @@ export function Button(props: Immutable<Props>): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{icon && <Ico icon={icon} />}
|
{icon && <Ico icon={icon} />}
|
||||||
<p>{text}</p>
|
{text && <p>{text}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ export function ReturnButton(props: Immutable<Props>): JSX.Element {
|
||||||
<Button
|
<Button
|
||||||
onClick={() => appLayout.setSubPanelOpen(false)}
|
onClick={() => appLayout.setSubPanelOpen(false)}
|
||||||
href={props.href}
|
href={props.href}
|
||||||
className="grid grid-flow-col gap-2"
|
|
||||||
text={`${props.langui.return_to} ${props.title}`}
|
text={`${props.langui.return_to} ${props.title}`}
|
||||||
icon={Icon.NavigateBefore}
|
icon={Icon.NavigateBefore}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { GetStaticPropsContext } from "next";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import TurndownService from "turndown";
|
import TurndownService from "turndown";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
|
import { TOC } from "components/Markdown/TOC";
|
||||||
|
|
||||||
interface Props extends AppStaticProps {}
|
interface Props extends AppStaticProps {}
|
||||||
|
|
||||||
|
@ -24,19 +25,124 @@ export default function Editor(props: Immutable<Props>): JSX.Element {
|
||||||
const [markdown, setMarkdown] = useState("");
|
const [markdown, setMarkdown] = useState("");
|
||||||
const [converterOpened, setConverterOpened] = useState(false);
|
const [converterOpened, setConverterOpened] = useState(false);
|
||||||
|
|
||||||
function insert(
|
function wrap(
|
||||||
text: string,
|
wrapper: string,
|
||||||
prepend: string,
|
properties?: Record<string, string>,
|
||||||
append: string,
|
addInnerNewLines?: boolean
|
||||||
selectionStart: number,
|
) {
|
||||||
selectionEnd: number
|
transformationWrapper((value, selectionStart, selectionEnd) => {
|
||||||
): string {
|
let prepend = wrapper;
|
||||||
let newText = text.slice(0, selectionStart);
|
let append = wrapper;
|
||||||
newText += prepend;
|
|
||||||
newText += text.slice(selectionStart, selectionEnd);
|
if (properties) {
|
||||||
newText += append;
|
prepend = `<${wrapper}${Object.entries(properties).map(
|
||||||
newText += text.slice(selectionEnd);
|
([propertyName, propertyValue]) =>
|
||||||
return newText;
|
` ${propertyName}="${propertyValue}"`
|
||||||
|
)}>`;
|
||||||
|
append = `</${wrapper}>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addInnerNewLines) {
|
||||||
|
prepend = `${prepend}\n`;
|
||||||
|
append = `\n${append}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newValue = "";
|
||||||
|
newValue += value.slice(0, selectionStart);
|
||||||
|
newValue += prepend;
|
||||||
|
newValue += value.slice(selectionStart, selectionEnd);
|
||||||
|
newValue += append;
|
||||||
|
newValue += value.slice(selectionEnd);
|
||||||
|
return { prependLength: prepend.length, transformedValue: newValue };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleWrap(
|
||||||
|
wrapper: string,
|
||||||
|
properties?: Record<string, string>,
|
||||||
|
addInnerNewLines?: boolean
|
||||||
|
) {
|
||||||
|
const textarea = document.querySelector(
|
||||||
|
"#editorTextArea"
|
||||||
|
) as HTMLTextAreaElement;
|
||||||
|
const { value, selectionStart, selectionEnd } = textarea;
|
||||||
|
|
||||||
|
if (
|
||||||
|
value.slice(selectionStart - wrapper.length, selectionStart) ===
|
||||||
|
wrapper &&
|
||||||
|
value.slice(selectionEnd, selectionEnd + wrapper.length) === wrapper
|
||||||
|
) {
|
||||||
|
unwrap(wrapper);
|
||||||
|
} else {
|
||||||
|
wrap(wrapper, properties, addInnerNewLines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrap(wrapper: string) {
|
||||||
|
transformationWrapper((value, selectionStart, selectionEnd) => {
|
||||||
|
let newValue = "";
|
||||||
|
newValue += value.slice(0, selectionStart - wrapper.length);
|
||||||
|
newValue += value.slice(selectionStart, selectionEnd);
|
||||||
|
newValue += value.slice(wrapper.length + selectionEnd);
|
||||||
|
return { prependLength: -wrapper.length, transformedValue: newValue };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function preline(prepend: string) {
|
||||||
|
transformationWrapper((value, selectionStart) => {
|
||||||
|
const lastNewLine = value.slice(0, selectionStart).lastIndexOf("\n") + 1;
|
||||||
|
|
||||||
|
let newValue = "";
|
||||||
|
newValue += value.slice(0, lastNewLine);
|
||||||
|
newValue += prepend;
|
||||||
|
newValue += value.slice(lastNewLine);
|
||||||
|
|
||||||
|
return { prependLength: prepend.length, transformedValue: newValue };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function insert(prepend: string) {
|
||||||
|
transformationWrapper((value, selectionStart) => {
|
||||||
|
let newValue = "";
|
||||||
|
newValue += value.slice(0, selectionStart);
|
||||||
|
newValue += prepend;
|
||||||
|
newValue += value.slice(selectionStart);
|
||||||
|
|
||||||
|
return { prependLength: prepend.length, transformedValue: newValue };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendDoc(append: string) {
|
||||||
|
transformationWrapper((value) => {
|
||||||
|
let newValue = value + append;
|
||||||
|
return { prependLength: 0, transformedValue: newValue };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformationWrapper(
|
||||||
|
transformation: (
|
||||||
|
value: string,
|
||||||
|
selectionStart: number,
|
||||||
|
selectedEnd: number
|
||||||
|
) => { prependLength: number; transformedValue: string }
|
||||||
|
) {
|
||||||
|
const textarea = document.querySelector(
|
||||||
|
"#editorTextArea"
|
||||||
|
) as HTMLTextAreaElement;
|
||||||
|
const { value, selectionStart, selectionEnd } = textarea;
|
||||||
|
|
||||||
|
const { prependLength, transformedValue } = transformation(
|
||||||
|
value,
|
||||||
|
selectionStart,
|
||||||
|
selectionEnd
|
||||||
|
);
|
||||||
|
|
||||||
|
textarea.value = transformedValue;
|
||||||
|
handleInput(textarea.value);
|
||||||
|
|
||||||
|
textarea.focus();
|
||||||
|
textarea.selectionStart = selectionStart + prependLength;
|
||||||
|
textarea.selectionEnd = selectionEnd + prependLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentPanel = (
|
const contentPanel = (
|
||||||
|
@ -79,111 +185,125 @@ export default function Editor(props: Immutable<Props>): JSX.Element {
|
||||||
className="h-[50vh] w-[50vw] font-monospace mobile:w-[75vw]"
|
className="h-[50vh] w-[50vw] font-monospace mobile:w-[75vw]"
|
||||||
/>
|
/>
|
||||||
</Popup>
|
</Popup>
|
||||||
|
|
||||||
<div className="mb-4 flex flex-row gap-2">
|
<div className="mb-4 flex flex-row gap-2">
|
||||||
<ToolTip
|
<ToolTip
|
||||||
placement="bottom"
|
|
||||||
content={
|
content={
|
||||||
<>
|
<div className="grid gap-2">
|
||||||
<h3 className="text-lg">Transcript container</h3>
|
<h3 className="text-lg">Headers</h3>
|
||||||
<p>
|
<Button onClick={() => preline("# ")} text={"H1"} />
|
||||||
Use this to create dialogues and transcripts. You can then add
|
<Button onClick={() => preline("## ")} text={"H2"} />
|
||||||
transcript speech line within (
|
<Button onClick={() => preline("### ")} text={"H3"} />
|
||||||
<Ico className="text-xs" icon={Icon.RecordVoiceOver} />)
|
<Button onClick={() => preline("#### ")} text={"H4"} />
|
||||||
</p>
|
<Button onClick={() => preline("##### ")} text={"H5"} />
|
||||||
</>
|
<Button onClick={() => preline("###### ")} text={"H6"} />
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button
|
<Button icon={Icon.Title} />
|
||||||
onClick={() => {
|
|
||||||
const textarea = document.querySelector(
|
|
||||||
"#editorTextArea"
|
|
||||||
) as HTMLTextAreaElement;
|
|
||||||
const { value, selectionStart, selectionEnd } = textarea;
|
|
||||||
textarea.value = insert(
|
|
||||||
value,
|
|
||||||
"\n<Transcript>\n",
|
|
||||||
"\n</Transcript>\n",
|
|
||||||
selectionStart,
|
|
||||||
selectionEnd
|
|
||||||
);
|
|
||||||
handleInput(textarea.value);
|
|
||||||
}}
|
|
||||||
icon={Icon.QuestionAnswer}
|
|
||||||
/>
|
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
<ToolTip
|
|
||||||
placement="bottom"
|
|
||||||
content={
|
|
||||||
<>
|
|
||||||
<h3 className="text-lg">Transcript speech line</h3>
|
|
||||||
<p>
|
|
||||||
Use to add a dialogue/transcript line. Change the{" "}
|
|
||||||
<kbd>name</kbd> property to chang the name of the speaker
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
const textarea = document.querySelector(
|
|
||||||
"#editorTextArea"
|
|
||||||
) as HTMLTextAreaElement;
|
|
||||||
|
|
||||||
const { value, selectionStart, selectionEnd } = textarea;
|
|
||||||
textarea.value = insert(
|
|
||||||
value,
|
|
||||||
'<Line name="speaker">',
|
|
||||||
"</Line>\n",
|
|
||||||
selectionStart,
|
|
||||||
selectionEnd
|
|
||||||
);
|
|
||||||
handleInput(textarea.value);
|
|
||||||
}}
|
|
||||||
icon={Icon.RecordVoiceOver}
|
|
||||||
/>
|
|
||||||
</ToolTip>
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
content={<h3 className="text-lg">Vertical spacer</h3>}
|
content={<h3 className="text-lg">Toggle Bold</h3>}
|
||||||
|
>
|
||||||
|
<Button onClick={() => toggleWrap("**")} icon={Icon.FormatBold} />
|
||||||
|
</ToolTip>
|
||||||
|
|
||||||
|
<ToolTip
|
||||||
|
placement="bottom"
|
||||||
|
content={<h3 className="text-lg">Toggle Italic</h3>}
|
||||||
|
>
|
||||||
|
<Button onClick={() => toggleWrap("_")} icon={Icon.FormatItalic} />
|
||||||
|
</ToolTip>
|
||||||
|
|
||||||
|
<ToolTip
|
||||||
|
placement="bottom"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<h3 className="text-lg">Toggle Inline Code</h3>
|
||||||
|
<p>
|
||||||
|
Makes the text monospace (like text from a computer terminal).
|
||||||
|
Usually used for stylistic purposes in transcripts.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button onClick={() => toggleWrap("`")} icon={Icon.Code} />
|
||||||
|
</ToolTip>
|
||||||
|
|
||||||
|
<ToolTip
|
||||||
|
placement="bottom"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<h3 className="text-lg">Insert footnote</h3>
|
||||||
|
<p>When inserted “x”</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const textarea = document.querySelector(
|
insert("[^x]");
|
||||||
"#editorTextArea"
|
appendDoc("\n\n[^x]: This is a footnote.");
|
||||||
) as HTMLTextAreaElement;
|
|
||||||
const { value, selectionStart, selectionEnd } = textarea;
|
|
||||||
textarea.value = insert(
|
|
||||||
value,
|
|
||||||
"<Sep />",
|
|
||||||
"",
|
|
||||||
selectionStart,
|
|
||||||
selectionEnd
|
|
||||||
);
|
|
||||||
handleInput(textarea.value);
|
|
||||||
}}
|
}}
|
||||||
icon={Icon.DensityLarge}
|
icon={Icon.Superscript}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
||||||
|
<ToolTip
|
||||||
|
placement="bottom"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<h3 className="text-lg">Transcripts</h3>
|
||||||
|
<p>
|
||||||
|
Use this to create dialogues and transcripts. Start by adding a
|
||||||
|
container, then add transcript speech line within.
|
||||||
|
</p>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<ToolTip
|
||||||
|
placement="right"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<h3 className="text-lg">Transcript container</h3>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={() => wrap("Transcript", {}, true)}
|
||||||
|
icon={Icon.AddBox}
|
||||||
|
/>
|
||||||
|
</ToolTip>
|
||||||
|
<ToolTip
|
||||||
|
placement="right"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<h3 className="text-lg">Transcript speech line</h3>
|
||||||
|
<p>
|
||||||
|
Use to add a dialogue/transcript line. Change the{" "}
|
||||||
|
<kbd>name</kbd> property to chang the name of the
|
||||||
|
speaker
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={() => wrap("Line", { name: "speaker" })}
|
||||||
|
icon={Icon.RecordVoiceOver}
|
||||||
|
/>
|
||||||
|
</ToolTip>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button icon={Icon.RecordVoiceOver} />
|
||||||
|
</ToolTip>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
content={<h3 className="text-lg">Inset box</h3>}
|
content={<h3 className="text-lg">Inset box</h3>}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => wrap("InsetBox", {}, true)}
|
||||||
const textarea = document.querySelector(
|
|
||||||
"#editorTextArea"
|
|
||||||
) as HTMLTextAreaElement;
|
|
||||||
const { value, selectionStart, selectionEnd } = textarea;
|
|
||||||
textarea.value = insert(
|
|
||||||
value,
|
|
||||||
"\n<InsetBox>\n",
|
|
||||||
"\n</InsetBox>\n",
|
|
||||||
selectionStart,
|
|
||||||
selectionEnd
|
|
||||||
);
|
|
||||||
handleInput(textarea.value);
|
|
||||||
}}
|
|
||||||
icon={Icon.CheckBoxOutlineBlank}
|
icon={Icon.CheckBoxOutlineBlank}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
@ -191,28 +311,30 @@ export default function Editor(props: Immutable<Props>): JSX.Element {
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
content={<h3 className="text-lg">Scene break</h3>}
|
content={<h3 className="text-lg">Scene break</h3>}
|
||||||
>
|
>
|
||||||
<Button
|
<Button onClick={() => insert("\n* * *\n")} icon={Icon.MoreHoriz} />
|
||||||
onClick={() => {
|
|
||||||
const textarea = document.querySelector(
|
|
||||||
"#editorTextArea"
|
|
||||||
) as HTMLTextAreaElement;
|
|
||||||
const { value, selectionStart, selectionEnd } = textarea;
|
|
||||||
textarea.value = insert(
|
|
||||||
value,
|
|
||||||
"\n\n<SceneBreak />\n\n",
|
|
||||||
"",
|
|
||||||
selectionStart,
|
|
||||||
selectionEnd
|
|
||||||
);
|
|
||||||
handleInput(textarea.value);
|
|
||||||
}}
|
|
||||||
icon={Icon.MoreHoriz}
|
|
||||||
/>
|
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={
|
content={
|
||||||
<div className="flex flex-col place-items-center gap-2">
|
<div className="flex flex-col place-items-center gap-2">
|
||||||
<h3 className="text-lg">Intralink</h3>
|
<h3 className="text-lg">Links</h3>
|
||||||
|
<ToolTip
|
||||||
|
placement="right"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<h3 className="text-lg">External Link</h3>
|
||||||
|
<p className="text-xs">
|
||||||
|
Provides a link to another webpage / website
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={() => insert("[Link name](https://domain.com)")}
|
||||||
|
icon={Icon.Link}
|
||||||
|
text={"External"}
|
||||||
|
/>
|
||||||
|
</ToolTip>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
placement="right"
|
placement="right"
|
||||||
content={
|
content={
|
||||||
|
@ -226,21 +348,9 @@ export default function Editor(props: Immutable<Props>): JSX.Element {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => wrap("IntraLink", {})}
|
||||||
const textarea = document.querySelector(
|
|
||||||
"#editorTextArea"
|
|
||||||
) as HTMLTextAreaElement;
|
|
||||||
const { value, selectionStart, selectionEnd } = textarea;
|
|
||||||
textarea.value = insert(
|
|
||||||
value,
|
|
||||||
"<IntraLink>",
|
|
||||||
"</IntraLink>",
|
|
||||||
selectionStart,
|
|
||||||
selectionEnd
|
|
||||||
);
|
|
||||||
handleInput(textarea.value);
|
|
||||||
}}
|
|
||||||
icon={Icon.Link}
|
icon={Icon.Link}
|
||||||
|
text={"Internal"}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
<ToolTip
|
<ToolTip
|
||||||
|
@ -256,22 +366,9 @@ export default function Editor(props: Immutable<Props>): JSX.Element {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => wrap("IntraLink", { target: "target" })}
|
||||||
const textarea = document.querySelector(
|
|
||||||
"#editorTextArea"
|
|
||||||
) as HTMLTextAreaElement;
|
|
||||||
const { value, selectionStart, selectionEnd } = textarea;
|
|
||||||
textarea.value = insert(
|
|
||||||
value,
|
|
||||||
'<IntraLink target="target">',
|
|
||||||
"</IntraLink>",
|
|
||||||
selectionStart,
|
|
||||||
selectionEnd
|
|
||||||
);
|
|
||||||
handleInput(textarea.value);
|
|
||||||
}}
|
|
||||||
icon={Icon.Link}
|
icon={Icon.Link}
|
||||||
text="+ target"
|
text="Internal (w/ target)"
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -284,23 +381,7 @@ export default function Editor(props: Immutable<Props>): JSX.Element {
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
content={<h3 className="text-lg">Player’s name placeholder</h3>}
|
content={<h3 className="text-lg">Player’s name placeholder</h3>}
|
||||||
>
|
>
|
||||||
<Button
|
<Button onClick={() => insert("<player>")} icon={Icon.Person} />
|
||||||
onClick={() => {
|
|
||||||
const textarea = document.querySelector(
|
|
||||||
"#editorTextArea"
|
|
||||||
) as HTMLTextAreaElement;
|
|
||||||
const { value, selectionStart, selectionEnd } = textarea;
|
|
||||||
textarea.value = insert(
|
|
||||||
value,
|
|
||||||
"<player>",
|
|
||||||
"",
|
|
||||||
selectionStart,
|
|
||||||
selectionEnd
|
|
||||||
);
|
|
||||||
handleInput(textarea.value);
|
|
||||||
}}
|
|
||||||
icon={Icon.Person}
|
|
||||||
/>
|
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
|
@ -325,8 +406,8 @@ export default function Editor(props: Immutable<Props>): JSX.Element {
|
||||||
const textarea = event.target as HTMLTextAreaElement;
|
const textarea = event.target as HTMLTextAreaElement;
|
||||||
handleInput(textarea.value);
|
handleInput(textarea.value);
|
||||||
}}
|
}}
|
||||||
className="h-[70vh] w-full rounded-xl
|
className="h-[70vh] w-full rounded-xl bg-mid !bg-opacity-40 p-8 font-monospace
|
||||||
bg-mid !bg-opacity-40 p-8 font-monospace text-black outline-none"
|
text-black outline-none"
|
||||||
value={markdown}
|
value={markdown}
|
||||||
title="Input textarea"
|
title="Input textarea"
|
||||||
/>
|
/>
|
||||||
|
@ -338,6 +419,10 @@ export default function Editor(props: Immutable<Props>): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8">
|
||||||
|
<TOC text={markdown} />
|
||||||
|
</div>
|
||||||
</ContentPanel>
|
</ContentPanel>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
|
|
Loading…
Reference in New Issue