Edit workspace files directly on web (#2270)
* feat(files): edit workspace files on web Keep source buffers synchronized with host file changes and require an explicit overwrite or reload when revisions diverge. * feat(panels): surface and protect modified tabs Expose tooltip and modification state through the generic panel boundary so tabs can show stable metadata and guard every close route consistently. * fix(tests): use portable fake timeout handle * fix(files): harden editor conflict handling Preserve modified panel state across tab eviction, use precise revisions for optimistic writes, coalesce concurrent file watchers, and localize the editor interface. * fix(files): close editor concurrency gaps Coalesce clean reloads, preserve subscriber identities and file permissions, suspend pending saves during close confirmation, and carry precise revisions through file reads. * test(files): expect read revision metadata
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
||||
import {
|
||||
bracketMatching,
|
||||
defaultHighlightStyle,
|
||||
indentOnInput,
|
||||
syntaxHighlighting,
|
||||
} from "@codemirror/language";
|
||||
import { searchKeymap } from "@codemirror/search";
|
||||
import {
|
||||
EditorView,
|
||||
drawSelection,
|
||||
highlightActiveLine,
|
||||
keymap,
|
||||
lineNumbers,
|
||||
} from "@codemirror/view";
|
||||
import { createCodeMirrorHighlightStyle, type HighlightStyle } from "@getpaseo/highlight";
|
||||
|
||||
export interface EditorVisualTheme {
|
||||
colorScheme: "light" | "dark";
|
||||
background: string;
|
||||
foreground: string;
|
||||
cursor: string;
|
||||
foregroundMuted: string;
|
||||
border: string;
|
||||
selection: string;
|
||||
monoFont: string;
|
||||
codeFontSize: number;
|
||||
syntax: Record<HighlightStyle, string>;
|
||||
}
|
||||
|
||||
export function editorBaseExtensions(onSave: () => void) {
|
||||
return [
|
||||
lineNumbers(),
|
||||
history(),
|
||||
drawSelection(),
|
||||
indentOnInput(),
|
||||
bracketMatching(),
|
||||
highlightActiveLine(),
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
keymap.of([
|
||||
{ key: "Mod-s", preventDefault: true, run: () => (onSave(), true) },
|
||||
indentWithTab,
|
||||
...defaultKeymap,
|
||||
...historyKeymap,
|
||||
...searchKeymap,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
export function editorTheme(theme: EditorVisualTheme) {
|
||||
return [
|
||||
EditorView.theme(
|
||||
{
|
||||
"&": {
|
||||
height: "100%",
|
||||
backgroundColor: theme.background,
|
||||
color: theme.foreground,
|
||||
fontFamily: theme.monoFont,
|
||||
fontSize: `${theme.codeFontSize}px`,
|
||||
},
|
||||
".cm-scroller": {
|
||||
overflow: "auto",
|
||||
fontFamily: theme.monoFont,
|
||||
lineHeight: "1.45",
|
||||
},
|
||||
".cm-content": { caretColor: theme.foreground, padding: "16px 0" },
|
||||
".cm-cursor, .cm-dropCursor": { borderLeftColor: theme.cursor },
|
||||
".cm-gutters": {
|
||||
backgroundColor: theme.background,
|
||||
color: theme.foregroundMuted,
|
||||
borderRight: `1px solid ${theme.border}`,
|
||||
},
|
||||
".cm-activeLine": { backgroundColor: "transparent" },
|
||||
".cm-activeLineGutter": { backgroundColor: "transparent", color: theme.foreground },
|
||||
"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
||||
backgroundColor: theme.selection,
|
||||
},
|
||||
".cm-selectionBackground, ::selection": {
|
||||
backgroundColor: theme.selection,
|
||||
},
|
||||
"&.cm-focused": { outline: "none" },
|
||||
},
|
||||
{ dark: theme.colorScheme === "dark" },
|
||||
),
|
||||
syntaxHighlighting(createCodeMirrorHighlightStyle(theme.syntax)),
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user