Add basic multi-view support

This commit is contained in:
Quinten Kock 2025-10-20 18:02:37 +02:00
parent 1d31225dad
commit 001d215b0e
4 changed files with 123 additions and 12 deletions

View File

@ -1,7 +1,10 @@
import { basicSetup } from "codemirror";
import { EditorView } from "@codemirror/view";
import { Transaction } from "@codemirror/state";
import { EditorView, keymap } from "@codemirror/view";
import { defaultKeymap, undo, redo } from "@codemirror/commands";
import { oneDark } from "@codemirror/theme-one-dark";
import { OpenFile } from "./filestate";
const fixedHeightEditor = EditorView.theme({
"&": {
height: "100%",
@ -11,17 +14,33 @@ const fixedHeightEditor = EditorView.theme({
width: "600px",
minWidth: "8em",
flex: "none",
fontSize: "16px",
},
".cm-scroller": { overflow: "auto scroll" },
});
export class Editor {
view: EditorView;
file: OpenFile;
constructor() {
dispatch(tr: Transaction, inhibitSync = false) {
this.view.update([tr]);
if (!inhibitSync) {
this.file.dispatch({ changes: tr.changes }, this);
}
}
constructor(file: OpenFile) {
this.file = file;
const kmap = keymap.of([
...defaultKeymap,
{ key: "Mod-z", run: () => undo(file.target) },
{ key: "Mod-shift-z", run: () => redo(file.target) },
]);
this.view = new EditorView({
doc: "Start document",
extensions: [basicSetup, oneDark, fixedHeightEditor],
doc: file.rootState.doc,
dispatch: (trs) => this.dispatch(trs),
extensions: [oneDark, fixedHeightEditor, kmap],
});
}

View File

@ -2,7 +2,7 @@ import van from "vanjs-core";
import * as vanX from "vanjs-ext";
const v = van.tags;
import { Editor } from "./editor";
import { OpenFile } from "./filestate";
import * as u from "./utils";
const EditorWrapper = (editor: any, del: any, k: any) =>
@ -20,9 +20,10 @@ const editors = vanX.reactive([[]]);
const currentTab = van.state(0);
van.derive(() => console.log("Setting tab to", currentTab.val));
export function addEditor() {
export function addEditor(file: OpenFile) {
console.log("Adding editor to tab ", currentTab.val, editors);
editors[currentTab.val].push(vanX.noreactive(new Editor()));
const editor = file.createEditor();
editors[currentTab.val].push(vanX.noreactive(editor));
}
export function addTab() {
editors.push([]);

85
src/app/filestate.ts Normal file
View File

@ -0,0 +1,85 @@
import {
EditorState,
EditorStateConfig,
TransactionSpec,
StateEffect,
} from "@codemirror/state";
import { history } from "@codemirror/commands";
import { Editor } from "./editor";
const openFiles: { [path: string]: OpenFile } = {};
export class OpenFile {
filePath: string;
editors: Editor[];
rootState: EditorState;
constructor(cfg: EditorStateConfig) {
this.filePath = null;
this.editors = [];
this.rootState = EditorState.create(cfg).update({
effects: [StateEffect.appendConfig.of([history()])],
}).state;
}
static async openFile(filePath?: string) {
const { content, path } = await window.electronAPI.readFile(filePath);
const file = new OpenFile({ doc: content });
file.setPath(path);
return file;
}
private setPath(path: string) {
delete openFiles[this.filePath];
this.filePath = path;
openFiles[path] = this;
}
async saveFile() {
if (this.filePath) {
await window.electronAPI.saveFile(
this.rootState.doc.toString(),
this.filePath,
);
} else {
await this.saveAs();
}
}
async saveAs(filePath?: string) {
const { path } = await window.electronAPI.saveFile(
this.rootState.doc.toString(),
filePath,
);
this.setPath(path);
}
// Function to create and return a new EditorView for this file
createEditor(): Editor {
const editor = new Editor(this);
this.editors.push(editor);
return editor;
}
dispatch(trs: TransactionSpec, origin?: Editor) {
console.log("Dispatching trs", trs, "to", this.editors, "from", origin);
console.log(this.rootState);
this.rootState = this.rootState.update(trs).state;
if (origin) {
const es = this.editors.filter((e) => e !== origin);
es.forEach((e) => e.dispatch(e.view.state.update(trs), true));
} else {
this.editors.forEach((e) =>
e.dispatch(e.view.state.update(trs), true),
);
}
}
get target() {
console.log("Getting target");
return {
state: this.rootState,
dispatch: (tr: TransactionSpec) => this.dispatch(tr),
};
}
}

View File

@ -2,13 +2,17 @@
import "./index.css";
import van from "vanjs-core";
import * as vanX from "vanjs-ext";
const v = van.tags;
import { Editor } from "./editor";
import { FolderTreeView } from "./foldernav";
import { EditorTabs, addTab, addEditor } from "./editorgrid";
import * as u from "./utils";
import { OpenFile } from "./filestate";
function newFile() {
const file = new OpenFile({});
addEditor(file);
}
const app = v.div(
{ class: "h-screen max-h-screen w-screen max-w-screen flex" },
@ -17,7 +21,7 @@ const app = v.div(
class: "flex-none resize-x overflow-x-hidden overflow-y-scroll w-3xs min-w-32",
},
u.InlineButton(addTab, "Add Tab", "+Tab"),
u.InlineButton(addEditor, "Add Editor", "+File"),
u.InlineButton(newFile, "Add Editor", "+File"),
FolderTreeView,
),
EditorTabs,
@ -25,4 +29,6 @@ const app = v.div(
van.add(document.body, app);
addEditor();
const file = new OpenFile({});
addEditor(file);
addEditor(file);