Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
"upgrade-z3": "../scripts/upgrade-z3.sh"
},
"dependencies": {
"@docusaurus/core": "^3.7.0",
"@docusaurus/preset-classic": "^3.7.0",
"@docusaurus/theme-live-codeblock": "^3.7.0",
"@docusaurus/core": "^3.10.1",
"@docusaurus/preset-classic": "^3.10.1",
"@docusaurus/theme-live-codeblock": "^3.10.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@mdx-js/react": "^3.1.0",
Expand All @@ -45,22 +45,26 @@
"react-loadable": "^5.5.0",
"rehype-katex": "7",
"remark-math": "6",
"semantic-release": "^24.2.3",
"semantic-release": "^25.0.3",
"use-editable": "^2.3.3",
"z3-solver": "4.15.1"
},
"devDependencies": {
"@docusaurus/eslint-plugin": "3.7.0",
"@docusaurus/module-type-aliases": "3.7.0",
"@tsconfig/docusaurus": "^2.0.3",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"eslint": "^9.22.0",
"eslint-plugin-react": "^7.37.4",
"@docusaurus/eslint-plugin": "3.10.1",
"@docusaurus/module-type-aliases": "3.10.1",
"@tsconfig/docusaurus": "^2.0.9",
"@typescript-eslint/eslint-plugin": "^8.61.0",
"@typescript-eslint/parser": "^8.61.0",
"eslint": "^9.39.4",
"eslint-plugin-react": "^7.37.5",
"ignore-loader": "^0.1.2",
"ts-loader": "^9.5.2",
"typescript": "^5.8.2"
},
"resolutions": {
"serialize-javascript": "7.0.3",
"uuid": "11.1.1"
},
"browserslist": {
"production": [
">0.5%",
Expand Down
24 changes: 24 additions & 0 deletions website/src/components/HomepageFeatures/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import clsx from 'clsx';
import styles from './styles.module.css';
const FeatureList = [];
function Feature({ title, Svg, description }) {
return (<div className={clsx('col col--4')}>
<div className="text--center">
<Svg className={styles.featureSvg} role="img"/>
</div>
<div className="text--center padding-horiz--md">
<h3>{title}</h3>
<p>{description}</p>
</div>
</div>);
}
export default function HomepageFeatures() {
return (<section className={styles.features}>
<div className="container">
<div className="row">
{FeatureList.map((props, idx) => (<Feature key={idx} {...props}/>))}
</div>
</div>
</section>);
}
165 changes: 165 additions & 0 deletions website/src/components/TutorialComponents/CodeBlock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React, { useEffect, useState, useRef, useCallback, } from "react";
import clsx from "clsx";
import { useEditable } from "use-editable";
import codeBlockContentStyles from "@docusaurus/theme-classic/src/theme/CodeBlock/Content/styles.module.css";
import CopyButton from "./CopyButton";
import { Highlight, Prism } from "prism-react-renderer";
import styles from "./styles.module.css";
export function GithubDiscussionBtn(props) {
const { repo } = props;
const openInNewTab = () => {
const url = `https://github.com/${repo}/discussions`;
window.open(url, "_blank");
};
return (<button type="button" aria-label="Go to GitHub discussion" title="Go to GitHub discussion" className={clsx("clean-btn", codeBlockContentStyles.codeButton)} onClick={openInNewTab}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-chat-left" viewBox="0 0 16 16">
<path d="M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H4.414A2 2 0 0 0 3 11.586l-2 2V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12.793a.5.5 0 0 0 .854.353l2.853-2.853A1 1 0 0 1 4.414 12H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
</svg>
</button>);
}
export function ResetBtn(props) {
const { resetCode } = props;
return (<button type="button" aria-label="Reset code" title="Reset code" className={clsx("clean-btn", codeBlockContentStyles.codeButton)} onClick={resetCode}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fillRule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
</svg>
</button>);
}
export function UndoBtn(props) {
const { undoCode } = props;
return (<button type="button" aria-label="Undo the reset" title="Undo the reset" className={clsx("clean-btn", codeBlockContentStyles.codeButton)} style={{ borderColor: "var(--custom-editor-reset-color)" }} onClick={undoCode}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="var(--custom-editor-reset-color)" strokeWidth="3" className="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fillRule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
</svg>
</button>);
}
// source code of LiveEditor that allows for code editing
// a good starting point for customizing our own code editor
function CodeEditor(props) {
const editorRef = useRef(null);
const [code, setCode] = useState(props.code || "");
// const [disabled, setDisabled] = useState(props.disabled);
const [allowUndo, setAllowUndo] = useState(false);
const [tmpCode, setTmpCode] = useState("");
const [hasFocus, setHasFocus] = useState(false);
useEffect(() => {
setCode(props.code);
}, [props.code]);
const onEditableChange = useCallback((_code) => {
setCode(_code.slice(0, -1));
}, []);
const disabled = props.disabled || !hasFocus;
// we might use `editObj` later for fixing cursor position
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const editObj = useEditable(editorRef, onEditableChange, {
disabled: disabled,
indentation: 2,
});
useEffect(() => {
if (props.onChange) {
props.onChange(code);
}
}, [code]);
const onClickReset = () => {
setTmpCode(code.slice()); // use copy not reference
setCode(props.code);
// setDisabled(true);
setHasFocus(false);
setAllowUndo(true);
setTimeout(() => {
// setDisabled(props.disabled);
setHasFocus(true);
setAllowUndo(false);
}, 3000);
};
const onClickUndo = () => {
setCode(tmpCode);
};
const handleFocus = () => {
const selectObj = window.getSelection();
if (selectObj.rangeCount === 0) {
// when focusing on the editor without using the mouse,
// merely from the Tab key
const range = new Range();
range.collapse(true);
selectObj.addRange(range);
}
setHasFocus(true);
};
const handleBlur = () => {
setHasFocus(false);
};
const handleKeyDown = (e) => {
const editor = editorRef.current;
if (e.key === "Escape" && !props.disabled) {
if (e.target === editor) {
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
editor.focus(); // sometimes blur won't fire without focus first
editor.blur();
}
}
};
const handleKeyDownIfEnabled = useCallback((_e) => {
if (!disabled) {
handleKeyDown(_e);
}
}, [disabled]);
useEffect(() => {
document.addEventListener("keydown", handleKeyDownIfEnabled, {
capture: true,
});
return () => {
document.removeEventListener("keydown", handleKeyDownIfEnabled);
};
}, [handleKeyDownIfEnabled]);
return (<div className={props.className} style={props.style}>
<Highlight Prism={props.prism || Prism} code={code} theme={props.theme} language={props.language}>
{({ className: _className, tokens, getLineProps, getTokenProps, style: _style, }) => (<div className={clsx(styles.CustomCodeEditorContent, codeBlockContentStyles.codeBlockContent)}>
{props.showLineNumbers && (<span className={clsx(styles.LineNumber,
// codeBlockLineNumberStyles.codeLineNumber,
codeBlockContentStyles.codeBlockLines)}>
{tokens.map((line, i) => (<>
{i + 1}
<br />
</>))}
</span>)}
<pre tabIndex={0} className={clsx(_className, styles.codeBlock, "thin-scrollbar")} style={{
padding: "0",
...(!props.className || !props.theme
? {}
: _style),
}} ref={editorRef} spellCheck="false" onFocus={handleFocus} onBlur={handleBlur}>
<code className={clsx(codeBlockContentStyles.codeBlockLines)}>
{tokens.map((line, lineIndex) => (
// eslint-disable-next-line react/jsx-key
<div {...getLineProps({
line,
key: `line-${lineIndex}`,
})}>
{line
.filter((token) => !token.empty)
.map((token, tokenIndex) => (
// eslint-disable-next-line react/jsx-key
<span {...getTokenProps({
token,
key: `token-${tokenIndex}`,
})}/>))}
{"\n"}
</div>))}
</code>
</pre>
</div>)}
</Highlight>
<div className={codeBlockContentStyles.buttonGroup}>
<CopyButton className={codeBlockContentStyles.codeButton} code={code}/>
{!props.readonly && !allowUndo && (<ResetBtn resetCode={onClickReset}/>)}
{!props.readonly && allowUndo && (<UndoBtn undoCode={onClickUndo}/>)}
{props.githubRepo && (<GithubDiscussionBtn repo={props.githubRepo}/>)}
</div>
</div>);
}
export default CodeEditor;
2 changes: 1 addition & 1 deletion website/src/components/TutorialComponents/CodeBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React, {
import clsx from "clsx";
import { useEditable } from "use-editable";
import codeBlockContentStyles from "@docusaurus/theme-classic/src/theme/CodeBlock/Content/styles.module.css";
import CopyButton from "@theme/CodeBlock/CopyButton";
import CopyButton from "./CopyButton";
import { Highlight, Prism, Language, PrismTheme } from "prism-react-renderer";
import styles from "./styles.module.css";

Expand Down
85 changes: 85 additions & 0 deletions website/src/components/TutorialComponents/CodeEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { useState } from "react";
import clsx from "clsx";
import Editor, { useMonaco } from "@monaco-editor/react";
import codeBlockContentStyles from '@docusaurus/theme-classic/src/theme/CodeBlock/Content/styles.module.css';
import CopyButton from "./CopyButton";
import { useEffect } from "react";
export function GithubDiscussionBtn(props) {
const { repo } = props;
const openInNewTab = () => {
const url = `https://github.com/${repo}/discussions`;
window.open(url, '_blank');
};
return (<button type="button" aria-label="Go to GitHub discussion" title="Go to GitHub discussion" className={clsx('clean-btn', codeBlockContentStyles.codeButton)} onClick={openInNewTab}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-chat-left" viewBox="0 0 16 16">
<path d="M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H4.414A2 2 0 0 0 3 11.586l-2 2V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12.793a.5.5 0 0 0 .854.353l2.853-2.853A1 1 0 0 1 4.414 12H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
</svg>
</button>);
}
export function ResetBtn(props) {
const { resetCode } = props;
return (<button type="button" aria-label="Reset code" title="Reset code" className={clsx('clean-btn', codeBlockContentStyles.codeButton)} onClick={resetCode}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fillRule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466z"/>
</svg>
</button>);
}
export function UndoBtn(props) {
const { undoCode } = props;
return (<button type="button" aria-label="Undo the reset" title="Undo the reset" className={clsx('clean-btn', codeBlockContentStyles.codeButton)} style={{ borderColor: "var(--custom-editor-reset-color)" }} onClick={undoCode}>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="var(--custom-editor-reset-color)" strokeWidth="3" className="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fillRule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
</svg>
</button>);
}
export function CodeEditor(props) {
const [code, setCode] = useState(props.code);
const [allowUndo, setAllowUndo] = useState(false);
const [tmpCode, setTmpCode] = useState("");
const disabled = props.disabled;
const handleEditorChange = (value) => {
setCode(value);
};
const monaco = useMonaco();
useEffect(() => {
if (monaco) {
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
});
}
}, [monaco]);
useEffect(() => {
if (props.onChange) {
props.onChange(code);
}
}, [code]);
const onClickReset = () => {
setTmpCode(code.slice()); // use copy not reference
setCode(props.code);
setAllowUndo(true);
setTimeout(() => {
setAllowUndo(false);
}, 3000);
};
const onClickUndo = () => {
setCode(tmpCode);
};
const options = {
readOnly: disabled,
minimap: { enabled: false },
fontSize: '15px'
};
const numLines = code.split('\n').length;
const height = `${numLines + 4}em`;
return (<div className={props.className} style={props.style}>
<Editor height={height} language={props.lang} value={code} onChange={handleEditorChange} options={options}/>
<div className={codeBlockContentStyles.buttonGroup}>
<CopyButton className={codeBlockContentStyles.codeButton} code={code}/>
{!props.readonly && !allowUndo && <ResetBtn resetCode={onClickReset}/>}
{!props.readonly && allowUndo && <UndoBtn undoCode={onClickUndo}/>}
{props.githubRepo && <GithubDiscussionBtn repo={props.githubRepo}/>}
</div>
</div>);
}
2 changes: 1 addition & 1 deletion website/src/components/TutorialComponents/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useRef, useState } from "react";
import clsx from "clsx";
import Editor, { useMonaco } from "@monaco-editor/react";
import codeBlockContentStyles from '@docusaurus/theme-classic/src/theme/CodeBlock/Content/styles.module.css';
import CopyButton from '@theme/CodeBlock/CopyButton';
import CopyButton from "./CopyButton";
import { useEffect } from "react";


Expand Down
32 changes: 32 additions & 0 deletions website/src/components/TutorialComponents/CopyButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import clsx from "clsx";
export default function CopyButton(props) {
const { className, code } = props;
const [isCopied, setIsCopied] = useState(false);
const timeoutRef = useRef(undefined);
const onClick = useCallback(async () => {
if (navigator.clipboard) {
await navigator.clipboard.writeText(code);
}
else {
const textarea = document.createElement("textarea");
textarea.value = code;
textarea.setAttribute("readonly", "");
textarea.style.position = "absolute";
textarea.style.left = "-9999px";
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
}
setIsCopied(true);
window.clearTimeout(timeoutRef.current);
timeoutRef.current = window.setTimeout(() => setIsCopied(false), 1000);
}, [code]);
useEffect(() => () => window.clearTimeout(timeoutRef.current), []);
return (<button type="button" aria-label={isCopied ? "Copied" : "Copy code to clipboard"} title={isCopied ? "Copied" : "Copy"} className={clsx("clean-btn", className)} onClick={() => {
void onClick();
}}>
{isCopied ? "Copied" : "Copy"}
</button>);
}
Loading