Fixes #207
parent
3545d00d46
commit
52f0c14c78
|
@ -0,0 +1,123 @@
|
|||
import type { CompletionContext, CompletionResult } from "../deps.ts";
|
||||
import { useRef, useState } from "../deps.ts";
|
||||
import { MiniEditor } from "./mini_editor.tsx";
|
||||
|
||||
export function Prompt({
|
||||
message,
|
||||
defaultValue,
|
||||
vimMode,
|
||||
darkMode,
|
||||
completer,
|
||||
callback,
|
||||
}: {
|
||||
message: string;
|
||||
defaultValue?: string;
|
||||
vimMode: boolean;
|
||||
darkMode: boolean;
|
||||
completer: (context: CompletionContext) => Promise<CompletionResult | null>;
|
||||
callback: (value?: string) => void;
|
||||
}) {
|
||||
const [text, setText] = useState(defaultValue || "");
|
||||
const returnEl = (
|
||||
<div className="sb-modal-wrapper">
|
||||
<div className="sb-modal-box">
|
||||
<div className="sb-prompt">
|
||||
<label>{message}</label>
|
||||
<MiniEditor
|
||||
text={defaultValue || ""}
|
||||
vimMode={vimMode}
|
||||
vimStartInInsertMode={true}
|
||||
focus={true}
|
||||
darkMode={darkMode}
|
||||
completer={completer}
|
||||
onEnter={(text) => {
|
||||
callback(text);
|
||||
return true;
|
||||
}}
|
||||
onEscape={() => {
|
||||
callback();
|
||||
}}
|
||||
onChange={(text) => {
|
||||
setText(text);
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
onClick={() => {
|
||||
callback(text);
|
||||
}}
|
||||
>
|
||||
Ok
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
callback();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return returnEl;
|
||||
}
|
||||
|
||||
export function Confirm({
|
||||
message,
|
||||
callback,
|
||||
}: {
|
||||
message: string;
|
||||
callback: (value: boolean) => void;
|
||||
}) {
|
||||
const okButtonRef = useRef<HTMLButtonElement>(null);
|
||||
setTimeout(() => {
|
||||
okButtonRef.current?.focus();
|
||||
});
|
||||
const returnEl = (
|
||||
<div className="sb-modal-wrapper">
|
||||
<div className="sb-modal-box">
|
||||
<div
|
||||
className="sb-prompt"
|
||||
onKeyDown={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
switch (e.key) {
|
||||
case "Enter":
|
||||
callback(true);
|
||||
break;
|
||||
case "Escape":
|
||||
callback(false);
|
||||
break;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<label>{message}</label>
|
||||
<div>
|
||||
<button
|
||||
ref={okButtonRef}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
callback(true);
|
||||
}}
|
||||
>
|
||||
Ok
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
callback(false);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return returnEl;
|
||||
}
|
|
@ -127,8 +127,8 @@ export function FilterList({
|
|||
}, []);
|
||||
|
||||
const returnEl = (
|
||||
<div className="sb-filter-wrapper">
|
||||
<div className="sb-filter-box">
|
||||
<div className="sb-modal-wrapper">
|
||||
<div className="sb-modal-box">
|
||||
<div className="sb-header">
|
||||
<label>{label}</label>
|
||||
<MiniEditor
|
||||
|
|
158
web/editor.tsx
158
web/editor.tsx
|
@ -1,15 +1,4 @@
|
|||
// Third party web dependencies
|
||||
import {
|
||||
BookIcon,
|
||||
HomeIcon,
|
||||
preactRender,
|
||||
TerminalIcon,
|
||||
useEffect,
|
||||
useReducer,
|
||||
yUndoManagerKeymap,
|
||||
} from "./deps.ts";
|
||||
|
||||
// Third-party dependencies
|
||||
import {
|
||||
autocompletion,
|
||||
closeBrackets,
|
||||
|
@ -32,6 +21,7 @@ import {
|
|||
keymap,
|
||||
LanguageDescription,
|
||||
LanguageSupport,
|
||||
markdown,
|
||||
runScopeHandlers,
|
||||
searchKeymap,
|
||||
standardKeymap,
|
||||
|
@ -43,9 +33,7 @@ import {
|
|||
ViewUpdate,
|
||||
yamlLanguage,
|
||||
} from "../common/deps.ts";
|
||||
|
||||
import { SilverBulletHooks } from "../common/manifest.ts";
|
||||
import { markdown } from "../common/deps.ts";
|
||||
import {
|
||||
loadMarkdownExtensions,
|
||||
MDExt,
|
||||
|
@ -55,50 +43,14 @@ import { Space } from "../common/spaces/space.ts";
|
|||
import { markdownSyscalls } from "../common/syscalls/markdown.ts";
|
||||
import { FilterOption, PageMeta } from "../common/types.ts";
|
||||
import { isMacLike, safeRun, throttle } from "../common/util.ts";
|
||||
|
||||
import { PathPageNavigator } from "./navigator.ts";
|
||||
import reducer from "./reducer.ts";
|
||||
|
||||
// PlugOS Dependencies
|
||||
import { createSandbox } from "../plugos/environments/webworker_sandbox.ts";
|
||||
import { EventHook } from "../plugos/hooks/event.ts";
|
||||
import assetSyscalls from "../plugos/syscalls/asset.ts";
|
||||
import { eventSyscalls } from "../plugos/syscalls/event.ts";
|
||||
import sandboxSyscalls from "../plugos/syscalls/sandbox.ts";
|
||||
import { System } from "../plugos/system.ts";
|
||||
import { CommandHook } from "./hooks/command.ts";
|
||||
import { SlashCommandHook } from "./hooks/slash_command.ts";
|
||||
|
||||
// Syscalls
|
||||
import { clientStoreSyscalls } from "./syscalls/clientStore.ts";
|
||||
import { editorSyscalls } from "./syscalls/editor.ts";
|
||||
import { fulltextSyscalls } from "./syscalls/fulltext.ts";
|
||||
import { indexerSyscalls } from "./syscalls/index.ts";
|
||||
import { spaceSyscalls } from "./syscalls/space.ts";
|
||||
import { storeSyscalls } from "./syscalls/store.ts";
|
||||
import { systemSyscalls } from "./syscalls/system.ts";
|
||||
import assetSyscalls from "../plugos/syscalls/asset.ts";
|
||||
|
||||
// State and state transitions
|
||||
import {
|
||||
Action,
|
||||
AppViewState,
|
||||
BuiltinSettings,
|
||||
initialViewState,
|
||||
} from "./types.ts";
|
||||
import type {
|
||||
AppEvent,
|
||||
ClickEvent,
|
||||
CompleteEvent,
|
||||
} from "../plug-api/app_event.ts";
|
||||
|
||||
// UI Components
|
||||
import { CommandPalette } from "./components/command_palette.tsx";
|
||||
import { FilterList } from "./components/filter.tsx";
|
||||
import { PageNavigator } from "./components/page_navigator.tsx";
|
||||
import { Panel } from "./components/panel.tsx";
|
||||
import { TopBar } from "./components/top_bar.tsx";
|
||||
|
||||
// CodeMirror plugins
|
||||
import { cleanModePlugins } from "./cm_plugins/clean.ts";
|
||||
import { CollabState } from "./cm_plugins/collab.ts";
|
||||
import {
|
||||
attachmentExtension,
|
||||
pasteLinkExtension,
|
||||
|
@ -106,14 +58,50 @@ import {
|
|||
import { inlineImagesPlugin } from "./cm_plugins/inline_image.ts";
|
||||
import { lineWrapper } from "./cm_plugins/line_wrapper.ts";
|
||||
import { smartQuoteKeymap } from "./cm_plugins/smart_quotes.ts";
|
||||
import { cleanModePlugins } from "./cm_plugins/clean.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 { Panel } from "./components/panel.tsx";
|
||||
import { TopBar } from "./components/top_bar.tsx";
|
||||
import {
|
||||
BookIcon,
|
||||
HomeIcon,
|
||||
preactRender,
|
||||
TerminalIcon,
|
||||
useEffect,
|
||||
useReducer,
|
||||
vim,
|
||||
yUndoManagerKeymap,
|
||||
} from "./deps.ts";
|
||||
import { CommandHook } from "./hooks/command.ts";
|
||||
import { SlashCommandHook } from "./hooks/slash_command.ts";
|
||||
import { PathPageNavigator } from "./navigator.ts";
|
||||
import reducer from "./reducer.ts";
|
||||
import customMarkdownStyle from "./style.ts";
|
||||
|
||||
// Real-time collaboration
|
||||
import { CollabState } from "./cm_plugins/collab.ts";
|
||||
import { clientStoreSyscalls } from "./syscalls/clientStore.ts";
|
||||
import { collabSyscalls } from "./syscalls/collab.ts";
|
||||
import { Vim, vim, vimGetCm } from "./deps.ts";
|
||||
import { editorSyscalls } from "./syscalls/editor.ts";
|
||||
import { fulltextSyscalls } from "./syscalls/fulltext.ts";
|
||||
import { indexerSyscalls } from "./syscalls/index.ts";
|
||||
import { spaceSyscalls } from "./syscalls/space.ts";
|
||||
import { storeSyscalls } from "./syscalls/store.ts";
|
||||
import { systemSyscalls } from "./syscalls/system.ts";
|
||||
import { AppViewState, BuiltinSettings, initialViewState } from "./types.ts";
|
||||
|
||||
// Third-party dependencies
|
||||
// PlugOS Dependencies
|
||||
// Syscalls
|
||||
// State and state transitions
|
||||
import type {
|
||||
AppEvent,
|
||||
ClickEvent,
|
||||
CompleteEvent,
|
||||
} from "../plug-api/app_event.ts";
|
||||
|
||||
// UI Components
|
||||
// CodeMirror plugins
|
||||
// Real-time collaboration
|
||||
const frontMatterRegex = /^---\n(.*?)---\n/ms;
|
||||
|
||||
class PageState {
|
||||
|
@ -423,6 +411,40 @@ export class Editor {
|
|||
});
|
||||
}
|
||||
|
||||
prompt(
|
||||
message: string,
|
||||
defaultValue = "",
|
||||
): Promise<string | undefined> {
|
||||
return new Promise((resolve) => {
|
||||
this.viewDispatch({
|
||||
type: "show-prompt",
|
||||
message,
|
||||
defaultValue,
|
||||
callback: (value: string | undefined) => {
|
||||
this.viewDispatch({ type: "hide-prompt" });
|
||||
this.focus();
|
||||
resolve(value);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
confirm(
|
||||
message: string,
|
||||
): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
this.viewDispatch({
|
||||
type: "show-confirm",
|
||||
message,
|
||||
callback: (value: boolean) => {
|
||||
this.viewDispatch({ type: "hide-confirm" });
|
||||
this.focus();
|
||||
resolve(value);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dispatchAppEvent(name: AppEvent, data?: any): Promise<any[]> {
|
||||
return this.eventHook.dispatchEvent(name, data);
|
||||
}
|
||||
|
@ -924,6 +946,28 @@ export class Editor {
|
|||
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}
|
||||
|
|
|
@ -139,6 +139,36 @@ export default function reducer(
|
|||
filterBoxOptions: [],
|
||||
filterBoxHelpText: "",
|
||||
};
|
||||
case "show-prompt":
|
||||
return {
|
||||
...state,
|
||||
showPrompt: true,
|
||||
promptDefaultValue: action.defaultValue,
|
||||
promptMessage: action.message,
|
||||
promptCallback: action.callback,
|
||||
};
|
||||
case "hide-prompt":
|
||||
return {
|
||||
...state,
|
||||
showPrompt: false,
|
||||
promptDefaultValue: undefined,
|
||||
promptMessage: undefined,
|
||||
promptCallback: undefined,
|
||||
};
|
||||
case "show-confirm":
|
||||
return {
|
||||
...state,
|
||||
showConfirm: true,
|
||||
confirmMessage: action.message,
|
||||
confirmCallback: action.callback,
|
||||
};
|
||||
case "hide-confirm":
|
||||
return {
|
||||
...state,
|
||||
showConfirm: false,
|
||||
confirmMessage: undefined,
|
||||
confirmCallback: undefined,
|
||||
};
|
||||
case "set-ui-option":
|
||||
return {
|
||||
...state,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@use "editor";
|
||||
@use "filter_box";
|
||||
@use "modals";
|
||||
@use "theme";
|
||||
|
||||
@font-face {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.sb-filter-wrapper {
|
||||
.sb-modal-wrapper {
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
max-width: 500px;
|
||||
|
@ -11,7 +11,7 @@
|
|||
z-index: 100;
|
||||
}
|
||||
|
||||
.sb-filter-box {
|
||||
.sb-modal-box {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin: 10px;
|
||||
|
@ -27,6 +27,19 @@
|
|||
|
||||
}
|
||||
|
||||
.sb-prompt {
|
||||
padding: 13px 10px 10px 10px;
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sb-mini-editor {
|
||||
border: 1px solid #333;
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sb-help-text {
|
||||
padding: 5px;
|
||||
}
|
||||
|
@ -46,9 +59,7 @@
|
|||
.sb-selected-option {
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
// height: 20px;
|
||||
line-height: 20px;
|
||||
// white-space: nowrap;
|
||||
}
|
||||
|
||||
.sb-selected-option {
|
|
@ -69,7 +69,7 @@
|
|||
}
|
||||
|
||||
/* Filter boxes */
|
||||
.sb-filter-box {
|
||||
.sb-modal-box {
|
||||
background-color: #fff;
|
||||
border: rgb(103, 103, 103) 1px solid;
|
||||
box-shadow: rgba(0, 0, 0, 0.35) 0px 20px 20px;
|
||||
|
@ -87,29 +87,29 @@
|
|||
}
|
||||
}
|
||||
|
||||
.sb-filter-box .sb-header {
|
||||
.sb-modal-box .sb-header {
|
||||
border-bottom: 1px rgb(108, 108, 108) solid;
|
||||
}
|
||||
|
||||
.sb-filter-box .sb-header .sb-mini-editor {
|
||||
.sb-modal-box .sb-header .sb-mini-editor {
|
||||
font-family: var(--ui-font);
|
||||
width: 100%;
|
||||
border: 0;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.sb-filter-box .sb-help-text {
|
||||
.sb-modal-box .sb-help-text {
|
||||
background-color: #eee;
|
||||
border-bottom: 1px rgb(108, 108, 108) solid;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.sb-filter-box .sb-selected-option {
|
||||
.sb-modal-box .sb-selected-option {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.sb-filter-box .sb-option .sb-hint,
|
||||
.sb-filter-box .sb-selected-option .sb-hint {
|
||||
.sb-modal-box .sb-option .sb-hint,
|
||||
.sb-modal-box .sb-selected-option .sb-hint {
|
||||
color: #eee;
|
||||
background-color: #212476;
|
||||
}
|
||||
|
@ -570,7 +570,7 @@ html[data-theme="dark"] {
|
|||
|
||||
|
||||
|
||||
.sb-filter-box,
|
||||
.sb-modal-box,
|
||||
/* duplicating the class name to increase specificity */
|
||||
.sb-help-text.sb-help-text {
|
||||
color: #ccc;
|
||||
|
|
|
@ -139,14 +139,14 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
|||
_ctx,
|
||||
message: string,
|
||||
defaultValue = "",
|
||||
): string | null => {
|
||||
return prompt(message, defaultValue);
|
||||
): Promise<string | undefined> => {
|
||||
return editor.prompt(message, defaultValue);
|
||||
},
|
||||
"editor.confirm": (
|
||||
_ctx,
|
||||
message: string,
|
||||
): boolean => {
|
||||
return confirm(message);
|
||||
): Promise<boolean> => {
|
||||
return editor.confirm(message);
|
||||
},
|
||||
"editor.getUiOption": (_ctx, key: string): any => {
|
||||
return (editor.viewState.uiOptions as any)[key];
|
||||
|
|
28
web/types.ts
28
web/types.ts
|
@ -42,12 +42,24 @@ export type AppViewState = {
|
|||
forcedROMode: boolean;
|
||||
};
|
||||
|
||||
// Filter box
|
||||
showFilterBox: boolean;
|
||||
filterBoxLabel: string;
|
||||
filterBoxPlaceHolder: string;
|
||||
filterBoxOptions: FilterOption[];
|
||||
filterBoxHelpText: string;
|
||||
filterBoxOnSelect: (option: FilterOption | undefined) => void;
|
||||
|
||||
// Prompt
|
||||
showPrompt: boolean;
|
||||
promptMessage?: string;
|
||||
promptDefaultValue?: string;
|
||||
promptCallback?: (value: string | undefined) => void;
|
||||
|
||||
// Confirm
|
||||
showConfirm: boolean;
|
||||
confirmMessage?: string;
|
||||
confirmCallback?: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export const initialViewState: AppViewState = {
|
||||
|
@ -78,6 +90,9 @@ export const initialViewState: AppViewState = {
|
|||
filterBoxOnSelect: () => {},
|
||||
filterBoxOptions: [],
|
||||
filterBoxPlaceHolder: "",
|
||||
|
||||
showPrompt: false,
|
||||
showConfirm: false,
|
||||
};
|
||||
|
||||
export type Action =
|
||||
|
@ -112,4 +127,17 @@ export type Action =
|
|||
onSelect: (option: FilterOption | undefined) => void;
|
||||
}
|
||||
| { type: "hide-filterbox" }
|
||||
| {
|
||||
type: "show-prompt";
|
||||
message: string;
|
||||
defaultValue: string;
|
||||
callback: (value: string | undefined) => void;
|
||||
}
|
||||
| { type: "hide-prompt" }
|
||||
| {
|
||||
type: "show-confirm";
|
||||
message: string;
|
||||
callback: (value: boolean) => void;
|
||||
}
|
||||
| { type: "hide-confirm" }
|
||||
| { type: "set-ui-option"; key: string; value: any };
|
||||
|
|
Loading…
Reference in New Issue