relative markdown text (#1489)

This commit is contained in:
Mike Sawka 2024-12-16 16:04:07 -08:00 committed by GitHub
parent 51bd45bd2b
commit f1cd6b933d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 246 additions and 102 deletions

View File

@ -20,6 +20,9 @@
"[less]": { "[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": { "[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },

View File

@ -1,6 +1,12 @@
// Copyright 2024, Command Line Inc. // Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
import { RpcApi } from "@/app/store/wshclientapi";
import { TabRpcClient } from "@/app/store/wshrpcutil";
import { getWebServerEndpoint } from "@/util/endpoints";
import { isBlank, makeConnRoute } from "@/util/util";
import parseSrcSet from "parse-srcset";
export type MarkdownContentBlockType = { export type MarkdownContentBlockType = {
type: string; type: string;
id: string; id: string;
@ -147,3 +153,56 @@ export function transformBlocks(content: string): { content: string; blocks: Map
blocks: blocks, blocks: blocks,
}; };
} }
export const resolveRemoteFile = async (filepath: string, resolveOpts: MarkdownResolveOpts): Promise<string | null> => {
if (!filepath || filepath.startsWith("http://") || filepath.startsWith("https://")) {
return filepath;
}
try {
const route = makeConnRoute(resolveOpts.connName);
const fileInfo = await RpcApi.RemoteFileJoinCommand(TabRpcClient, [resolveOpts.baseDir, filepath], {
route: route,
});
const usp = new URLSearchParams();
usp.set("path", fileInfo.path);
if (!isBlank(resolveOpts.connName)) {
usp.set("connection", resolveOpts.connName);
}
return getWebServerEndpoint() + "/wave/stream-file?" + usp.toString();
} catch (err) {
console.warn("Failed to resolve remote file:", filepath, err);
return null;
}
};
export const resolveSrcSet = async (srcSet: string, resolveOpts: MarkdownResolveOpts): Promise<string> => {
if (!srcSet) return null;
// Parse the srcset
const candidates = parseSrcSet(srcSet);
// Resolve each URL in the array of candidates
const resolvedCandidates = await Promise.all(
candidates.map(async (candidate) => {
const resolvedUrl = await resolveRemoteFile(candidate.url, resolveOpts);
return {
...candidate,
url: resolvedUrl,
};
})
);
// Reconstruct the srcset string
return resolvedCandidates
.map((candidate) => {
let part = candidate.url;
if (candidate.w) part += ` ${candidate.w}w`;
if (candidate.h) part += ` ${candidate.h}h`;
if (candidate.d) part += ` ${candidate.d}x`;
return part;
})
.join(", ");
};

View File

@ -16,21 +16,66 @@
overflow: scroll; overflow: scroll;
line-height: 1.5; line-height: 1.5;
color: var(--main-text-color); color: var(--main-text-color);
font-family: var(--markdown-font); font-family: var(--markdown-font-family);
font-size: 14px; font-size: var(--markdown-font-size);
overflow-wrap: break-word; overflow-wrap: break-word;
&.non-scrollable { &.non-scrollable {
overflow: hidden; overflow: hidden;
} }
.heading { .heading:not(.heading ~ .heading) {
&:first-of-type {
margin-top: 0 !important; margin-top: 0 !important;
} }
.heading {
color: var(--main-text-color); color: var(--main-text-color);
margin-top: 16px; margin-top: 1.143em;
margin-bottom: 8px; margin-bottom: 0.571em;
font-weight: semibold;
padding-top: 0.429em;
&.is-1 {
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.429em;
font-size: 2em;
}
&.is-2 {
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.429em;
font-size: 1.5em;
}
&.is-3 {
font-size: 1.25em;
}
&.is-4 {
font-size: 1em;
}
&.is-5 {
font-size: 0.875em;
}
&.is-6 {
font-size: 0.85em;
}
}
.paragraph {
margin-top: 0;
margin-bottom: 10px;
}
img {
border-style: none;
max-width: 100%;
box-sizing: content-box;
&[align="right"] {
padding-left: 20px;
}
&[align="left"] {
padding-right: 20px;
}
} }
strong { strong {
@ -44,24 +89,24 @@
ul { ul {
list-style-type: disc; list-style-type: disc;
list-style-position: outside; list-style-position: outside;
margin-left: 16px; margin-left: 1.143em;
} }
ol { ol {
list-style-position: outside; list-style-position: outside;
margin-left: 19px; margin-left: 1.357em;
} }
blockquote { blockquote {
margin: 4px 10px 4px 10px; margin: 0.286em 0.714em;
border-radius: 3px; border-radius: 4px;
background-color: var(--panel-bg-color); background-color: var(--panel-bg-color);
padding: 2px 4px 2px 6px; padding: 0.143em 0.286em 0.143em 0.429em;
} }
pre.codeblock { pre.codeblock {
background-color: var(--panel-bg-color); background-color: var(--panel-bg-color);
margin: 4px 10px; margin: 0.286em 0.714em;
padding: 0.4em 0.7em; padding: 0.4em 0.7em;
border-radius: 4px; border-radius: 4px;
position: relative; position: relative;
@ -83,11 +128,11 @@
right: 0; right: 0;
border-radius: 4px; border-radius: 4px;
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
margin: 2px 2px; margin: 0.143em;
padding: 4px 4px; padding: 0.286em;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
gap: 4px; gap: 0.286em;
} }
&:hover .codeblock-actions { &:hover .codeblock-actions {
@ -98,6 +143,7 @@
code { code {
color: var(--main-text-color); color: var(--main-text-color);
font: var(--fixed-font); font: var(--fixed-font);
font-size: var(--markdown-fixed-font-size);
border-radius: 4px; border-radius: 4px;
} }
@ -105,41 +151,13 @@
outline: 2px solid var(--accent-color); outline: 2px solid var(--accent-color);
} }
.heading {
font-weight: semibold;
padding-top: 6px;
}
.heading.is-1 {
border-bottom: 1px solid var(--border-color);
padding-bottom: 6px;
font-size: 2em;
}
.heading.is-2 {
border-bottom: 1px solid var(--border-color);
padding-bottom: 6px;
font-size: 1.5em;
}
.heading.is-3 {
font-size: 1.25em;
}
.heading.is-4 {
font-size: 1em;
}
.heading.is-5 {
font-size: 0.875em;
}
.heading.is-6 {
font-size: 0.85em;
}
.waveblock { .waveblock {
margin: 16px 0; margin: 1.143em 0;
.wave-block-content { .wave-block-content {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 12px; padding: 0.857em;
background-color: var(--highlight-bg-color); background-color: var(--highlight-bg-color);
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 8px; border-radius: 8px;
@ -150,15 +168,15 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 40px; width: 2.857em;
height: 40px; height: 2.857em;
background-color: black; background-color: black;
border-radius: 8px; border-radius: 8px;
margin-right: 12px; margin-right: 0.857em;
} }
.wave-block-icon i { .wave-block-icon i {
font-size: 18px; font-size: 1.125em;
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
@ -168,19 +186,18 @@
} }
.wave-block-filename { .wave-block-filename {
font-size: 14px; font-size: 1em;
font-weight: 500; font-weight: 500;
color: var(--main-text-color); color: var(--main-text-color);
} }
.wave-block-size { .wave-block-size {
font-size: 12px; font-size: 0.857em;
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
} }
} }
// The TOC view should scroll independently of the contents view.
.toc { .toc {
max-width: 40%; max-width: 40%;
height: 100%; height: 100%;
@ -192,21 +209,20 @@
top: 0; top: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 5px; gap: 0.357em;
text-wrap: wrap; text-wrap: wrap;
h4 { h4 {
padding-left: 5px; padding-left: 0.357em;
} }
.toc-item { .toc-item {
cursor: pointer; cursor: pointer;
--indent-factor: 1; --indent-factor: 1;
// The offset in the padding will ensure that when the text in the item wraps, it indents slightly.
// The 5px offset in the padding will ensure that when the text in the item wraps, it indents slightly.
// The indent factor is set in the React code and denotes the depth of the item in the TOC tree. // The indent factor is set in the React code and denotes the depth of the item in the TOC tree.
padding-left: calc((var(--indent-factor) - 1) * 10px + 5px); padding-left: calc((var(--indent-factor) - 1) * 0.714em + 0.357em);
text-indent: -5px; text-indent: -0.357em;
} }
} }
} }

View File

@ -3,11 +3,13 @@
import { CopyButton } from "@/app/element/copybutton"; import { CopyButton } from "@/app/element/copybutton";
import { createContentBlockPlugin } from "@/app/element/markdown-contentblock-plugin"; import { createContentBlockPlugin } from "@/app/element/markdown-contentblock-plugin";
import { MarkdownContentBlockType, transformBlocks } from "@/app/element/markdown-util"; import {
import { RpcApi } from "@/app/store/wshclientapi"; MarkdownContentBlockType,
import { TabRpcClient } from "@/app/store/wshrpcutil"; resolveRemoteFile,
import { getWebServerEndpoint } from "@/util/endpoints"; resolveSrcSet,
import { isBlank, makeConnRoute, useAtomValueSafe } from "@/util/util"; transformBlocks,
} from "@/app/element/markdown-util";
import { useAtomValueSafe } from "@/util/util";
import { clsx } from "clsx"; import { clsx } from "clsx";
import { Atom } from "jotai"; import { Atom } from "jotai";
import { OverlayScrollbarsComponent, OverlayScrollbarsComponentRef } from "overlayscrollbars-react"; import { OverlayScrollbarsComponent, OverlayScrollbarsComponentRef } from "overlayscrollbars-react";
@ -108,8 +110,34 @@ const CodeBlock = ({ children, onClickExecute }: CodeBlockProps) => {
); );
}; };
const MarkdownSource = (props: React.HTMLAttributes<HTMLSourceElement>) => { const MarkdownSource = ({
props,
resolveOpts,
}: {
props: React.HTMLAttributes<HTMLSourceElement> & {
srcSet?: string;
media?: string;
};
resolveOpts: MarkdownResolveOpts;
}) => {
const [resolvedSrcSet, setResolvedSrcSet] = useState<string>(props.srcSet);
const [resolving, setResolving] = useState<boolean>(true);
useEffect(() => {
const resolvePath = async () => {
const resolved = await resolveSrcSet(props.srcSet, resolveOpts);
setResolvedSrcSet(resolved);
setResolving(false);
};
resolvePath();
}, [props.srcSet]);
if (resolving) {
return null; return null;
}
return <source srcSet={resolvedSrcSet} media={props.media} />;
}; };
interface WaveBlockProps { interface WaveBlockProps {
@ -148,16 +176,11 @@ const MarkdownImg = ({
resolveOpts: MarkdownResolveOpts; resolveOpts: MarkdownResolveOpts;
}) => { }) => {
const [resolvedSrc, setResolvedSrc] = useState<string>(props.src); const [resolvedSrc, setResolvedSrc] = useState<string>(props.src);
const [resolvedSrcSet, setResolvedSrcSet] = useState<string>(props.srcSet);
const [resolvedStr, setResolvedStr] = useState<string>(null); const [resolvedStr, setResolvedStr] = useState<string>(null);
const [resolving, setResolving] = useState<boolean>(true); const [resolving, setResolving] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
if (props.src.startsWith("http://") || props.src.startsWith("https://")) {
setResolving(false);
setResolvedSrc(props.src);
setResolvedStr(null);
return;
}
if (props.src.startsWith("data:image/")) { if (props.src.startsWith("data:image/")) {
setResolving(false); setResolving(false);
setResolvedSrc(props.src); setResolvedSrc(props.src);
@ -170,23 +193,20 @@ const MarkdownImg = ({
setResolvedStr(`[img:${props.src}]`); setResolvedStr(`[img:${props.src}]`);
return; return;
} }
const resolveFn = async () => { const resolveFn = async () => {
const route = makeConnRoute(resolveOpts.connName); const [resolvedSrc, resolvedSrcSet] = await Promise.all([
const fileInfo = await RpcApi.RemoteFileJoinCommand(TabRpcClient, [resolveOpts.baseDir, props.src], { resolveRemoteFile(props.src, resolveOpts),
route: route, resolveSrcSet(props.srcSet, resolveOpts),
}); ]);
const usp = new URLSearchParams();
usp.set("path", fileInfo.path); setResolvedSrc(resolvedSrc);
if (!isBlank(resolveOpts.connName)) { setResolvedSrcSet(resolvedSrcSet);
usp.set("connection", resolveOpts.connName);
}
const streamingUrl = getWebServerEndpoint() + "/wave/stream-file?" + usp.toString();
setResolvedSrc(streamingUrl);
setResolvedStr(null); setResolvedStr(null);
setResolving(false); setResolving(false);
}; };
resolveFn(); resolveFn();
}, [props.src]); }, [props.src, props.srcSet]);
if (resolving) { if (resolving) {
return null; return null;
@ -195,7 +215,7 @@ const MarkdownImg = ({
return <span>{resolvedStr}</span>; return <span>{resolvedStr}</span>;
} }
if (resolvedSrc != null) { if (resolvedSrc != null) {
return <img {...props} src={resolvedSrc} />; return <img {...props} src={resolvedSrc} srcSet={resolvedSrcSet} />;
} }
return <span>[img]</span>; return <span>[img]</span>;
}; };
@ -210,6 +230,8 @@ type MarkdownProps = {
resolveOpts?: MarkdownResolveOpts; resolveOpts?: MarkdownResolveOpts;
scrollable?: boolean; scrollable?: boolean;
rehype?: boolean; rehype?: boolean;
fontSizeOverride?: number;
fixedFontSizeOverride?: number;
}; };
const Markdown = ({ const Markdown = ({
@ -219,6 +241,8 @@ const Markdown = ({
style, style,
className, className,
resolveOpts, resolveOpts,
fontSizeOverride,
fixedFontSizeOverride,
scrollable = true, scrollable = true,
rehype = true, rehype = true,
onClickExecute, onClickExecute,
@ -262,7 +286,9 @@ const Markdown = ({
h5: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={5} />, h5: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={5} />,
h6: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={6} />, h6: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={6} />,
img: (props: React.HTMLAttributes<HTMLImageElement>) => <MarkdownImg props={props} resolveOpts={resolveOpts} />, img: (props: React.HTMLAttributes<HTMLImageElement>) => <MarkdownImg props={props} resolveOpts={resolveOpts} />,
source: (props: React.HTMLAttributes<HTMLSourceElement>) => <MarkdownSource {...props} />, source: (props: React.HTMLAttributes<HTMLSourceElement>) => (
<MarkdownSource props={props} resolveOpts={resolveOpts} />
),
code: Code, code: Code,
pre: (props: React.HTMLAttributes<HTMLPreElement>) => ( pre: (props: React.HTMLAttributes<HTMLPreElement>) => (
<CodeBlock children={props.children} onClickExecute={onClickExecute} /> <CodeBlock children={props.children} onClickExecute={onClickExecute} />
@ -301,12 +327,15 @@ const Markdown = ({
...(defaultSchema.attributes?.span || []), ...(defaultSchema.attributes?.span || []),
// Allow all class names starting with `hljs-`. // Allow all class names starting with `hljs-`.
["className", /^hljs-./], ["className", /^hljs-./],
["srcset"],
["media"],
["type"],
// Alternatively, to allow only certain class names: // Alternatively, to allow only certain class names:
// ['className', 'hljs-number', 'hljs-title', 'hljs-variable'] // ['className', 'hljs-number', 'hljs-title', 'hljs-variable']
], ],
waveblock: [["blockkey"]], waveblock: [["blockkey"]],
}, },
tagNames: [...(defaultSchema.tagNames || []), "span", "waveblock"], tagNames: [...(defaultSchema.tagNames || []), "span", "waveblock", "picture", "source"],
}), }),
() => rehypeSlug({ prefix: idPrefix }), () => rehypeSlug({ prefix: idPrefix }),
]; ];
@ -349,8 +378,15 @@ const Markdown = ({
); );
}; };
const mergedStyle = { ...style };
if (fontSizeOverride != null) {
mergedStyle["--markdown-font-size"] = `${fontSizeOverride}px`;
}
if (fixedFontSizeOverride != null) {
mergedStyle["--markdown-fixed-font-size"] = `${fixedFontSizeOverride}px`;
}
return ( return (
<div className={clsx("markdown", className)} style={style}> <div className={clsx("markdown", className)} style={mergedStyle}>
{scrollable ? <ScrollableMarkdown /> : <NonScrollableMarkdown />} {scrollable ? <ScrollableMarkdown /> : <NonScrollableMarkdown />}
{toc && ( {toc && (
<OverlayScrollbarsComponent className="toc" options={{ scrollbars: { autoHide: "leave" } }}> <OverlayScrollbarsComponent className="toc" options={{ scrollbars: { autoHide: "leave" } }}>

View File

@ -1,8 +1,6 @@
// Copyright 2024, Command Line Inc. // Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Used for syntax highlighting in markdown
:root { :root {
--main-text-color: #f7f7f7; --main-text-color: #f7f7f7;
--title-font-size: 18px; --title-font-size: 18px;
@ -16,8 +14,10 @@
--accent-color: rgb(88, 193, 66); --accent-color: rgb(88, 193, 66);
--panel-bg-color: rgba(31, 33, 31, 0.5); --panel-bg-color: rgba(31, 33, 31, 0.5);
--highlight-bg-color: rgba(255, 255, 255, 0.2); --highlight-bg-color: rgba(255, 255, 255, 0.2);
--markdown-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, --markdown-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji"; "Apple Color Emoji", "Segoe UI Emoji";
--markdown-font-size: 14px;
--markdown-fixed-font-size: 12px;
--error-color: rgb(229, 77, 46); --error-color: rgb(229, 77, 46);
--warning-color: rgb(224, 185, 86); --warning-color: rgb(224, 185, 86);
--success-color: rgb(78, 154, 6); --success-color: rgb(78, 154, 6);

View File

@ -783,6 +783,8 @@ function makePreviewModel(blockId: string, nodeModel: BlockNodeModel): PreviewMo
function MarkdownPreview({ model }: SpecializedViewProps) { function MarkdownPreview({ model }: SpecializedViewProps) {
const connName = useAtomValue(model.connection); const connName = useAtomValue(model.connection);
const fileInfo = useAtomValue(model.statFile); const fileInfo = useAtomValue(model.statFile);
const fontSizeOverride = useAtomValue(getOverrideConfigAtom(model.blockId, "markdown:fontsize"));
const fixedFontSizeOverride = useAtomValue(getOverrideConfigAtom(model.blockId, "markdown:fixedfontsize"));
const resolveOpts: MarkdownResolveOpts = useMemo<MarkdownResolveOpts>(() => { const resolveOpts: MarkdownResolveOpts = useMemo<MarkdownResolveOpts>(() => {
return { return {
connName: connName, connName: connName,
@ -791,7 +793,13 @@ function MarkdownPreview({ model }: SpecializedViewProps) {
}, [connName, fileInfo.dir]); }, [connName, fileInfo.dir]);
return ( return (
<div className="view-preview view-preview-markdown"> <div className="view-preview view-preview-markdown">
<Markdown textAtom={model.fileContent} showTocAtom={model.markdownShowToc} resolveOpts={resolveOpts} /> <Markdown
textAtom={model.fileContent}
showTocAtom={model.markdownShowToc}
resolveOpts={resolveOpts}
fontSizeOverride={fontSizeOverride}
fixedFontSizeOverride={fixedFontSizeOverride}
/>
</div> </div>
); );
} }

View File

@ -131,9 +131,6 @@
outline: none; outline: none;
overflow: auto; overflow: auto;
overflow-wrap: anywhere; overflow-wrap: anywhere;
font-family: var(--termfontfamily);
font-weight: normal;
line-height: var(--termlineheight);
height: 21px; height: 21px;
} }
} }

View File

@ -569,7 +569,7 @@ const ChatWindow = memo(
interface ChatInputProps { interface ChatInputProps {
value: string; value: string;
termFontSize: number; baseFontSize: number;
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void; onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void; onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
onMouseDown: (e: React.MouseEvent<HTMLTextAreaElement>) => void; onMouseDown: (e: React.MouseEvent<HTMLTextAreaElement>) => void;
@ -577,7 +577,7 @@ interface ChatInputProps {
} }
const ChatInput = forwardRef<HTMLTextAreaElement, ChatInputProps>( const ChatInput = forwardRef<HTMLTextAreaElement, ChatInputProps>(
({ value, onChange, onKeyDown, onMouseDown, termFontSize, model }, ref) => { ({ value, onChange, onKeyDown, onMouseDown, baseFontSize, model }, ref) => {
const textAreaRef = useRef<HTMLTextAreaElement>(null); const textAreaRef = useRef<HTMLTextAreaElement>(null);
useImperativeHandle(ref, () => textAreaRef.current as HTMLTextAreaElement); useImperativeHandle(ref, () => textAreaRef.current as HTMLTextAreaElement);
@ -594,7 +594,7 @@ const ChatInput = forwardRef<HTMLTextAreaElement, ChatInputProps>(
// Adjust the height of the textarea to fit the text // Adjust the height of the textarea to fit the text
const textAreaMaxLines = 5; const textAreaMaxLines = 5;
const textAreaLineHeight = termFontSize * 1.5; const textAreaLineHeight = baseFontSize * 1.5;
const textAreaMinHeight = textAreaLineHeight; const textAreaMinHeight = textAreaLineHeight;
const textAreaMaxHeight = textAreaLineHeight * textAreaMaxLines; const textAreaMaxHeight = textAreaLineHeight * textAreaMaxLines;
@ -608,7 +608,7 @@ const ChatInput = forwardRef<HTMLTextAreaElement, ChatInputProps>(
const newHeight = Math.min(Math.max(scrollHeight, textAreaMinHeight), textAreaMaxHeight); const newHeight = Math.min(Math.max(scrollHeight, textAreaMinHeight), textAreaMaxHeight);
textAreaRef.current.style.height = newHeight + "px"; textAreaRef.current.style.height = newHeight + "px";
}, },
[termFontSize] [baseFontSize]
); );
useEffect(() => { useEffect(() => {
@ -624,7 +624,7 @@ const ChatInput = forwardRef<HTMLTextAreaElement, ChatInputProps>(
onMouseDown={onMouseDown} // When the user clicks on the textarea onMouseDown={onMouseDown} // When the user clicks on the textarea
onChange={onChange} onChange={onChange}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
style={{ fontSize: termFontSize }} style={{ fontSize: baseFontSize }}
placeholder="Ask anything..." placeholder="Ask anything..."
value={value} value={value}
></textarea> ></textarea>
@ -642,7 +642,7 @@ const WaveAi = ({ model }: { model: WaveAiModel; blockId: string }) => {
const [value, setValue] = useState(""); const [value, setValue] = useState("");
const [selectedBlockIdx, setSelectedBlockIdx] = useState<number | null>(null); const [selectedBlockIdx, setSelectedBlockIdx] = useState<number | null>(null);
const termFontSize: number = 14; const baseFontSize: number = 14;
const msgWidths = {}; const msgWidths = {};
const locked = useAtomValue(model.locked); const locked = useAtomValue(model.locked);
@ -815,7 +815,7 @@ const WaveAi = ({ model }: { model: WaveAiModel; blockId: string }) => {
onChange={handleTextAreaChange} onChange={handleTextAreaChange}
onKeyDown={handleTextAreaKeyDown} onKeyDown={handleTextAreaKeyDown}
onMouseDown={handleTextAreaMouseDown} onMouseDown={handleTextAreaMouseDown}
termFontSize={termFontSize} baseFontSize={baseFontSize}
/> />
</div> </div>
<Button className={buttonClass} onClick={handleButtonPress}> <Button className={buttonClass} onClick={handleButtonPress}>

View File

@ -490,6 +490,8 @@ declare global {
"term:vdomblockid"?: string; "term:vdomblockid"?: string;
"term:vdomtoolbarblockid"?: string; "term:vdomtoolbarblockid"?: string;
"web:zoom"?: number; "web:zoom"?: number;
"markdown:fontsize"?: number;
"markdown:fixedfontsize"?: number;
"vdom:*"?: boolean; "vdom:*"?: boolean;
"vdom:initialized"?: boolean; "vdom:initialized"?: boolean;
"vdom:correlationid"?: string; "vdom:correlationid"?: string;
@ -638,6 +640,8 @@ declare global {
"autoupdate:intervalms"?: number; "autoupdate:intervalms"?: number;
"autoupdate:installonquit"?: boolean; "autoupdate:installonquit"?: boolean;
"autoupdate:channel"?: string; "autoupdate:channel"?: string;
"markdown:fontsize"?: number;
"markdown:fixedfontsize"?: number;
"preview:showhiddenfiles"?: boolean; "preview:showhiddenfiles"?: boolean;
"tab:preset"?: string; "tab:preset"?: string;
"widget:*"?: boolean; "widget:*"?: boolean;

View File

@ -116,6 +116,7 @@
"overlayscrollbars": "^2.10.1", "overlayscrollbars": "^2.10.1",
"overlayscrollbars-react": "^0.5.6", "overlayscrollbars-react": "^0.5.6",
"papaparse": "^5.4.1", "papaparse": "^5.4.1",
"parse-srcset": "^1.0.2",
"pngjs": "^7.0.0", "pngjs": "^7.0.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "^18.3.1", "react": "^18.3.1",

View File

@ -96,6 +96,9 @@ const (
MetaKey_WebZoom = "web:zoom" MetaKey_WebZoom = "web:zoom"
MetaKey_MarkdownFontSize = "markdown:fontsize"
MetaKey_MarkdownFixedFontSize = "markdown:fixedfontsize"
MetaKey_VDomClear = "vdom:*" MetaKey_VDomClear = "vdom:*"
MetaKey_VDomInitialized = "vdom:initialized" MetaKey_VDomInitialized = "vdom:initialized"
MetaKey_VDomCorrelationId = "vdom:correlationid" MetaKey_VDomCorrelationId = "vdom:correlationid"

View File

@ -97,6 +97,9 @@ type MetaTSType struct {
WebZoom float64 `json:"web:zoom,omitempty"` WebZoom float64 `json:"web:zoom,omitempty"`
MarkdownFontSize float64 `json:"markdown:fontsize,omitempty"`
MarkdownFixedFontSize float64 `json:"markdown:fixedfontsize,omitempty"`
VDomClear bool `json:"vdom:*,omitempty"` VDomClear bool `json:"vdom:*,omitempty"`
VDomInitialized bool `json:"vdom:initialized,omitempty"` VDomInitialized bool `json:"vdom:initialized,omitempty"`
VDomCorrelationId string `json:"vdom:correlationid,omitempty"` VDomCorrelationId string `json:"vdom:correlationid,omitempty"`

View File

@ -46,6 +46,9 @@ const (
ConfigKey_AutoUpdateInstallOnQuit = "autoupdate:installonquit" ConfigKey_AutoUpdateInstallOnQuit = "autoupdate:installonquit"
ConfigKey_AutoUpdateChannel = "autoupdate:channel" ConfigKey_AutoUpdateChannel = "autoupdate:channel"
ConfigKey_MarkdownFontSize = "markdown:fontsize"
ConfigKey_MarkdownFixedFontSize = "markdown:fixedfontsize"
ConfigKey_PreviewShowHiddenFiles = "preview:showhiddenfiles" ConfigKey_PreviewShowHiddenFiles = "preview:showhiddenfiles"
ConfigKey_TabPreset = "tab:preset" ConfigKey_TabPreset = "tab:preset"

View File

@ -73,6 +73,9 @@ type SettingsType struct {
AutoUpdateInstallOnQuit bool `json:"autoupdate:installonquit,omitempty"` AutoUpdateInstallOnQuit bool `json:"autoupdate:installonquit,omitempty"`
AutoUpdateChannel string `json:"autoupdate:channel,omitempty"` AutoUpdateChannel string `json:"autoupdate:channel,omitempty"`
MarkdownFontSize float64 `json:"markdown:fontsize,omitempty"`
MarkdownFixedFontSize float64 `json:"markdown:fixedfontsize,omitempty"`
PreviewShowHiddenFiles *bool `json:"preview:showhiddenfiles,omitempty"` PreviewShowHiddenFiles *bool `json:"preview:showhiddenfiles,omitempty"`
TabPreset string `json:"tab:preset,omitempty"` TabPreset string `json:"tab:preset,omitempty"`

View File

@ -16290,6 +16290,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"parse-srcset@npm:^1.0.2":
version: 1.0.2
resolution: "parse-srcset@npm:1.0.2"
checksum: 10c0/2f268e3d110d4c53d06ed2a8e8ee61a7da0cee13bf150819a6da066a8ca9b8d15b5600d6e6cae8be940e2edc50ee7c1e1052934d6ec858324065ecef848f0497
languageName: node
linkType: hard
"parse5-htmlparser2-tree-adapter@npm:^7.0.0": "parse5-htmlparser2-tree-adapter@npm:^7.0.0":
version: 7.1.0 version: 7.1.0
resolution: "parse5-htmlparser2-tree-adapter@npm:7.1.0" resolution: "parse5-htmlparser2-tree-adapter@npm:7.1.0"
@ -22003,6 +22010,7 @@ __metadata:
overlayscrollbars: "npm:^2.10.1" overlayscrollbars: "npm:^2.10.1"
overlayscrollbars-react: "npm:^0.5.6" overlayscrollbars-react: "npm:^0.5.6"
papaparse: "npm:^5.4.1" papaparse: "npm:^5.4.1"
parse-srcset: "npm:^1.0.2"
pngjs: "npm:^7.0.0" pngjs: "npm:^7.0.0"
prettier: "npm:^3.4.2" prettier: "npm:^3.4.2"
prettier-plugin-jsdoc: "npm:^1.3.0" prettier-plugin-jsdoc: "npm:^1.3.0"