1
0

Editor refactor: extract UI

This commit is contained in:
Zef Hemel 2023-07-14 14:22:26 +02:00
parent c5849f881b
commit e92ed2c5be
20 changed files with 315 additions and 274 deletions

View File

@ -1,5 +1,5 @@
import { safeRun } from "../common/util.ts";
import { Editor } from "./editor.tsx";
import { Editor } from "./editor.ts";
safeRun(async () => {
console.log("Booting");

View File

@ -10,7 +10,7 @@ import assetSyscalls from "../plugos/syscalls/asset.ts";
import { eventSyscalls } from "../plugos/syscalls/event.ts";
import { storeSyscalls } from "../plugos/syscalls/store.dexie_browser.ts";
import { SysCallMapping, System } from "../plugos/system.ts";
import type { Editor } from "./editor.tsx";
import type { Editor } from "./editor.ts";
import { CodeWidgetHook } from "./hooks/code_widget.ts";
import { CommandHook } from "./hooks/command.ts";
import { SlashCommandHook } from "./hooks/slash_command.ts";
@ -74,7 +74,7 @@ export class ClientSystem {
this.commandHook = new CommandHook();
this.commandHook.on({
commandsUpdated: (commandMap) => {
this.editor.viewDispatch({
this.editor.ui.viewDispatch({
type: "update-commands",
commands: commandMap,
});

View File

@ -6,7 +6,7 @@ import {
syntaxTree,
WidgetType,
} from "../deps.ts";
import { Editor } from "../editor.tsx";
import { Editor } from "../editor.ts";
import { decoratorStateField, isCursorInRange } from "./util.ts";
type AdmonitionType = "note" | "warning";

View File

@ -1,6 +1,6 @@
import type { ClickEvent } from "../../plug-api/app_event.ts";
import type { Extension } from "../deps.ts";
import type { Editor } from "../editor.tsx";
import type { Editor } from "../editor.ts";
import { blockquotePlugin } from "./block_quote.ts";
import { admonitionPlugin } from "./admonition.ts";
import { directivePlugin } from "./directive.ts";

View File

@ -1,7 +1,7 @@
import { commandLinkRegex } from "../../common/markdown_parser/parser.ts";
import { ClickEvent } from "$sb/app_event.ts";
import { Decoration, syntaxTree } from "../deps.ts";
import { Editor } from "../editor.tsx";
import { Editor } from "../editor.ts";
import {
ButtonWidget,
decoratorStateField,

View File

@ -1,6 +1,6 @@
import { EditorView, syntaxTree, ViewPlugin, ViewUpdate } from "../deps.ts";
import { maximumAttachmentSize } from "../../common/types.ts";
import { Editor } from "../editor.tsx";
import { Editor } from "../editor.ts";
// We use turndown to convert HTML to Markdown
import TurndownService from "https://cdn.skypack.dev/turndown@7.1.1";

View File

@ -1,7 +1,7 @@
import { WidgetContent } from "../../plug-api/app_event.ts";
import { panelHtml } from "../components/panel.tsx";
import { Decoration, EditorState, syntaxTree, WidgetType } from "../deps.ts";
import type { Editor } from "../editor.tsx";
import type { Editor } from "../editor.ts";
import { CodeWidgetCallback } from "../hooks/code_widget.ts";
import {
decoratorStateField,

View File

@ -8,7 +8,7 @@ import {
import { decoratorStateField } from "./util.ts";
import type { Space } from "../space.ts";
import type { Editor } from "../editor.tsx";
import type { Editor } from "../editor.ts";
class InlineImageWidget extends WidgetType {
constructor(

View File

@ -8,7 +8,7 @@ import {
import { renderMarkdownToHtml } from "../../plugs/markdown/markdown_render.ts";
import { ParseTree } from "$sb/lib/tree.ts";
import { lezerToParseTree } from "../../common/markdown_parser/parse_tree.ts";
import type { Editor } from "../editor.tsx";
import type { Editor } from "../editor.ts";
class TableViewWidget extends WidgetType {
constructor(

View File

@ -1,7 +1,7 @@
import { pageLinkRegex } from "../../common/markdown_parser/parser.ts";
import { ClickEvent } from "../../plug-api/app_event.ts";
import { Decoration, syntaxTree } from "../deps.ts";
import { Editor } from "../editor.tsx";
import { Editor } from "../editor.ts";
import {
decoratorStateField,
invisibleDecoration,

View File

@ -1,5 +1,5 @@
import { useEffect, useRef } from "../deps.ts";
import { Editor } from "../editor.tsx";
import { Editor } from "../editor.ts";
import { PanelConfig } from "../types.ts";
export const panelHtml = `<!DOCTYPE html>

View File

@ -54,6 +54,7 @@ import { isValidPageName } from "$sb/lib/page.ts";
import { ClientSystem } from "./client_system.ts";
import { createEditorState } from "./editor_state.ts";
import { OpenPages } from "./open_pages.ts";
import { MainUI } from "./editor_ui.tsx";
const frontMatterRegex = /^---\n(([^\n]|\n)*?)---\n/;
const autoSaveInterval = 1000;
@ -71,8 +72,6 @@ declare global {
// TODO: Oh my god, need to refactor this
export class Editor {
editorView?: EditorView;
viewState: AppViewState = initialViewState;
viewDispatch: (action: Action) => void = () => {};
pageNavigator?: PathPageNavigator;
space: Space;
@ -99,6 +98,8 @@ export class Editor {
// Event bus used to communicate between components
eventHook: EventHook;
ui: MainUI;
openPages: OpenPages;
constructor(
@ -184,7 +185,8 @@ export class Editor {
},
);
this.render(parent);
this.ui = new MainUI(this);
this.ui.render(parent);
this.editorView = new EditorView({
state: createEditorState(this, "", "", false),
@ -211,19 +213,22 @@ export class Editor {
if (ev.touches.length === 2) {
ev.stopPropagation();
ev.preventDefault();
this.viewDispatch({ type: "start-navigate" });
this.ui.viewDispatch({ type: "start-navigate" });
}
// Launch the command palette using a three-finger tap
if (ev.touches.length === 3) {
ev.stopPropagation();
ev.preventDefault();
this.viewDispatch({ type: "show-palette", context: this.getContext() });
this.ui.viewDispatch({
type: "show-palette",
context: this.getContext(),
});
}
});
}
get currentPage(): string | undefined {
return this.viewState.currentPage;
return this.ui.viewState.currentPage;
}
async init() {
@ -239,7 +244,7 @@ export class Editor {
}
},
pageListUpdated: (pages) => {
this.viewDispatch({
this.ui.viewDispatch({
type: "pages-listed",
pages: pages,
});
@ -338,10 +343,10 @@ export class Editor {
// Reset for next sync cycle
this.system.plugsUpdated = false;
this.viewDispatch({ type: "sync-change", synced: true });
this.ui.viewDispatch({ type: "sync-change", synced: true });
});
this.eventHook.addLocalListener("sync:error", (name) => {
this.viewDispatch({ type: "sync-change", synced: false });
this.ui.viewDispatch({ type: "sync-change", synced: false });
});
this.eventHook.addLocalListener("sync:conflict", (name) => {
this.flashNotification(
@ -384,8 +389,8 @@ export class Editor {
() => {
if (this.currentPage) {
if (
!this.viewState.unsavedChanges ||
this.viewState.uiOptions.forcedROMode
!this.ui.viewState.unsavedChanges ||
this.ui.viewState.uiOptions.forcedROMode
) {
// No unsaved changes, or read-only mode, not gonna save
return resolve();
@ -398,7 +403,7 @@ export class Editor {
true,
)
.then(async (meta) => {
this.viewDispatch({ type: "page-saved" });
this.ui.viewDispatch({ type: "page-saved" });
await this.dispatchAppEvent(
"editor:pageSaved",
this.currentPage,
@ -425,7 +430,7 @@ export class Editor {
flashNotification(message: string, type: "info" | "error" = "info") {
const id = Math.floor(Math.random() * 1000000);
this.viewDispatch({
this.ui.viewDispatch({
type: "show-notification",
notification: {
id,
@ -436,7 +441,7 @@ export class Editor {
});
setTimeout(
() => {
this.viewDispatch({
this.ui.viewDispatch({
type: "dismiss-notification",
id: id,
});
@ -448,7 +453,7 @@ export class Editor {
progressTimeout?: number;
showProgress(progressPerc: number) {
this.viewDispatch({
this.ui.viewDispatch({
type: "set-progress",
progressPerc,
});
@ -457,7 +462,7 @@ export class Editor {
}
this.progressTimeout = setTimeout(
() => {
this.viewDispatch({
this.ui.viewDispatch({
type: "set-progress",
});
},
@ -472,14 +477,14 @@ export class Editor {
placeHolder = "",
): Promise<FilterOption | undefined> {
return new Promise((resolve) => {
this.viewDispatch({
this.ui.viewDispatch({
type: "show-filterbox",
label,
options,
placeHolder,
helpText,
onSelect: (option: any) => {
this.viewDispatch({ type: "hide-filterbox" });
this.ui.viewDispatch({ type: "hide-filterbox" });
this.focus();
resolve(option);
},
@ -492,12 +497,12 @@ export class Editor {
defaultValue = "",
): Promise<string | undefined> {
return new Promise((resolve) => {
this.viewDispatch({
this.ui.viewDispatch({
type: "show-prompt",
message,
defaultValue,
callback: (value: string | undefined) => {
this.viewDispatch({ type: "hide-prompt" });
this.ui.viewDispatch({ type: "hide-prompt" });
this.focus();
resolve(value);
},
@ -509,11 +514,11 @@ export class Editor {
message: string,
): Promise<boolean> {
return new Promise((resolve) => {
this.viewDispatch({
this.ui.viewDispatch({
type: "show-confirm",
message,
callback: (value: boolean) => {
this.viewDispatch({ type: "hide-confirm" });
this.ui.viewDispatch({ type: "hide-confirm" });
this.focus();
resolve(value);
},
@ -547,7 +552,7 @@ export class Editor {
this,
this.currentPage,
editorView.state.sliceDoc(),
this.viewState.currentPageMeta?.perm === "ro",
this.ui.viewState.currentPageMeta?.perm === "ro",
),
);
if (editorView.contentDOM) {
@ -658,7 +663,7 @@ export class Editor {
}
}
this.viewDispatch({
this.ui.viewDispatch({
type: "page-loading",
name: pageName,
});
@ -692,7 +697,7 @@ export class Editor {
const stateRestored = this.openPages.restoreState(pageName);
this.space.watchPage(pageName);
this.viewDispatch({
this.ui.viewDispatch({
type: "page-loaded",
meta: doc.meta,
});
@ -737,220 +742,8 @@ export class Editor {
}
}
ViewComponent() {
const [viewState, dispatch] = useReducer(reducer, initialViewState);
this.viewState = viewState;
this.viewDispatch = dispatch;
// deno-lint-ignore no-this-alias
const editor = this;
useEffect(() => {
if (viewState.currentPage) {
document.title = viewState.currentPage;
}
}, [viewState.currentPage]);
useEffect(() => {
if (editor.editorView) {
editor.tweakEditorDOM(
editor.editorView.contentDOM,
);
}
}, [viewState.uiOptions.forcedROMode]);
useEffect(() => {
this.rebuildEditorState();
this.dispatchAppEvent("editor:modeswitch");
}, [viewState.uiOptions.vimMode]);
useEffect(() => {
document.documentElement.dataset.theme = viewState.uiOptions.darkMode
? "dark"
: "light";
}, [viewState.uiOptions.darkMode]);
useEffect(() => {
// Need to dispatch a resize event so that the top_bar can pick it up
globalThis.dispatchEvent(new Event("resize"));
}, [viewState.panels]);
return (
<>
{viewState.showPageNavigator && (
<PageNavigator
allPages={viewState.allPages}
currentPage={this.currentPage}
completer={this.miniEditorComplete.bind(this)}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
onNavigate={(page) => {
dispatch({ type: "stop-navigate" });
setTimeout(() => {
editor.focus();
});
if (page) {
safeRun(async () => {
await editor.navigate(page);
});
}
}}
/>
)}
{viewState.showCommandPalette && (
<CommandPalette
onTrigger={(cmd) => {
dispatch({ type: "hide-palette" });
setTimeout(() => {
editor.focus();
});
if (cmd) {
dispatch({ type: "command-run", command: cmd.command.name });
cmd
.run()
.catch((e: any) => {
console.error("Error running command", e.message);
})
.then(() => {
// Always be focusing the editor after running a command
editor.focus();
});
}
}}
commands={this.getCommandsByContext(viewState)}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
completer={this.miniEditorComplete.bind(this)}
recentCommands={viewState.recentCommands}
/>
)}
{viewState.showFilterBox && (
<FilterList
label={viewState.filterBoxLabel}
placeholder={viewState.filterBoxPlaceHolder}
options={viewState.filterBoxOptions}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
allowNew={false}
completer={this.miniEditorComplete.bind(this)}
helpText={viewState.filterBoxHelpText}
onSelect={viewState.filterBoxOnSelect}
/>
)}
{viewState.showPrompt && (
<Prompt
message={viewState.promptMessage!}
defaultValue={viewState.promptDefaultValue}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
completer={this.miniEditorComplete.bind(this)}
callback={(value) => {
dispatch({ type: "hide-prompt" });
viewState.promptCallback!(value);
}}
/>
)}
{viewState.showConfirm && (
<Confirm
message={viewState.confirmMessage!}
callback={(value) => {
dispatch({ type: "hide-confirm" });
viewState.confirmCallback!(value);
}}
/>
)}
<TopBar
pageName={viewState.currentPage}
notifications={viewState.notifications}
synced={viewState.synced}
unsavedChanges={viewState.unsavedChanges}
isLoading={viewState.isLoading}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
progressPerc={viewState.progressPerc}
completer={editor.miniEditorComplete.bind(editor)}
onRename={async (newName) => {
if (!newName) {
// Always move cursor to the start of the page
editor.editorView?.dispatch({
selection: { anchor: 0 },
});
editor.focus();
return;
}
console.log("Now renaming page to...", newName);
await editor.system.system.loadedPlugs.get("core")!.invoke(
"renamePage",
[{ page: newName }],
);
editor.focus();
}}
actionButtons={[
{
icon: HomeIcon,
description: `Go home (Alt-h)`,
callback: () => {
editor.navigate("");
},
href: "",
},
{
icon: BookIcon,
description: `Open page (${isMacLike() ? "Cmd-k" : "Ctrl-k"})`,
callback: () => {
dispatch({ type: "start-navigate" });
this.space.updatePageList();
},
},
{
icon: TerminalIcon,
description: `Run command (${isMacLike() ? "Cmd-/" : "Ctrl-/"})`,
callback: () => {
dispatch({ type: "show-palette", context: this.getContext() });
},
},
]}
rhs={!!viewState.panels.rhs.mode && (
<div
className="panel"
style={{ flex: viewState.panels.rhs.mode }}
/>
)}
lhs={!!viewState.panels.lhs.mode && (
<div
className="panel"
style={{ flex: viewState.panels.lhs.mode }}
/>
)}
/>
<div id="sb-main">
{!!viewState.panels.lhs.mode && (
<Panel config={viewState.panels.lhs} editor={editor} />
)}
<div id="sb-editor" />
{!!viewState.panels.rhs.mode && (
<Panel config={viewState.panels.rhs} editor={editor} />
)}
</div>
{!!viewState.panels.modal.mode && (
<div
className="sb-modal"
style={{ inset: `${viewState.panels.modal.mode}px` }}
>
<Panel config={viewState.panels.modal} editor={editor} />
</div>
)}
{!!viewState.panels.bhs.mode && (
<div className="sb-bhs">
<Panel config={viewState.panels.bhs} editor={editor} />
</div>
)}
</>
);
}
async runCommandByName(name: string, ...args: any[]) {
const cmd = this.viewState.commands.get(name);
const cmd = this.ui.viewState.commands.get(name);
if (cmd) {
await cmd.run();
} else {
@ -958,12 +751,7 @@ export class Editor {
}
}
render(container: Element) {
const ViewComponent = this.ViewComponent.bind(this);
preactRender(<ViewComponent />, container);
}
private getCommandsByContext(
getCommandsByContext(
state: AppViewState,
): Map<string, AppCommand> {
const commands = new Map(state.commands);

View File

@ -49,7 +49,7 @@ import {
xmlLanguage,
yamlLanguage,
} from "../common/deps.ts";
import { Editor } from "./editor.tsx";
import { Editor } from "./editor.ts";
import { vim } from "./deps.ts";
import { inlineImagesPlugin } from "./cm_plugins/inline_image.ts";
import { cleanModePlugins } from "./cm_plugins/clean.ts";
@ -107,11 +107,15 @@ export function createEditorState(
doc: text,
extensions: [
// Not using CM theming right now, but some extensions depend on the "dark" thing
EditorView.theme({}, { dark: editor.viewState.uiOptions.darkMode }),
EditorView.theme({}, {
dark: editor.ui.viewState.uiOptions.darkMode,
}),
// Enable vim mode, or not
[...editor.viewState.uiOptions.vimMode ? [vim({ status: true })] : []],
[
...readOnly || editor.viewState.uiOptions.forcedROMode
...editor.ui.viewState.uiOptions.vimMode ? [vim({ status: true })] : [],
],
[
...readOnly || editor.ui.viewState.uiOptions.forcedROMode
? [readonlyMode()]
: [],
],
@ -309,7 +313,7 @@ export function createEditorState(
key: "Ctrl-k",
mac: "Cmd-k",
run: (): boolean => {
editor.viewDispatch({ type: "start-navigate" });
editor.ui.viewDispatch({ type: "start-navigate" });
editor.space.updatePageList();
return true;
@ -319,7 +323,7 @@ export function createEditorState(
key: "Ctrl-/",
mac: "Cmd-/",
run: (): boolean => {
editor.viewDispatch({
editor.ui.viewDispatch({
type: "show-palette",
context: editor.getContext(),
});
@ -330,7 +334,7 @@ export function createEditorState(
key: "Ctrl-.",
mac: "Cmd-.",
run: (): boolean => {
editor.viewDispatch({
editor.ui.viewDispatch({
type: "show-palette",
context: editor.getContext(),
});
@ -415,7 +419,7 @@ export function createEditorState(
class {
update(update: ViewUpdate): void {
if (update.docChanged) {
editor.viewDispatch({ type: "page-changed" });
editor.ui.viewDispatch({ type: "page-changed" });
editor.debouncedUpdateEvent();
editor.save().catch((e) => console.error("Error saving", e));
}

246
web/editor_ui.tsx Normal file
View File

@ -0,0 +1,246 @@
import { isMacLike, safeRun } from "../common/util.ts";
import { Confirm, Prompt } from "./components/basic_modals.tsx";
import { CommandPalette } from "./components/command_palette.tsx";
import { FilterList } from "./components/filter.tsx";
import { PageNavigator } from "./components/page_navigator.tsx";
import { TopBar } from "./components/top_bar.tsx";
import reducer from "./reducer.ts";
import { Action, AppViewState, initialViewState } from "./types.ts";
import {
BookIcon,
HomeIcon,
preactRender,
TerminalIcon,
useEffect,
useReducer,
} from "./deps.ts";
import type { Editor } from "./editor.ts";
import { Panel } from "./components/panel.tsx";
import { h } from "./deps.ts";
export class MainUI {
viewState: AppViewState = initialViewState;
viewDispatch: (action: Action) => void = () => {};
constructor(private editor: Editor) {
}
ViewComponent() {
const [viewState, dispatch] = useReducer(reducer, initialViewState);
this.viewState = viewState;
this.viewDispatch = dispatch;
const editor = this.editor;
useEffect(() => {
if (viewState.currentPage) {
document.title = viewState.currentPage;
}
}, [viewState.currentPage]);
useEffect(() => {
if (editor.editorView) {
editor.tweakEditorDOM(
editor.editorView.contentDOM,
);
}
}, [viewState.uiOptions.forcedROMode]);
useEffect(() => {
this.editor.rebuildEditorState();
this.editor.dispatchAppEvent("editor:modeswitch");
}, [viewState.uiOptions.vimMode]);
useEffect(() => {
document.documentElement.dataset.theme = viewState.uiOptions.darkMode
? "dark"
: "light";
}, [viewState.uiOptions.darkMode]);
useEffect(() => {
// Need to dispatch a resize event so that the top_bar can pick it up
globalThis.dispatchEvent(new Event("resize"));
}, [viewState.panels]);
return (
<>
{viewState.showPageNavigator && (
<PageNavigator
allPages={viewState.allPages}
currentPage={editor.currentPage}
completer={editor.miniEditorComplete.bind(editor)}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
onNavigate={(page) => {
dispatch({ type: "stop-navigate" });
setTimeout(() => {
editor.focus();
});
if (page) {
safeRun(async () => {
await editor.navigate(page);
});
}
}}
/>
)}
{viewState.showCommandPalette && (
<CommandPalette
onTrigger={(cmd) => {
dispatch({ type: "hide-palette" });
setTimeout(() => {
editor.focus();
});
if (cmd) {
dispatch({ type: "command-run", command: cmd.command.name });
cmd
.run()
.catch((e: any) => {
console.error("Error running command", e.message);
})
.then(() => {
// Always be focusing the editor after running a command
editor.focus();
});
}
}}
commands={editor.getCommandsByContext(viewState)}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
completer={editor.miniEditorComplete.bind(editor)}
recentCommands={viewState.recentCommands}
/>
)}
{viewState.showFilterBox && (
<FilterList
label={viewState.filterBoxLabel}
placeholder={viewState.filterBoxPlaceHolder}
options={viewState.filterBoxOptions}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
allowNew={false}
completer={editor.miniEditorComplete.bind(editor)}
helpText={viewState.filterBoxHelpText}
onSelect={viewState.filterBoxOnSelect}
/>
)}
{viewState.showPrompt && (
<Prompt
message={viewState.promptMessage!}
defaultValue={viewState.promptDefaultValue}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
completer={editor.miniEditorComplete.bind(editor)}
callback={(value) => {
dispatch({ type: "hide-prompt" });
viewState.promptCallback!(value);
}}
/>
)}
{viewState.showConfirm && (
<Confirm
message={viewState.confirmMessage!}
callback={(value) => {
dispatch({ type: "hide-confirm" });
viewState.confirmCallback!(value);
}}
/>
)}
<TopBar
pageName={viewState.currentPage}
notifications={viewState.notifications}
synced={viewState.synced}
unsavedChanges={viewState.unsavedChanges}
isLoading={viewState.isLoading}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
progressPerc={viewState.progressPerc}
completer={editor.miniEditorComplete.bind(editor)}
onRename={async (newName) => {
if (!newName) {
// Always move cursor to the start of the page
editor.editorView?.dispatch({
selection: { anchor: 0 },
});
editor.focus();
return;
}
console.log("Now renaming page to...", newName);
await editor.system.system.loadedPlugs.get("core")!.invoke(
"renamePage",
[{ page: newName }],
);
editor.focus();
}}
actionButtons={[
{
icon: HomeIcon,
description: `Go home (Alt-h)`,
callback: () => {
editor.navigate("");
},
href: "",
},
{
icon: BookIcon,
description: `Open page (${isMacLike() ? "Cmd-k" : "Ctrl-k"})`,
callback: () => {
dispatch({ type: "start-navigate" });
editor.space.updatePageList();
},
},
{
icon: TerminalIcon,
description: `Run command (${isMacLike() ? "Cmd-/" : "Ctrl-/"})`,
callback: () => {
dispatch({
type: "show-palette",
context: editor.getContext(),
});
},
},
]}
rhs={!!viewState.panels.rhs.mode && (
<div
className="panel"
style={{ flex: viewState.panels.rhs.mode }}
/>
)}
lhs={!!viewState.panels.lhs.mode && (
<div
className="panel"
style={{ flex: viewState.panels.lhs.mode }}
/>
)}
/>
<div id="sb-main">
{!!viewState.panels.lhs.mode && (
<Panel config={viewState.panels.lhs} editor={editor} />
)}
<div id="sb-editor" />
{!!viewState.panels.rhs.mode && (
<Panel config={viewState.panels.rhs} editor={editor} />
)}
</div>
{!!viewState.panels.modal.mode && (
<div
className="sb-modal"
style={{ inset: `${viewState.panels.modal.mode}px` }}
>
<Panel config={viewState.panels.modal} editor={editor} />
</div>
)}
{!!viewState.panels.bhs.mode && (
<div className="sb-bhs">
<Panel config={viewState.panels.bhs} editor={editor} />
</div>
)}
</>
);
}
render(container: Element) {
// const ViewComponent = this.ui.ViewComponent.bind(this.ui);
preactRender(h(this.ViewComponent.bind(this), {}), container);
}
}

View File

@ -53,12 +53,15 @@ export class CommandHook extends EventEmitter<CommandHookEvents>
}
apply(system: System<CommandHookT>): void {
this.buildAllCommands(system);
system.on({
plugLoaded: () => {
this.buildAllCommands(system);
},
});
// On next tick
setTimeout(() => {
this.buildAllCommands(system);
});
}
validateManifest(manifest: Manifest<CommandHookT>): string[] {

View File

@ -2,7 +2,7 @@ import { Hook, Manifest } from "../../plugos/types.ts";
import { System } from "../../plugos/system.ts";
import { Completion, CompletionContext, CompletionResult } from "../deps.ts";
import { safeRun } from "../../common/util.ts";
import { Editor } from "../editor.tsx";
import { Editor } from "../editor.ts";
import { syntaxTree } from "../deps.ts";
export type SlashCommandDef = {

View File

@ -1,4 +1,4 @@
import { Editor } from "../editor.tsx";
import { Editor } from "../editor.ts";
import {
EditorView,
foldAll,
@ -81,14 +81,14 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
html: string,
script: string,
) => {
editor.viewDispatch({
editor.ui.viewDispatch({
type: "show-panel",
id: id as any,
config: { html, script, mode },
});
},
"editor.hidePanel": (_ctx, id: string) => {
editor.viewDispatch({
editor.ui.viewDispatch({
type: "hide-panel",
id: id as any,
});
@ -169,10 +169,10 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
return editor.confirm(message);
},
"editor.getUiOption": (_ctx, key: string): any => {
return (editor.viewState.uiOptions as any)[key];
return (editor.ui.viewState.uiOptions as any)[key];
},
"editor.setUiOption": (_ctx, key: string, value: any) => {
editor.viewDispatch({
editor.ui.viewDispatch({
type: "set-ui-option",
key,
value,

View File

@ -1,4 +1,4 @@
import { Editor } from "../editor.tsx";
import { Editor } from "../editor.ts";
import { SysCallMapping } from "../../plugos/system.ts";
import { AttachmentMeta, PageMeta } from "../types.ts";

View File

@ -1,5 +1,5 @@
import { SysCallMapping } from "../../plugos/system.ts";
import type { Editor } from "../editor.tsx";
import type { Editor } from "../editor.ts";
export function syncSyscalls(editor: Editor): SysCallMapping {
return {

View File

@ -1,6 +1,6 @@
import type { Plug } from "../../plugos/plug.ts";
import { SysCallMapping, System } from "../../plugos/system.ts";
import type { Editor } from "../editor.tsx";
import type { Editor } from "../editor.ts";
import { CommandDef } from "../hooks/command.ts";
export function systemSyscalls(