From 8d67304482967c4d656b22dce2e4fb9b0e87f364 Mon Sep 17 00:00:00 2001 From: slate20 Date: Sun, 19 Oct 2025 02:34:29 -0500 Subject: [PATCH 1/2] Fix Monaco editor syntax highlighting in Preview block The Preview block's CodeEditPreview component was not passing file information to Monaco editor, preventing automatic language detection from file extensions. Changes: - Add fileName prop to CodeEditor component - Pass actual file path to Monaco instead of random UUID - Monaco now auto-detects language from file extension (.js, .py, .ts, etc.) Fixes syntax highlighting for all text files opened in Preview blocks while maintaining backward compatibility with existing CodeEditor usage. --- frontend/app/view/codeeditor/codeeditor.tsx | 5 +++-- frontend/app/view/preview/preview-edit.tsx | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/app/view/codeeditor/codeeditor.tsx b/frontend/app/view/codeeditor/codeeditor.tsx index 3f278a6825..971fb4db4f 100644 --- a/frontend/app/view/codeeditor/codeeditor.tsx +++ b/frontend/app/view/codeeditor/codeeditor.tsx @@ -108,11 +108,12 @@ interface CodeEditorProps { text: string; readonly: boolean; language?: string; + fileName?: string; onChange?: (text: string) => void; onMount?: (monacoPtr: MonacoTypes.editor.IStandaloneCodeEditor, monaco: Monaco) => () => void; } -export function CodeEditor({ blockId, text, language, readonly, onChange, onMount }: CodeEditorProps) { +export function CodeEditor({ blockId, text, language, fileName, readonly, onChange, onMount }: CodeEditorProps) { const divRef = useRef(null); const unmountRef = useRef<() => void>(null); const minimapEnabled = useOverrideConfigAtom(blockId, "editor:minimapenabled") ?? false; @@ -120,7 +121,7 @@ export function CodeEditor({ blockId, text, language, readonly, onChange, onMoun const wordWrap = useOverrideConfigAtom(blockId, "editor:wordwrap") ?? false; const fontSize = boundNumber(useOverrideConfigAtom(blockId, "editor:fontsize"), 6, 64); const theme = "wave-theme-dark"; - const editorPath = useRef(crypto.randomUUID()).current; + const editorPath = useRef(fileName ?? crypto.randomUUID()).current; React.useEffect(() => { return () => { diff --git a/frontend/app/view/preview/preview-edit.tsx b/frontend/app/view/preview/preview-edit.tsx index f249f7ee7f..10a55d604c 100644 --- a/frontend/app/view/preview/preview-edit.tsx +++ b/frontend/app/view/preview/preview-edit.tsx @@ -16,6 +16,7 @@ function CodeEditPreview({ model }: SpecializedViewProps) { const fileContent = useAtomValue(model.fileContent); const setNewFileContent = useSetAtom(model.newFileContent); const fileInfo = useAtomValue(model.statFile); + const fileName = fileInfo?.path || fileInfo?.name; function codeEditKeyDownHandler(e: WaveKeyboardEvent): boolean { if (checkKeyPressed(e, "Cmd:e")) { @@ -65,6 +66,7 @@ function CodeEditPreview({ model }: SpecializedViewProps) { setNewFileContent(text)} onMount={onMount} From 8cd3bd2e9f3218c1985699bdb6de5a68a9f362c1 Mon Sep 17 00:00:00 2001 From: sawka Date: Mon, 20 Oct 2025 17:23:20 -0700 Subject: [PATCH 2/2] handle common shell files (bashrc, zshrc, etc.) also, add blockid to filename to make sure it is unique (same filename on two different servers). --- .../app/onboarding/onboarding-command.tsx | 11 ++++++-- frontend/app/onboarding/onboarding-layout.tsx | 22 ++++++++++++++-- frontend/app/view/codeeditor/codeeditor.tsx | 9 ++++++- frontend/app/view/preview/preview-edit.tsx | 25 +++++++++++++++++++ 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/frontend/app/onboarding/onboarding-command.tsx b/frontend/app/onboarding/onboarding-command.tsx index 783f544e51..d98065dbf6 100644 --- a/frontend/app/onboarding/onboarding-command.tsx +++ b/frontend/app/onboarding/onboarding-command.tsx @@ -1,7 +1,7 @@ // Copyright 2025, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 -import { useCallback, useLayoutEffect, useState } from "react"; +import { useCallback, useLayoutEffect, useRef, useState } from "react"; import { FakeBlock } from "./onboarding-layout"; import waveLogo from "/logos/wave-logo.png"; @@ -114,6 +114,7 @@ export const ViewLogoCommand = ({ onComplete }: { onComplete?: () => void }) => }; export const EditBashrcCommand = ({ onComplete }: { onComplete?: () => void }) => { + const fileNameRef = useRef(`${crypto.randomUUID()}/.bashrc`); const bashrcContent = `# Aliases alias ll="ls -lah" alias gst="git status" @@ -127,7 +128,13 @@ export PATH="$HOME/.local/bin:$PATH"`; return ( - + ); }; diff --git a/frontend/app/onboarding/onboarding-layout.tsx b/frontend/app/onboarding/onboarding-layout.tsx index 057aef0fc2..bbe2aece5f 100644 --- a/frontend/app/onboarding/onboarding-layout.tsx +++ b/frontend/app/onboarding/onboarding-layout.tsx @@ -15,9 +15,21 @@ export type FakeBlockProps = { markdown?: string; imgsrc?: string; editorText?: string; + editorFileName?: string; + editorLanguage?: string; }; -export const FakeBlock = ({ icon, name, highlighted, className, markdown, imgsrc, editorText }: FakeBlockProps) => { +export const FakeBlock = ({ + icon, + name, + highlighted, + className, + markdown, + imgsrc, + editorText, + editorFileName, + editorLanguage, +}: FakeBlockProps) => { return (
{editorText ? (
- +
) : imgsrc ? ( {name} diff --git a/frontend/app/view/codeeditor/codeeditor.tsx b/frontend/app/view/codeeditor/codeeditor.tsx index 971fb4db4f..13c810d6a0 100644 --- a/frontend/app/view/codeeditor/codeeditor.tsx +++ b/frontend/app/view/codeeditor/codeeditor.tsx @@ -121,7 +121,14 @@ export function CodeEditor({ blockId, text, language, fileName, readonly, onChan const wordWrap = useOverrideConfigAtom(blockId, "editor:wordwrap") ?? false; const fontSize = boundNumber(useOverrideConfigAtom(blockId, "editor:fontsize"), 6, 64); const theme = "wave-theme-dark"; - const editorPath = useRef(fileName ?? crypto.randomUUID()).current; + const uuidRef = useRef(crypto.randomUUID()).current; + let editorPath: string; + if (fileName) { + const separator = fileName.startsWith("/") ? "" : "/"; + editorPath = blockId + separator + fileName; + } else { + editorPath = uuidRef; + } React.useEffect(() => { return () => { diff --git a/frontend/app/view/preview/preview-edit.tsx b/frontend/app/view/preview/preview-edit.tsx index 10a55d604c..a7ec656cc2 100644 --- a/frontend/app/view/preview/preview-edit.tsx +++ b/frontend/app/view/preview/preview-edit.tsx @@ -12,12 +12,36 @@ import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api"; import { useEffect } from "react"; import type { SpecializedViewProps } from "./preview"; +export const shellFileMap: Record = { + ".bashrc": "shell", + ".bash_profile": "shell", + ".bash_login": "shell", + ".bash_logout": "shell", + ".profile": "shell", + ".zshrc": "shell", + ".zprofile": "shell", + ".zlogin": "shell", + ".zlogout": "shell", + ".kshrc": "shell", + ".cshrc": "shell", + ".tcshrc": "shell", + ".xonshrc": "python", + ".shrc": "shell", + ".aliases": "shell", + ".functions": "shell", + ".exports": "shell", + ".direnvrc": "shell", +}; + function CodeEditPreview({ model }: SpecializedViewProps) { const fileContent = useAtomValue(model.fileContent); const setNewFileContent = useSetAtom(model.newFileContent); const fileInfo = useAtomValue(model.statFile); const fileName = fileInfo?.path || fileInfo?.name; + const baseName = fileName ? fileName.split("/").pop() : null; + const language = baseName && shellFileMap[baseName] ? shellFileMap[baseName] : undefined; + function codeEditKeyDownHandler(e: WaveKeyboardEvent): boolean { if (checkKeyPressed(e, "Cmd:e")) { fireAndForget(() => model.setEditMode(false)); @@ -67,6 +91,7 @@ function CodeEditPreview({ model }: SpecializedViewProps) { blockId={model.blockId} text={fileContent} fileName={fileName} + language={language} readonly={fileInfo.readonly} onChange={(text) => setNewFileContent(text)} onMount={onMount}