A whole lot of enhancements
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link href="panel.scss" rel="stylesheet" />
|
||||
<base target="_top">
|
||||
<script type="module">
|
||||
window.addEventListener("message", (message) => {
|
||||
const data = message.data;
|
||||
switch(data.type) {
|
||||
case "html":
|
||||
document.body.innerHTML = data.html;
|
||||
break;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Send me HTML
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
body {
|
||||
background: white;
|
||||
font-family: "Menlo";
|
||||
}
|
||||
@@ -1,12 +1,32 @@
|
||||
import { useRef } from "react";
|
||||
import {useEffect, useRef} from "react";
|
||||
// @ts-ignore
|
||||
import iframeHtml from "bundle-text:./panel.html";
|
||||
import {Simulate} from "react-dom/test-utils";
|
||||
|
||||
export function Panel({ html }: { html: string }) {
|
||||
const iFrameRef = useRef<HTMLIFrameElement>(null);
|
||||
// @ts-ignore
|
||||
window.iframeRef = iFrameRef;
|
||||
useEffect(() => {
|
||||
function loadContent() {
|
||||
if (iFrameRef.current?.contentWindow) {
|
||||
iFrameRef.current.contentWindow.postMessage({
|
||||
type: "html",
|
||||
html: html,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!iFrameRef.current) {
|
||||
return;
|
||||
}
|
||||
let iframe = iFrameRef.current;
|
||||
iframe.onload = loadContent;
|
||||
loadContent();
|
||||
return () => {
|
||||
iframe.onload = null;
|
||||
};
|
||||
}, [html]);
|
||||
return (
|
||||
<div className="panel">
|
||||
<iframe srcDoc={html} ref={iFrameRef} />
|
||||
<iframe srcDoc={iframeHtml} ref={iFrameRef} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import {EditorView} from "@codemirror/view";
|
||||
import * as util from "../util";
|
||||
|
||||
export function StatusBar({ editorView }: { editorView?: EditorView }) {
|
||||
@@ -11,7 +11,9 @@ export function StatusBar({ editorView }: { editorView?: EditorView }) {
|
||||
}
|
||||
return (
|
||||
<div id="bottom">
|
||||
{wordCount} words | {readingTime} min
|
||||
<div className="inner">
|
||||
{wordCount} words | {readingTime} min
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+19
-9
@@ -6,18 +6,18 @@ import {bracketMatching} from "@codemirror/matchbrackets";
|
||||
import {searchKeymap} from "@codemirror/search";
|
||||
import {EditorSelection, EditorState} from "@codemirror/state";
|
||||
import {
|
||||
drawSelection,
|
||||
dropCursor,
|
||||
EditorView,
|
||||
highlightSpecialChars,
|
||||
KeyBinding,
|
||||
keymap,
|
||||
ViewPlugin,
|
||||
ViewUpdate,
|
||||
drawSelection,
|
||||
dropCursor,
|
||||
EditorView,
|
||||
highlightSpecialChars,
|
||||
KeyBinding,
|
||||
keymap,
|
||||
ViewPlugin,
|
||||
ViewUpdate,
|
||||
} from "@codemirror/view";
|
||||
import React, {useEffect, useReducer} from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import {createSandbox as createIFrameSandbox} from "../plugos/environments/iframe_sandbox";
|
||||
import {createSandbox as createIFrameSandbox} from "../plugos/environments/webworker_sandbox";
|
||||
import {AppEvent, AppEventDispatcher, ClickEvent} from "./app_event";
|
||||
import * as commands from "./commands";
|
||||
import {CommandPalette} from "./components/command_palette";
|
||||
@@ -46,6 +46,8 @@ import {SlashCommandHook} from "./hooks/slash_command";
|
||||
import {CompleterHook} from "./hooks/completer";
|
||||
import {pasteLinkExtension} from "./editor_paste";
|
||||
import {markdownSyscalls} from "../common/syscalls/markdown";
|
||||
import {clientStoreSyscalls} from "./syscalls/clientStore";
|
||||
import {StatusBar} from "./components/status_bar";
|
||||
|
||||
class PageState {
|
||||
scrollTop: number;
|
||||
@@ -71,6 +73,9 @@ export class Editor implements AppEventDispatcher {
|
||||
pageNavigator: PathPageNavigator;
|
||||
eventHook: EventHook;
|
||||
saveTimeout: any;
|
||||
debouncedUpdateEvent = throttle(() => {
|
||||
this.eventHook.dispatchEvent("editor:updated");
|
||||
}, 1000);
|
||||
private system = new System<SilverBulletHooks>("client");
|
||||
|
||||
constructor(space: Space, parent: Element) {
|
||||
@@ -114,6 +119,7 @@ export class Editor implements AppEventDispatcher {
|
||||
this.system.registerSyscalls([], indexerSyscalls(this.space));
|
||||
this.system.registerSyscalls([], systemSyscalls(this.space));
|
||||
this.system.registerSyscalls([], markdownSyscalls());
|
||||
this.system.registerSyscalls([], clientStoreSyscalls());
|
||||
}
|
||||
|
||||
get currentPage(): string | undefined {
|
||||
@@ -357,6 +363,7 @@ export class Editor implements AppEventDispatcher {
|
||||
update(update: ViewUpdate): void {
|
||||
if (update.docChanged) {
|
||||
editor.viewDispatch({ type: "page-changed" });
|
||||
editor.debouncedUpdateEvent();
|
||||
editor.save();
|
||||
}
|
||||
}
|
||||
@@ -438,6 +445,8 @@ export class Editor implements AppEventDispatcher {
|
||||
type: "page-loaded",
|
||||
name: pageName,
|
||||
});
|
||||
|
||||
await this.eventHook.dispatchEvent("editor:pageSwitched");
|
||||
}
|
||||
|
||||
ViewComponent(): React.ReactElement {
|
||||
@@ -494,6 +503,7 @@ export class Editor implements AppEventDispatcher {
|
||||
}}
|
||||
/>
|
||||
<div id="editor" />
|
||||
<StatusBar editorView={editor.editorView} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,4 +1,4 @@
|
||||
import { safeRun } from "./util";
|
||||
import {safeRun} from "./util";
|
||||
|
||||
function encodePageUrl(name: string): string {
|
||||
return name.replaceAll(" ", "_");
|
||||
@@ -33,7 +33,10 @@ export class PathPageNavigator {
|
||||
return;
|
||||
}
|
||||
safeRun(async () => {
|
||||
await pageLoadCallback(this.getCurrentPage(), event && event.state.pos);
|
||||
await pageLoadCallback(
|
||||
this.getCurrentPage(),
|
||||
event?.state && event.state.pos
|
||||
);
|
||||
if (this.navigationResolve) {
|
||||
this.navigationResolve();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
$max-editor-width: 800px;
|
||||
$top-bar-height: 55px;
|
||||
$bottom-bar-height: 30px;
|
||||
@@ -1,3 +1,9 @@
|
||||
@import "constants.scss";
|
||||
|
||||
div.rhs-open #editor .cm-editor .cm-content {
|
||||
max-width: 550px;
|
||||
}
|
||||
|
||||
.cm-editor {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -6,7 +12,7 @@
|
||||
.cm-content {
|
||||
font-family: var(--editor-font);
|
||||
margin: auto;
|
||||
max-width: 800px;
|
||||
max-width: $max-editor-width;
|
||||
}
|
||||
|
||||
.other-cursor {
|
||||
|
||||
+35
-13
@@ -1,5 +1,7 @@
|
||||
@use "editor.scss";
|
||||
@use "filter_box.scss";
|
||||
@import "constants";
|
||||
|
||||
|
||||
:root {
|
||||
--ident: 18px;
|
||||
@@ -19,25 +21,24 @@ body {
|
||||
|
||||
.panel {
|
||||
position: absolute;
|
||||
top: 55px;
|
||||
bottom: 0;
|
||||
top: $top-bar-height + 1px;
|
||||
bottom: $bottom-bar-height;
|
||||
right: 0;
|
||||
width: 400px;
|
||||
width: 50%;
|
||||
z-index: 20;
|
||||
background: #efefef;
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
scroll: auto;
|
||||
padding: 0px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#top {
|
||||
height: 55px;
|
||||
position: absolute;
|
||||
height: $top-bar-height;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@@ -47,7 +48,7 @@ body {
|
||||
|
||||
.inner {
|
||||
padding-top: 12px;
|
||||
max-width: 800px;
|
||||
max-width: $max-editor-width;
|
||||
font-size: 28px;
|
||||
margin: auto;
|
||||
|
||||
@@ -82,19 +83,40 @@ body {
|
||||
|
||||
#editor {
|
||||
position: absolute;
|
||||
top: 55px;
|
||||
bottom: 50px;
|
||||
top: $top-bar-height;
|
||||
bottom: $bottom-bar-height;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
div.rhs-open #editor {
|
||||
right: 350px;
|
||||
right: 50%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
div.rhs-open #top .inner {
|
||||
max-width: 550px;
|
||||
padding-right: 50%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $max-editor-width) {
|
||||
.cm-editor .cm-content {
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
#bottom {
|
||||
height: $bottom-bar-height;
|
||||
background-color: #ccc;
|
||||
position: fixed;
|
||||
text-align: right;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgb(213, 213, 213);
|
||||
border-top: rgb(193, 193, 193) 1px solid;
|
||||
|
||||
.inner {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import {transportSyscalls} from "../../plugos/syscalls/transport";
|
||||
import {SysCallMapping} from "../../plugos/system";
|
||||
import {storeSyscalls} from "../../plugos/syscalls/store.dexie_browser";
|
||||
|
||||
export function clientStoreSyscalls(): SysCallMapping {
|
||||
const storeCalls = storeSyscalls("local", "localData");
|
||||
return transportSyscalls(
|
||||
["clientStore.get", "clientStore.set", "clientStore.delete"],
|
||||
(ctx, name, ...args) => {
|
||||
return storeCalls[name.replace("clientStore.", "store.")](ctx, ...args);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import {Editor} from "../editor";
|
||||
import {syntaxTree} from "@codemirror/language";
|
||||
import {Transaction} from "@codemirror/state";
|
||||
import {SysCallMapping} from "../../plugos/system";
|
||||
|
||||
@@ -58,6 +57,11 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
html: html,
|
||||
});
|
||||
},
|
||||
"editor.hideRhs": (ctx, html: string) => {
|
||||
editor.viewDispatch({
|
||||
type: "hide-rhs",
|
||||
});
|
||||
},
|
||||
"editor.insertAtPos": (ctx, text: string, pos: number) => {
|
||||
editor.editorView!.dispatch({
|
||||
changes: {
|
||||
@@ -95,27 +99,7 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
},
|
||||
});
|
||||
},
|
||||
"editor.getSyntaxNodeUnderCursor": (): SyntaxNode | undefined => {
|
||||
const editorState = editor.editorView!.state;
|
||||
let selection = editorState.selection.main;
|
||||
if (selection.empty) {
|
||||
let node = syntaxTree(editorState).resolveInner(selection.from);
|
||||
if (node) {
|
||||
return {
|
||||
name: node.name,
|
||||
text: editorState.sliceDoc(node.from, node.to),
|
||||
from: node.from,
|
||||
to: node.to,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor.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);
|
||||
},
|
||||
|
||||
"editor.matchBefore": (
|
||||
ctx,
|
||||
regexp: string
|
||||
@@ -135,18 +119,6 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
"editor.getSyntaxNodeAtPos": (ctx, pos: number): SyntaxNode | undefined => {
|
||||
const editorState = editor.editorView!.state;
|
||||
let node = syntaxTree(editorState).resolveInner(pos);
|
||||
if (node) {
|
||||
return {
|
||||
name: node.name,
|
||||
text: editorState.sliceDoc(node.from, node.to),
|
||||
from: node.from,
|
||||
to: node.to,
|
||||
};
|
||||
}
|
||||
},
|
||||
"editor.dispatch": (ctx, change: Transaction) => {
|
||||
editor.editorView!.dispatch(change);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user