diff --git a/src/components/CodeEmbed/index.jsx b/src/components/CodeEmbed/index.jsx index d0b39fe557..5941f3d597 100644 --- a/src/components/CodeEmbed/index.jsx +++ b/src/components/CodeEmbed/index.jsx @@ -1,9 +1,13 @@ import { useState, useEffect, useRef } from "preact/hooks"; -import { useLiveRegion } from '../hooks/useLiveRegion'; +import { useLiveRegion } from "../hooks/useLiveRegion"; import CodeMirror, { EditorView } from "@uiw/react-codemirror"; import { javascript } from "@codemirror/lang-javascript"; import { cdnLibraryUrl, cdnSoundUrl } from "@/src/globals/globals"; +import { keymap } from "@codemirror/view"; +import { Prec , EditorState } from "@codemirror/state"; +import { insertTab } from "@codemirror/commands"; + import { CodeFrame } from "./frame"; import { CopyCodeButton } from "../CopyCodeButton"; import CircleButton from "../CircleButton"; @@ -26,6 +30,7 @@ import { Icon } from "../Icon"; * } */ export const CodeEmbed = (props) => { + const [enableTabIndent, setEnableTabIndent] = useState(false); const { ref: liveRegionRef, announce } = useLiveRegion(); const [rendered, setRendered] = useState(false); const initialCode = props.initialValue ?? ""; @@ -38,7 +43,10 @@ export const CodeEmbed = (props) => { ); let { previewWidth, previewHeight } = props; - const canvasMatch = /createCanvas\(\s*(\d+),\s*(\d+)\s*(?:,\s*(?:P2D|WEBGL)\s*)?\)/m.exec(initialCode); + const canvasMatch = + /createCanvas\(\s*(\d+),\s*(\d+)\s*(?:,\s*(?:P2D|WEBGL)\s*)?\)/m.exec( + initialCode, + ); if (canvasMatch) { previewWidth = previewWidth || parseFloat(canvasMatch[1]); previewHeight = previewHeight || parseFloat(canvasMatch[2]); @@ -47,7 +55,9 @@ export const CodeEmbed = (props) => { const largeSketch = previewWidth && previewWidth > 770 - 60; // Quick hack to make room for DOM that gets added below the canvas by default - const domMatch = /create(Button|Select|P|Div|Input|ColorPicker)/.exec(initialCode); + const domMatch = /create(Button|Select|P|Div|Input|ColorPicker)/.exec( + initialCode, + ); if (domMatch && previewHeight) { previewHeight += 100; } @@ -80,13 +90,23 @@ export const CodeEmbed = (props) => { if (!rendered) return
; + const escToBlur = EditorView.domEventHandlers({ + keydown(event, view) { + if (event.key === "Escape") { + view.contentDOM.blur(); + return true; + } + return false; + }, + }); + return (
{props.previewable ? (
{ scripts={props.includeSound ? [cdnSoundUrl] : []} />
-
+
{
) : null} + +
{ foldGutter: false, autocompletion: false, }} - indentWithTab={false} - extensions={[javascript(), EditorView.lineWrapping]} + extensions={[ + javascript(), + EditorView.lineWrapping, + EditorState.tabSize.of(4), + + // When tab indentation is disabled, blur the editor to avoid a keyboard trap. + // CodeMirror runs in a contenteditable element, so native Tab navigation + // cannot resume without explicitly leaving the editor. + + Prec.high( + keymap.of([ + { + key: "Tab", + run: (view) => { + if (!enableTabIndent) { + // Exit editor to avoid keyboard trap + view.contentDOM.blur(); + return true; + } + + // Tab indentation mode + return insertTab(view); + }, + }, + ]), + ), + + escToBlur, + ]} onChange={(val) => setCodeString(val)} editable={props.editable} onCreateEditor={(editorView) => diff --git a/src/scripts/builders/reference.ts b/src/scripts/builders/reference.ts index 5d785ee9a1..eea2e1349d 100644 --- a/src/scripts/builders/reference.ts +++ b/src/scripts/builders/reference.ts @@ -292,7 +292,12 @@ const convertToMDX = async ( try { // Add YAML comment to the frontmatter - const comment = `# This file was auto-generated. Please do not edit it manually!\n# To make changes, edit the comments in the corresponding source file:\n# https://github.com/processing/p5.js/blob/v${p5Version}/${doc.file.replace(/\\/g, '/')}#L${doc.line}`; + const repo = + doc.module === "p5.sound" + ? "processing/p5.sound" + : "processing/p5.js"; + + const comment = `# This file was auto-generated. Please do not edit it manually!\n# To make changes, edit the comments in the corresponding source file:\n# https://github.com/${repo}/blob/v${p5Version}/${doc.file.replace(/\\/g, '/')}#L${doc.line}`; // Convert the frontmatter to a string const frontmatter = matter.stringify("", frontMatterArgs);