Plugin stuff

This commit is contained in:
Zef Hemel
2022-03-28 15:25:05 +02:00
parent 16fa05d4cc
commit bf32d6d0bd
36 changed files with 523 additions and 219 deletions
+2 -6
View File
@@ -1,11 +1,7 @@
export type AppEvent =
| "app:ready"
| "page:save"
| "page:click"
| "page:index"
| "editor:complete";
export type AppEvent = "page:click" | "editor:complete";
export type ClickEvent = {
page: string;
pos: number;
metaKey: boolean;
ctrlKey: boolean;
+12
View File
@@ -0,0 +1,12 @@
import { useRef } from "react";
export function Panel({ html }: { html: string }) {
const iFrameRef = useRef<HTMLIFrameElement>(null);
// @ts-ignore
window.iframeRef = iFrameRef;
return (
<div className="panel">
<iframe srcDoc={html} ref={iFrameRef} />
</div>
);
}
+1 -1
View File
@@ -1 +1 @@
export const pageLinkRegex = /\[\[([\w\s\/\:,\.\-]+)\]\]/;
export const pageLinkRegex = /\[\[([\w\s\/\:,\.@\-]+)\]\]/;
+25 -17
View File
@@ -31,7 +31,7 @@ import { TopBar } from "./components/top_bar";
import { Cursor } from "./cursorEffect";
import { lineWrapper } from "./line_wrapper";
import { markdown } from "./markdown";
import { IPageNavigator, PathPageNavigator } from "./navigator";
import { PathPageNavigator } from "./navigator";
import customMarkDown from "./parser";
import reducer from "./reducer";
import { smartQuoteKeymap } from "./smart_quotes";
@@ -52,6 +52,7 @@ import { safeRun } from "./util";
import { System } from "../plugos/system";
import { EventFeature } from "../plugos/feature/event";
import { systemSyscalls } from "./syscalls/system";
import { Panel } from "./components/panel";
class PageState {
scrollTop: number;
@@ -72,7 +73,7 @@ export class Editor implements AppEventDispatcher {
viewDispatch: React.Dispatch<Action>;
space: Space;
navigationResolve?: (val: undefined) => void;
pageNavigator: IPageNavigator;
pageNavigator: PathPageNavigator;
private eventFeature: EventFeature;
constructor(space: Space, parent: Element) {
@@ -102,7 +103,7 @@ export class Editor implements AppEventDispatcher {
async init() {
this.focus();
this.pageNavigator.subscribe(async (pageName) => {
this.pageNavigator.subscribe(async (pageName, pos) => {
console.log("Now navigating to", pageName);
if (!this.editorView) {
@@ -110,6 +111,11 @@ export class Editor implements AppEventDispatcher {
}
await this.loadPage(pageName);
if (pos) {
this.editorView.dispatch({
selection: { anchor: pos },
});
}
});
this.space.on({
@@ -175,8 +181,8 @@ export class Editor implements AppEventDispatcher {
for (let cmd of cmds) {
this.editorCommands.set(cmd.name, {
command: cmd,
run: async (arg): Promise<any> => {
return await plug.invoke(name, [arg]);
run: () => {
return plug.invoke(name, []);
},
});
}
@@ -223,10 +229,11 @@ export class Editor implements AppEventDispatcher {
mac: def.command.mac,
run: (): boolean => {
Promise.resolve()
.then(async () => {
await def.run(null);
})
.catch((e) => console.error(e));
.then(def.run)
.catch((e: any) => {
console.error(e);
this.flashNotification(`Error running command: ${e.message}`);
});
return true;
},
});
@@ -317,6 +324,7 @@ export class Editor implements AppEventDispatcher {
click: (event: MouseEvent, view: EditorView) => {
safeRun(async () => {
let clickEvent: ClickEvent = {
page: pageName,
ctrlKey: event.ctrlKey,
metaKey: event.metaKey,
altKey: event.altKey,
@@ -375,7 +383,7 @@ export class Editor implements AppEventDispatcher {
},
});
safeRun(async () => {
await def.run(null);
await def.run();
});
},
});
@@ -390,8 +398,8 @@ export class Editor implements AppEventDispatcher {
this.editorView!.focus();
}
async navigate(name: string) {
await this.pageNavigator.navigate(name);
async navigate(name: string, pos?: number) {
await this.pageNavigator.navigate(name, pos);
}
async loadPage(pageName: string) {
@@ -451,7 +459,7 @@ export class Editor implements AppEventDispatcher {
}, [viewState.currentPage]);
return (
<>
<div className={viewState.showRHS ? "rhs-open" : ""}>
{viewState.showPageNavigator && (
<PageNavigator
allPages={viewState.allPages}
@@ -473,15 +481,15 @@ export class Editor implements AppEventDispatcher {
dispatch({ type: "hide-palette" });
editor!.focus();
if (cmd) {
safeRun(async () => {
let result = await cmd.run(null);
console.log("Result of command", result);
cmd.run().catch((e) => {
console.error("Error running command", e);
});
}
}}
commands={viewState.commands}
/>
)}
{viewState.showRHS && <Panel html={viewState.rhsHTML} />}
<TopBar
pageName={viewState.currentPage}
notifications={viewState.notifications}
@@ -490,7 +498,7 @@ export class Editor implements AppEventDispatcher {
}}
/>
<div id="editor"></div>
</>
</div>
);
}
+21 -41
View File
@@ -1,13 +1,5 @@
import { safeRun } from "./util";
export interface IPageNavigator {
subscribe(pageLoadCallback: (pageName: string) => Promise<void>): void;
navigate(page: string): Promise<void>;
getCurrentPage(): string;
}
function encodePageUrl(name: string): string {
return name.replaceAll(" ", "_");
}
@@ -16,26 +8,34 @@ function decodePageUrl(url: string): string {
return url.replaceAll("_", " ");
}
export class PathPageNavigator implements IPageNavigator {
navigationResolve?: (value: undefined) => void;
async navigate(page: string) {
window.history.pushState({ page: page }, page, `/${encodePageUrl(page)}`);
export class PathPageNavigator {
navigationResolve?: () => void;
async navigate(page: string, pos?: number) {
window.history.pushState(
{ page, pos },
page,
`/${encodePageUrl(page)}${pos ? "@" + pos : ""}`
);
window.dispatchEvent(new PopStateEvent("popstate"));
await new Promise<undefined>((resolve) => {
await new Promise<void>((resolve) => {
this.navigationResolve = resolve;
});
this.navigationResolve = undefined;
}
subscribe(pageLoadCallback: (pageName: string) => Promise<void>): void {
subscribe(
pageLoadCallback: (pageName: string, pos: number) => Promise<void>
): void {
const cb = () => {
const gotoPage = this.getCurrentPage();
if (!gotoPage) {
return;
}
safeRun(async () => {
await pageLoadCallback(this.getCurrentPage());
await pageLoadCallback(this.getCurrentPage(), this.getCurrentPos());
if (this.navigationResolve) {
this.navigationResolve(undefined);
this.navigationResolve();
}
});
};
@@ -44,32 +44,12 @@ export class PathPageNavigator implements IPageNavigator {
}
getCurrentPage(): string {
return decodePageUrl(location.pathname.substring(1));
let [page] = location.pathname.substring(1).split("@");
return decodePageUrl(page);
}
}
export class HashPageNavigator implements IPageNavigator {
navigationResolve?: (value: undefined) => void;
async navigate(page: string) {
location.hash = encodePageUrl(page);
await new Promise<undefined>((resolve) => {
this.navigationResolve = resolve;
});
this.navigationResolve = undefined;
}
subscribe(pageLoadCallback: (pageName: string) => Promise<void>): void {
const cb = () => {
safeRun(async () => {
await pageLoadCallback(this.getCurrentPage());
if (this.navigationResolve) {
this.navigationResolve(undefined);
}
});
};
window.addEventListener("hashchange", cb);
cb();
}
getCurrentPage(): string {
return decodePageUrl(location.hash.substring(1));
getCurrentPos(): number {
let [, pos] = location.pathname.substring(1).split("@");
return +pos || 0;
}
}
+12
View File
@@ -58,6 +58,18 @@ export default function reducer(
...state,
notifications: state.notifications.filter((n) => n.id !== action.id),
};
case "show-rhs":
return {
...state,
showRHS: true,
rhsHTML: action.html,
};
case "hide-rhs":
return {
...state,
showRHS: false,
rhsHTML: "",
};
}
return state;
}
+1 -1
View File
@@ -85,7 +85,7 @@ export class Space extends EventEmitter<SpaceEvents> {
this.reqId++;
this.socket!.once(`${eventName}Resp${this.reqId}`, (err, result) => {
if (err) {
reject(err);
reject(new Error(err));
} else {
resolve(result);
}
+23 -20
View File
@@ -17,6 +17,24 @@ body {
padding: 0;
}
.panel {
position: absolute;
top: 55px;
bottom: 0;
right: 0;
width: 400px;
z-index: 20;
background: #efefef;
iframe {
border: 0;
width: 100%;
height: 100%;
padding: 10px;
scroll: auto;
}
}
#top {
height: 55px;
position: fixed;
@@ -40,6 +58,7 @@ body {
padding: 3px;
font-size: 14px;
}
.current-page {
font-family: var(--ui-font);
font-weight: bold;
@@ -52,26 +71,6 @@ body {
}
}
// #bottom {
// position: fixed;
// bottom: 0;
// left: 0;
// right: 0;
// height: 20px;
// background-color: rgb(232, 232, 232);
// color: rgb(79, 78, 78);
// border-top: rgb(186, 186, 186) 1px solid;
// margin: 0;
// padding: 5px 10px;
// font-family: var(--ui-font);
// font-size: 0.9em;
// text-align: right;
// }
// body.keyboard #bottom {
// bottom: 250px;
// }
#editor {
position: absolute;
top: 55px;
@@ -81,6 +80,10 @@ body {
overflow-y: hidden;
}
div.rhs-open #editor {
right: 350px;
}
@media only screen and (max-width: 800px) {
.cm-editor .cm-content {
margin: 0 10px !important;
+14 -2
View File
@@ -36,8 +36,8 @@ export default (editor: Editor): SysCallMapping => ({
getCursor: (): number => {
return editor.editorView!.state.selection.main.from;
},
navigate: async (ctx, name: string) => {
await editor.navigate(name);
navigate: async (ctx, name: string, pos: number) => {
await editor.navigate(name, pos);
},
openUrl: async (ctx, url: string) => {
window.open(url, "_blank")!.focus();
@@ -45,6 +45,12 @@ export default (editor: Editor): SysCallMapping => ({
flashNotification: (ctx, message: string) => {
editor.flashNotification(message);
},
showRhs: (ctx, html: string) => {
editor.viewDispatch({
type: "show-rhs",
html: html,
});
},
insertAtPos: (ctx, text: string, pos: number) => {
editor.editorView!.dispatch({
changes: {
@@ -97,6 +103,12 @@ export default (editor: Editor): SysCallMapping => ({
}
}
},
getLineUnderCursor: (): string => {
const editorState = editor.editorView!.state;
let selection = editorState.selection.main;
let line = editorState.doc.lineAt(selection.from);
return editorState.sliceDoc(line.from, line.to);
},
matchBefore: (
ctx,
regexp: string
+1 -1
View File
@@ -7,7 +7,7 @@ export function systemSyscalls(space: Space): SysCallMapping {
if (!ctx.plug) {
throw Error("No plug associated with context");
}
return await space.wsCall("invokeFunction", ctx.plug.name, name, ...args);
return space.wsCall("invokeFunction", ctx.plug.name, name, ...args);
},
};
}
+8 -2
View File
@@ -9,7 +9,7 @@ export type PageMeta = {
export type AppCommand = {
command: CommandDef;
run: (arg: any) => Promise<any>;
run: () => Promise<void>;
};
export const slashCommandRegexp = /\/[\w\-]*/;
@@ -24,6 +24,8 @@ export type AppViewState = {
currentPage?: string;
showPageNavigator: boolean;
showCommandPalette: boolean;
showRHS: boolean;
rhsHTML: string;
allPages: Set<PageMeta>;
commands: Map<string, AppCommand>;
notifications: Notification[];
@@ -32,6 +34,8 @@ export type AppViewState = {
export const initialViewState: AppViewState = {
showPageNavigator: false,
showCommandPalette: false,
showRHS: false,
rhsHTML: "<h1>Loading...</h1>",
allPages: new Set(),
commands: new Map(),
notifications: [],
@@ -46,4 +50,6 @@ export type Action =
| { type: "show-palette" }
| { type: "hide-palette" }
| { type: "show-notification"; notification: Notification }
| { type: "dismiss-notification"; id: number };
| { type: "dismiss-notification"; id: number }
| { type: "show-rhs"; html: string }
| { type: "hide-rhs" };