Tons of progress
parent
aa7929ea29
commit
5e5968f09e
|
@ -5,6 +5,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../server"
|
"path": "../server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../plugin-bundler"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "webapp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "plugins"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "server"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
dist
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"deno.enable": true,
|
||||||
|
"deno.unstable": true
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
DENO_BUNDLE=deno run --allow-read --allow-write --unstable bundle.ts --debug
|
||||||
|
build: *
|
||||||
|
mkdir -p dist
|
||||||
|
$(DENO_BUNDLE) core/core.plugin.json dist/core.plugin.json
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
import { parse } from "https://deno.land/std@0.121.0/flags/mod.ts";
|
||||||
|
|
||||||
|
// import { mime } from "https://deno.land/x/mimetypes@v1.0.0/mod.ts";
|
||||||
|
//
|
||||||
|
// async function dataEncodeUint8Array(path : string, data: Uint8Array): Promise<string> {
|
||||||
|
// const base64url: string = await new Promise((r) => {
|
||||||
|
// const reader = new FileReader();
|
||||||
|
// reader.onload = () => r(reader.result as string);
|
||||||
|
// reader.readAsDataURL(new Blob([data]))
|
||||||
|
// })
|
||||||
|
// let [meta, content] = base64url.split(';');
|
||||||
|
// let [prefix, mimeType] = meta.split(':');
|
||||||
|
// return `data:${mime.getType(path)};${content}`;
|
||||||
|
// }
|
||||||
|
import * as path from "https://deno.land/std@0.121.0/path/mod.ts";
|
||||||
|
import { Manifest, FunctionDef } from "../webapp/src/plugins/types.ts";
|
||||||
|
|
||||||
|
async function compile(
|
||||||
|
filePath: string,
|
||||||
|
prettyFunctionName: string,
|
||||||
|
jsFunctionName: string,
|
||||||
|
sourceMaps: boolean
|
||||||
|
): Promise<string> {
|
||||||
|
// @ts-ignore for Deno.emit (unstable API)
|
||||||
|
let { files, diagnostics } = await Deno.emit(filePath, {
|
||||||
|
bundle: "classic",
|
||||||
|
check: true,
|
||||||
|
compilerOptions: {
|
||||||
|
lib: ["WebWorker", "ES2020"],
|
||||||
|
inlineSourceMap: sourceMaps,
|
||||||
|
sourceMap: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let bundleSource = files["deno:///bundle.js"];
|
||||||
|
|
||||||
|
if (diagnostics.length > 0) {
|
||||||
|
for (let diagnostic of diagnostics) {
|
||||||
|
if (diagnostic.start) {
|
||||||
|
console.error(
|
||||||
|
`In ${diagnostic.fileName}:${diagnostic.start!.line + 1}: ${
|
||||||
|
diagnostic.messageText
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("Diagnostics");
|
||||||
|
}
|
||||||
|
return `const mod = ${bundleSource}
|
||||||
|
|
||||||
|
self.addEventListener('invoke-function', async e => {
|
||||||
|
try {
|
||||||
|
let result = await mod['${jsFunctionName}'](...e.detail.args);
|
||||||
|
self.dispatchEvent(new CustomEvent('result', {detail: result}));
|
||||||
|
} catch(e) {
|
||||||
|
console.error(\`Error while running ${jsFunctionName}\`, e);
|
||||||
|
self.dispatchEvent(new CustomEvent('app-error', {detail: e.message}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bundle(
|
||||||
|
manifestPath: string,
|
||||||
|
sourceMaps: boolean
|
||||||
|
): Promise<Manifest> {
|
||||||
|
const rootPath = path.dirname(manifestPath);
|
||||||
|
const manifest = JSON.parse(
|
||||||
|
new TextDecoder().decode(await Deno.readFile(manifestPath))
|
||||||
|
) as Manifest;
|
||||||
|
|
||||||
|
for (let [name, def] of Object.entries(manifest.functions) as Array<
|
||||||
|
[string, FunctionDef]
|
||||||
|
>) {
|
||||||
|
let jsFunctionName,
|
||||||
|
filePath = path.join(rootPath, def.path);
|
||||||
|
if (filePath.indexOf(":") !== 0) {
|
||||||
|
[filePath, jsFunctionName] = filePath.split(":");
|
||||||
|
} else {
|
||||||
|
jsFunctionName = "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
def.code = await compile(filePath, name, jsFunctionName, sourceMaps);
|
||||||
|
}
|
||||||
|
return manifest;
|
||||||
|
// let files: { [key: string]: string } = {};
|
||||||
|
// for await (const entry of walk(path, {includeDirs: false})) {
|
||||||
|
// let content = await Deno.readFile(entry.path);
|
||||||
|
// files[entry.path.substring(path.length + 1)] = await dataEncodeUint8Array(entry.path, content);
|
||||||
|
// }
|
||||||
|
// return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
let commandLineArguments = parse(Deno.args, {
|
||||||
|
boolean: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let [manifestPath, outputPath] = commandLineArguments._ as string[];
|
||||||
|
console.log(`Generating bundle for ${manifestPath} to ${outputPath}`);
|
||||||
|
let b = await bundle(manifestPath, !!commandLineArguments.debug);
|
||||||
|
await Deno.writeFile(
|
||||||
|
outputPath,
|
||||||
|
new TextEncoder().encode(JSON.stringify(b, null, 2))
|
||||||
|
);
|
||||||
|
/*
|
||||||
|
const watcher = Deno.watchFs("test_app");
|
||||||
|
|
||||||
|
for await (const event of watcher) {
|
||||||
|
console.log("Updating bundle...");
|
||||||
|
let b = await bundle("test_app/test.cartridge.json");
|
||||||
|
await Deno.writeFile("test_app.bundle.json", new TextEncoder().encode(JSON.stringify(b, null, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"commands": {
|
||||||
|
"Count Words": {
|
||||||
|
"invoke": "word_count_command",
|
||||||
|
"requiredContext": {
|
||||||
|
"text": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Navigate To page": {
|
||||||
|
"invoke": "link_navigate",
|
||||||
|
"key": "Ctrl-Enter",
|
||||||
|
"mac": "Cmd-Enter",
|
||||||
|
"requiredContext": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Insert Current Date": {
|
||||||
|
"invoke": "insert_nice_date"
|
||||||
|
},
|
||||||
|
"Toggle : Heading 1": {
|
||||||
|
"invoke": "toggle_h1",
|
||||||
|
"mac": "Cmd-1",
|
||||||
|
"key": "Ctrl-1"
|
||||||
|
},
|
||||||
|
"Toggle : Heading 2": {
|
||||||
|
"invoke": "toggle_h2",
|
||||||
|
"mac": "Cmd-2",
|
||||||
|
"key": "Ctrl-2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"events": {},
|
||||||
|
"functions": {
|
||||||
|
"word_count_command": {
|
||||||
|
"path": "./word_count_command.ts:wordCount"
|
||||||
|
},
|
||||||
|
"link_navigate": {
|
||||||
|
"path": "./link_navigate.ts:linkNavigate"
|
||||||
|
},
|
||||||
|
"insert_nice_date": {
|
||||||
|
"path": "./dates.ts:insertToday"
|
||||||
|
},
|
||||||
|
"toggle_h1": {
|
||||||
|
"path": "./markup.ts:toggleH1"
|
||||||
|
},
|
||||||
|
"toggle_h2": {
|
||||||
|
"path": "./markup.ts:toggleH2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { syscall } from "./lib/syscall.ts";
|
||||||
|
|
||||||
|
export async function insertToday() {
|
||||||
|
let niceDate = new Date().toISOString().split("T")[0];
|
||||||
|
await syscall("editor.insertAtCursor", niceDate);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import {syscall} from "./syscall.ts";
|
||||||
|
|
||||||
|
export async function put(key: string, value: any) {
|
||||||
|
return await syscall("db.put", key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get(key: string) {
|
||||||
|
return await syscall("db.get", key);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import {syscall} from "./syscall.ts";
|
||||||
|
|
||||||
|
export async function publish(event: string, data?: object) {
|
||||||
|
return await syscall("event.publish", event, data);
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
export function syscall(name: string, ...args: Array<any>): any {
|
||||||
|
let reqId = Math.floor(Math.random() * 1000000);
|
||||||
|
// console.log("Syscall", name, reqId);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
self.dispatchEvent(
|
||||||
|
new CustomEvent("syscall", {
|
||||||
|
detail: {
|
||||||
|
id: reqId,
|
||||||
|
name: name,
|
||||||
|
args: args,
|
||||||
|
callback: resolve,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { syscall } from "./lib/syscall.ts";
|
||||||
|
|
||||||
|
export async function linkNavigate({ text }: { text: string }) {
|
||||||
|
let syntaxNode = await syscall("editor.getSyntaxNodeUnderCursor");
|
||||||
|
if (syntaxNode && syntaxNode.name === "WikiLinkPage") {
|
||||||
|
await syscall("editor.navigate", syntaxNode.text);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { syscall } from "./lib/syscall.ts";
|
||||||
|
|
||||||
|
export async function toggleH1() {
|
||||||
|
await togglePrefix("# ");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function toggleH2() {
|
||||||
|
await togglePrefix("## ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookBack(s: string, pos: number, backString: string): boolean {
|
||||||
|
return s.substring(pos - backString.length, pos) === backString;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function togglePrefix(prefix: string) {
|
||||||
|
let text = (await syscall("editor.getText")) as string;
|
||||||
|
let pos = (await syscall("editor.getCursor")) as number;
|
||||||
|
if (text[pos] === "\n") {
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
while (pos > 0 && text[pos] !== "\n") {
|
||||||
|
if (lookBack(text, pos, prefix)) {
|
||||||
|
// Already has this prefix, let's flip it
|
||||||
|
await syscall("editor.replaceRange", pos - prefix.length, pos, "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
if (pos) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
await syscall("editor.insertAtPos", prefix, pos);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
function countWords(str: string): number {
|
||||||
|
var matches = str.match(/[\w\d\'\'-]+/gi);
|
||||||
|
return matches ? matches.length : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readingTime(wordCount: number): number {
|
||||||
|
// 225 is average word reading speed for adults
|
||||||
|
return Math.ceil(wordCount / 225);
|
||||||
|
}
|
||||||
|
|
||||||
|
import { syscall } from "./lib/syscall.ts";
|
||||||
|
|
||||||
|
export async function wordCount({ text }: { text: string }) {
|
||||||
|
let sysCallText = (await syscall("editor.getText")) as string;
|
||||||
|
const count = countWords(sysCallText);
|
||||||
|
console.log("Word count", count);
|
||||||
|
let syntaxNode = await syscall("editor.getSyntaxNodeUnderCursor");
|
||||||
|
console.log("Syntax node", syntaxNode);
|
||||||
|
return count;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
deno run --allow-net --allow-read --allow-write server.ts
|
Binary file not shown.
|
@ -0,0 +1,2 @@
|
||||||
|
dist
|
||||||
|
node_modules
|
|
@ -22,6 +22,8 @@
|
||||||
"@codemirror/lang-markdown": "^0.19.6",
|
"@codemirror/lang-markdown": "^0.19.6",
|
||||||
"@codemirror/state": "^0.19.7",
|
"@codemirror/state": "^0.19.7",
|
||||||
"@codemirror/view": "^0.19.42",
|
"@codemirror/view": "^0.19.42",
|
||||||
|
"@parcel/service-worker": "^2.3.2",
|
||||||
|
"idb": "^7.0.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2"
|
"react-dom": "^17.0.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Editor } from "./editor";
|
||||||
|
import { AppCommand, CommandContext } from "./types";
|
||||||
|
|
||||||
|
export function buildContext(cmd: AppCommand, editor: Editor) {
|
||||||
|
let ctx: CommandContext = {};
|
||||||
|
if (!cmd.command.requiredContext) {
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
if (cmd.command.requiredContext.text) {
|
||||||
|
ctx.text = editor.editorView?.state.sliceDoc();
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
|
@ -1,20 +1,29 @@
|
||||||
import { AppCommand } from "../types";
|
import { AppCommand } from "../types";
|
||||||
import { FilterList } from "./filter";
|
import { FilterList, Option } from "./filter";
|
||||||
|
|
||||||
export function CommandPalette({
|
export function CommandPalette({
|
||||||
commands,
|
commands,
|
||||||
onTrigger,
|
onTrigger,
|
||||||
}: {
|
}: {
|
||||||
commands: AppCommand[];
|
commands: Map<string, AppCommand>;
|
||||||
onTrigger: (command: AppCommand) => void;
|
onTrigger: (command: AppCommand | undefined) => void;
|
||||||
}) {
|
}) {
|
||||||
|
let options: Option[] = [];
|
||||||
|
for (let [name, def] of commands.entries()) {
|
||||||
|
options.push({ name: name });
|
||||||
|
}
|
||||||
|
console.log("Commands", options);
|
||||||
return (
|
return (
|
||||||
<FilterList
|
<FilterList
|
||||||
placeholder="Enter command to run"
|
placeholder="Enter command to run"
|
||||||
options={commands}
|
options={options}
|
||||||
allowNew={false}
|
allowNew={false}
|
||||||
onSelect={(opt) => {
|
onSelect={(opt) => {
|
||||||
onTrigger(opt as AppCommand);
|
if (opt) {
|
||||||
|
onTrigger(commands.get(opt.name));
|
||||||
|
} else {
|
||||||
|
onTrigger(undefined);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
import {
|
||||||
|
autocompletion,
|
||||||
|
CompletionContext,
|
||||||
|
completionKeymap,
|
||||||
|
CompletionResult,
|
||||||
|
} from "@codemirror/autocomplete";
|
||||||
import { closeBrackets, closeBracketsKeymap } from "@codemirror/closebrackets";
|
import { closeBrackets, closeBracketsKeymap } from "@codemirror/closebrackets";
|
||||||
import { indentWithTab, standardKeymap } from "@codemirror/commands";
|
import { indentWithTab, standardKeymap } from "@codemirror/commands";
|
||||||
import { history, historyKeymap } from "@codemirror/history";
|
import { history, historyKeymap } from "@codemirror/history";
|
||||||
import { indentOnInput } from "@codemirror/language";
|
import { indentOnInput, syntaxTree } from "@codemirror/language";
|
||||||
import { bracketMatching } from "@codemirror/matchbrackets";
|
import { bracketMatching } from "@codemirror/matchbrackets";
|
||||||
import { searchKeymap } from "@codemirror/search";
|
import { searchKeymap } from "@codemirror/search";
|
||||||
import { EditorState, StateField, Transaction } from "@codemirror/state";
|
import { EditorState, StateField, Transaction } from "@codemirror/state";
|
||||||
|
import { KeyBinding } from "@codemirror/view";
|
||||||
import {
|
import {
|
||||||
drawSelection,
|
drawSelection,
|
||||||
dropCursor,
|
dropCursor,
|
||||||
|
@ -13,33 +19,33 @@ import {
|
||||||
highlightSpecialChars,
|
highlightSpecialChars,
|
||||||
keymap,
|
keymap,
|
||||||
} from "@codemirror/view";
|
} from "@codemirror/view";
|
||||||
import React, { useEffect, useReducer, useRef } from "react";
|
import React, { useEffect, useReducer } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
|
import coreManifest from "../../plugins/dist/core.plugin.json";
|
||||||
|
import { buildContext } from "./buildContext";
|
||||||
import * as commands from "./commands";
|
import * as commands from "./commands";
|
||||||
import { CommandPalette } from "./components/commandpalette";
|
import { CommandPalette } from "./components/commandpalette";
|
||||||
|
import { NavigationBar } from "./components/navigation_bar";
|
||||||
import { NoteNavigator } from "./components/notenavigator";
|
import { NoteNavigator } from "./components/notenavigator";
|
||||||
|
import { StatusBar } from "./components/status_bar";
|
||||||
import { FileSystem, HttpFileSystem } from "./fs";
|
import { FileSystem, HttpFileSystem } from "./fs";
|
||||||
import { lineWrapper } from "./lineWrapper";
|
import { lineWrapper } from "./lineWrapper";
|
||||||
import { markdown } from "./markdown";
|
import { markdown } from "./markdown";
|
||||||
import customMarkDown from "./parser";
|
import customMarkDown from "./parser";
|
||||||
|
import { BrowserSystem } from "./plugins/browser_system";
|
||||||
|
import { Manifest } from "./plugins/types";
|
||||||
import reducer from "./reducer";
|
import reducer from "./reducer";
|
||||||
import customMarkdownStyle from "./style";
|
import customMarkdownStyle from "./style";
|
||||||
import { Action, AppViewState } from "./types";
|
import dbSyscalls from "./syscalls/db.localstorage";
|
||||||
|
import editorSyscalls from "./syscalls/editor.browser";
|
||||||
import { syntaxTree } from "@codemirror/language";
|
import {
|
||||||
import * as util from "./util";
|
Action,
|
||||||
import { NoteMeta } from "./types";
|
AppCommand,
|
||||||
|
AppViewState,
|
||||||
const initialViewState: AppViewState = {
|
CommandContext,
|
||||||
isSaved: false,
|
initialViewState,
|
||||||
showNoteNavigator: false,
|
} from "./types";
|
||||||
showCommandPalette: false,
|
import { safeRun } from "./util";
|
||||||
allNotes: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
import { CompletionContext, CompletionResult } from "@codemirror/autocomplete";
|
|
||||||
import { NavigationBar } from "./components/navigation_bar";
|
|
||||||
import { StatusBar } from "./components/status_bar";
|
|
||||||
|
|
||||||
class NoteState {
|
class NoteState {
|
||||||
editorState: EditorState;
|
editorState: EditorState;
|
||||||
|
@ -51,15 +57,18 @@ class NoteState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Editor {
|
export class Editor {
|
||||||
editorView?: EditorView;
|
editorView?: EditorView;
|
||||||
viewState: AppViewState;
|
viewState: AppViewState;
|
||||||
viewDispatch: React.Dispatch<Action>;
|
viewDispatch: React.Dispatch<Action>;
|
||||||
$hashChange?: () => void;
|
$hashChange?: () => void;
|
||||||
openNotes: Map<string, NoteState>;
|
openNotes: Map<string, NoteState>;
|
||||||
fs: FileSystem;
|
fs: FileSystem;
|
||||||
|
editorCommands: Map<string, AppCommand>;
|
||||||
|
|
||||||
constructor(fs: FileSystem, parent: Element) {
|
constructor(fs: FileSystem, parent: Element) {
|
||||||
|
this.editorCommands = new Map();
|
||||||
|
this.openNotes = new Map();
|
||||||
this.fs = fs;
|
this.fs = fs;
|
||||||
this.viewState = initialViewState;
|
this.viewState = initialViewState;
|
||||||
this.viewDispatch = () => {};
|
this.viewDispatch = () => {};
|
||||||
|
@ -69,9 +78,37 @@ class Editor {
|
||||||
parent: document.getElementById("editor")!,
|
parent: document.getElementById("editor")!,
|
||||||
});
|
});
|
||||||
this.addListeners();
|
this.addListeners();
|
||||||
this.loadNoteList();
|
}
|
||||||
this.openNotes = new Map();
|
|
||||||
|
async init() {
|
||||||
|
await this.loadNoteList();
|
||||||
|
await this.loadPlugins();
|
||||||
this.$hashChange!();
|
this.$hashChange!();
|
||||||
|
this.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadPlugins() {
|
||||||
|
const system = new BrowserSystem("plugin");
|
||||||
|
system.registerSyscalls(dbSyscalls, editorSyscalls(this));
|
||||||
|
|
||||||
|
await system.bootServiceWorker();
|
||||||
|
console.log("Now loading core plugin");
|
||||||
|
let mainCartridge = await system.load("core", coreManifest as Manifest);
|
||||||
|
this.editorCommands = new Map<string, AppCommand>();
|
||||||
|
const cmds = mainCartridge.manifest!.commands;
|
||||||
|
for (let name in cmds) {
|
||||||
|
let cmd = cmds[name];
|
||||||
|
this.editorCommands.set(name, {
|
||||||
|
command: cmd,
|
||||||
|
run: async (arg: CommandContext): Promise<any> => {
|
||||||
|
return await mainCartridge.invoke(cmd.invoke, [arg]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.viewDispatch({
|
||||||
|
type: "update-commands",
|
||||||
|
commands: this.editorCommands,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentNote(): string | undefined {
|
get currentNote(): string | undefined {
|
||||||
|
@ -80,6 +117,23 @@ class Editor {
|
||||||
|
|
||||||
createEditorState(text: string): EditorState {
|
createEditorState(text: string): EditorState {
|
||||||
const editor = this;
|
const editor = this;
|
||||||
|
let commandKeyBindings: KeyBinding[] = [];
|
||||||
|
for (let def of this.editorCommands.values()) {
|
||||||
|
if (def.command.key) {
|
||||||
|
commandKeyBindings.push({
|
||||||
|
key: def.command.key,
|
||||||
|
mac: def.command.mac,
|
||||||
|
run: (): boolean => {
|
||||||
|
Promise.resolve()
|
||||||
|
.then(async () => {
|
||||||
|
await def.run(buildContext(def, this));
|
||||||
|
})
|
||||||
|
.catch((e) => console.error(e));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
return EditorState.create({
|
return EditorState.create({
|
||||||
doc: text,
|
doc: text,
|
||||||
extensions: [
|
extensions: [
|
||||||
|
@ -110,6 +164,7 @@ class Editor {
|
||||||
...historyKeymap,
|
...historyKeymap,
|
||||||
...completionKeymap,
|
...completionKeymap,
|
||||||
indentWithTab,
|
indentWithTab,
|
||||||
|
...commandKeyBindings,
|
||||||
{
|
{
|
||||||
key: "Ctrl-b",
|
key: "Ctrl-b",
|
||||||
mac: "Cmd-b",
|
mac: "Cmd-b",
|
||||||
|
@ -133,25 +188,6 @@ class Editor {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: "Ctrl-Enter",
|
|
||||||
mac: "Cmd-Enter",
|
|
||||||
run: (target): boolean => {
|
|
||||||
// TODO: Factor this and click handler into one action
|
|
||||||
let selection = target.state.selection.main;
|
|
||||||
if (selection.empty) {
|
|
||||||
let node = syntaxTree(target.state).resolveInner(
|
|
||||||
selection.from
|
|
||||||
);
|
|
||||||
if (node && node.name === "WikiLinkPage") {
|
|
||||||
let noteName = target.state.sliceDoc(node.from, node.to);
|
|
||||||
this.navigate(noteName);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: "Ctrl-p",
|
key: "Ctrl-p",
|
||||||
mac: "Cmd-p",
|
mac: "Cmd-p",
|
||||||
|
@ -371,10 +407,13 @@ class Editor {
|
||||||
dispatch({ type: "hide-palette" });
|
dispatch({ type: "hide-palette" });
|
||||||
editor!.focus();
|
editor!.focus();
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
console.log("Run", cmd);
|
safeRun(async () => {
|
||||||
|
let result = await cmd.run(buildContext(cmd, editor));
|
||||||
|
console.log("Result of command", result);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
commands={[{ name: "My command", run: () => {} }]}
|
commands={viewState.commands}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<NavigationBar
|
<NavigationBar
|
||||||
|
@ -400,7 +439,13 @@ let ed = new Editor(
|
||||||
document.getElementById("root")!
|
document.getElementById("root")!
|
||||||
);
|
);
|
||||||
|
|
||||||
ed.focus();
|
ed.loadPlugins().catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
safeRun(async () => {
|
||||||
|
await ed.init();
|
||||||
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.editor = ed;
|
window.editor = ed;
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Noot</title>
|
<title>Noot</title>
|
||||||
<link rel="stylesheet" href="styles.css" />
|
<link rel="stylesheet" href="styles.css" />
|
||||||
<script type="module" src="app.tsx"></script>
|
<script type="module" src="editor.tsx"></script>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { Manifest } from "./plugins/types";
|
||||||
|
|
||||||
|
import { openDB, wrap, unwrap } from "idb";
|
||||||
|
|
||||||
|
const rootUrl = location.origin + "/plugin";
|
||||||
|
|
||||||
|
// Storing manifests in IndexedDB, y'all
|
||||||
|
let manifestCache = caches.open("manifests");
|
||||||
|
|
||||||
|
const db = openDB("manifests-store", undefined, {
|
||||||
|
upgrade(db) {
|
||||||
|
db.createObjectStore("manifests");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function saveManifest(name: string, manifest: Manifest) {
|
||||||
|
await (await db).put("manifests", manifest, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getManifest(name: string): Promise<Manifest | undefined> {
|
||||||
|
return (await (await db).get("manifests", name)) as Manifest | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener("install", (event) => {
|
||||||
|
console.log("Installing");
|
||||||
|
// @ts-ignore
|
||||||
|
self.skipWaiting();
|
||||||
|
// event.waitUntil(fetchBundle());
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handlePut(req: Request, path: string) {
|
||||||
|
console.log("Got manifest load for", path);
|
||||||
|
let manifest = (await req.json()) as Manifest;
|
||||||
|
await saveManifest(path, manifest);
|
||||||
|
// loadedBundles.set(path, manifest);
|
||||||
|
return new Response("ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener("fetch", (event: any) => {
|
||||||
|
const req = event.request;
|
||||||
|
if (req.url.startsWith(rootUrl)) {
|
||||||
|
let path = req.url.substring(rootUrl.length + 1);
|
||||||
|
event.respondWith(
|
||||||
|
(async () => {
|
||||||
|
// console.log("Service worker is serving", path);
|
||||||
|
if (path === `$ping`) {
|
||||||
|
// console.log("Got ping");
|
||||||
|
return new Response("ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method === "PUT") {
|
||||||
|
return await handlePut(req, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let [cartridgeName, resourceType, functionName] = path.split("/");
|
||||||
|
|
||||||
|
let manifest = await getManifest(cartridgeName);
|
||||||
|
|
||||||
|
if (!manifest) {
|
||||||
|
// console.log("Ain't got", cartridgeName);
|
||||||
|
return new Response(`Cartridge not loaded: ${cartridgeName}`, {
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceType === "$manifest") {
|
||||||
|
return new Response(JSON.stringify(manifest));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceType === "function") {
|
||||||
|
let func = manifest.functions[functionName];
|
||||||
|
// console.log("Serving function", functionName, func);
|
||||||
|
if (!func) {
|
||||||
|
return new Response("Not found", {
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new Response(func.code, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"Content-type": "application/javascript",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("activate", (event) => {
|
||||||
|
// console.log("Now ready to pick up fetches");
|
||||||
|
// @ts-ignore
|
||||||
|
event.waitUntil(self.clients.claim());
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log("I'm a service worker, look at me!", location.href);
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { CartridgeLoader, System } from "./runtime";
|
||||||
|
import { Manifest } from "./types";
|
||||||
|
import { sleep } from "../util";
|
||||||
|
|
||||||
|
export class BrowserLoader implements CartridgeLoader {
|
||||||
|
readonly pathPrefix: string;
|
||||||
|
|
||||||
|
constructor(pathPrefix: string) {
|
||||||
|
this.pathPrefix = pathPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(name: string, manifest: Manifest): Promise<void> {
|
||||||
|
await fetch(`${this.pathPrefix}/${name}`, {
|
||||||
|
method: "PUT",
|
||||||
|
body: JSON.stringify(manifest),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BrowserSystem extends System {
|
||||||
|
constructor(pathPrefix: string) {
|
||||||
|
super(new BrowserLoader(pathPrefix), pathPrefix);
|
||||||
|
}
|
||||||
|
// Service worker stuff
|
||||||
|
async pollServiceWorkerActive() {
|
||||||
|
for (let i = 0; i < 25; i++) {
|
||||||
|
try {
|
||||||
|
console.log("Pinging...", `${this.pathPrefix}/$ping`);
|
||||||
|
let ping = await fetch(`${this.pathPrefix}/$ping`);
|
||||||
|
let text = await ping.text();
|
||||||
|
if (ping.status === 200 && text === "ok") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Not yet");
|
||||||
|
}
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
// Alright, something's messed up
|
||||||
|
throw new Error("Worker not successfully activated");
|
||||||
|
}
|
||||||
|
|
||||||
|
async bootServiceWorker() {
|
||||||
|
// @ts-ignore
|
||||||
|
let reg = navigator.serviceWorker.register(
|
||||||
|
new URL("../plugin_sw.ts", import.meta.url),
|
||||||
|
{
|
||||||
|
type: "module",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Service worker registered successfully");
|
||||||
|
|
||||||
|
await this.pollServiceWorkerActive();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
function safeRun(fn: () => Promise<void>) {
|
||||||
|
fn().catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let func = null;
|
||||||
|
let pendingRequests: {
|
||||||
|
[key: number]: any;
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
self.addEventListener("syscall", (event) => {
|
||||||
|
let customEvent = event as CustomEvent;
|
||||||
|
let detail = customEvent.detail;
|
||||||
|
pendingRequests[detail.id] = detail.callback;
|
||||||
|
self.postMessage({
|
||||||
|
type: "syscall",
|
||||||
|
id: detail.id,
|
||||||
|
name: detail.name,
|
||||||
|
args: detail.args,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("result", (event) => {
|
||||||
|
let customEvent = event as CustomEvent;
|
||||||
|
self.postMessage({
|
||||||
|
type: "result",
|
||||||
|
result: customEvent.detail,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("app-error", (event) => {
|
||||||
|
let customEvent = event as CustomEvent;
|
||||||
|
postMessage({
|
||||||
|
type: "error",
|
||||||
|
reason: customEvent.detail,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("message", (event) => {
|
||||||
|
safeRun(async () => {
|
||||||
|
let messageEvent = event as MessageEvent;
|
||||||
|
let data = messageEvent.data;
|
||||||
|
switch (data.type) {
|
||||||
|
case "boot":
|
||||||
|
console.log("Booting", `./${data.prefix}/function/${data.name}`);
|
||||||
|
importScripts(`./${data.prefix}/function/${data.name}`);
|
||||||
|
// if (data.userAgent && data.userAgent.indexOf("Firefox") !== -1) {
|
||||||
|
// // @ts-ignore
|
||||||
|
// } else {
|
||||||
|
// await import(`./${data.prefix}/function/${data.name}`);
|
||||||
|
// }
|
||||||
|
self.postMessage({
|
||||||
|
type: "inited",
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "invoke":
|
||||||
|
self.dispatchEvent(
|
||||||
|
new CustomEvent("invoke-function", {
|
||||||
|
detail: {
|
||||||
|
args: data.args || [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "syscall-response":
|
||||||
|
let id = data.id;
|
||||||
|
const lookup = pendingRequests[id];
|
||||||
|
if (!lookup) {
|
||||||
|
console.log(
|
||||||
|
"Current outstanding requests",
|
||||||
|
pendingRequests,
|
||||||
|
"looking up",
|
||||||
|
id
|
||||||
|
);
|
||||||
|
throw Error("Invalid request id");
|
||||||
|
}
|
||||||
|
return await lookup(data.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,196 @@
|
||||||
|
import { Manifest } from "./types";
|
||||||
|
|
||||||
|
export class SyscallContext {
|
||||||
|
public cartridge: Cartridge;
|
||||||
|
|
||||||
|
constructor(cartridge: Cartridge) {
|
||||||
|
this.cartridge = cartridge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SysCallMapping {
|
||||||
|
// TODO: Better typing
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FunctionWorker {
|
||||||
|
private worker: Worker;
|
||||||
|
private inited: Promise<any>;
|
||||||
|
private initCallback: any;
|
||||||
|
private invokeResolve?: (result?: any) => void;
|
||||||
|
private invokeReject?: (reason?: any) => void;
|
||||||
|
private cartridge: Cartridge;
|
||||||
|
|
||||||
|
constructor(cartridge: Cartridge, pathPrefix: string, name: string) {
|
||||||
|
this.worker = new Worker(new URL("function_worker.ts", import.meta.url));
|
||||||
|
// console.log("Starting worker", this.worker);
|
||||||
|
this.worker.onmessage = this.onmessage.bind(this);
|
||||||
|
this.worker.postMessage({
|
||||||
|
type: "boot",
|
||||||
|
prefix: pathPrefix,
|
||||||
|
name: name,
|
||||||
|
// @ts-ignore
|
||||||
|
userAgent: navigator.userAgent,
|
||||||
|
});
|
||||||
|
this.inited = new Promise((resolve) => {
|
||||||
|
this.initCallback = resolve;
|
||||||
|
});
|
||||||
|
this.cartridge = cartridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onmessage(evt: MessageEvent) {
|
||||||
|
let data = evt.data;
|
||||||
|
if (!data) return;
|
||||||
|
switch (data.type) {
|
||||||
|
case "inited":
|
||||||
|
this.initCallback();
|
||||||
|
break;
|
||||||
|
case "syscall":
|
||||||
|
const ctx = new SyscallContext(this.cartridge);
|
||||||
|
let result = await this.cartridge.system.syscall(
|
||||||
|
ctx,
|
||||||
|
data.name,
|
||||||
|
data.args
|
||||||
|
);
|
||||||
|
|
||||||
|
this.worker.postMessage({
|
||||||
|
type: "syscall-response",
|
||||||
|
id: data.id,
|
||||||
|
data: result,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "result":
|
||||||
|
this.invokeResolve!(data.result);
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
this.invokeReject!(data.reason);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error("Unknown message type", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async invoke(args: Array<any>): Promise<any> {
|
||||||
|
await this.inited;
|
||||||
|
this.worker.postMessage({
|
||||||
|
type: "invoke",
|
||||||
|
args: args,
|
||||||
|
});
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.invokeResolve = resolve;
|
||||||
|
this.invokeReject = reject;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.worker.terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CartridgeLoader {
|
||||||
|
load(name: string, manifest: Manifest): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Cartridge {
|
||||||
|
pathPrefix: string;
|
||||||
|
system: System;
|
||||||
|
private runningFunctions: Map<string, FunctionWorker>;
|
||||||
|
public manifest?: Manifest;
|
||||||
|
private name: string;
|
||||||
|
|
||||||
|
constructor(system: System, pathPrefix: string, name: string) {
|
||||||
|
this.name = name;
|
||||||
|
this.pathPrefix = `${pathPrefix}/${name}`;
|
||||||
|
this.system = system;
|
||||||
|
this.runningFunctions = new Map<string, FunctionWorker>();
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(manifest: Manifest) {
|
||||||
|
this.manifest = manifest;
|
||||||
|
await this.system.cartridgeLoader.load(this.name, manifest);
|
||||||
|
await this.dispatchEvent("load");
|
||||||
|
}
|
||||||
|
|
||||||
|
async invoke(name: string, args: Array<any>): Promise<any> {
|
||||||
|
if (!this.runningFunctions.has(name)) {
|
||||||
|
this.runningFunctions.set(
|
||||||
|
name,
|
||||||
|
new FunctionWorker(this, this.pathPrefix, name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return await this.runningFunctions.get(name)!.invoke(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
async dispatchEvent(name: string, data?: any) {
|
||||||
|
let functionsToSpawn = this.manifest!.events[name];
|
||||||
|
if (functionsToSpawn) {
|
||||||
|
await Promise.all(
|
||||||
|
functionsToSpawn.map(async (functionToSpawn: string) => {
|
||||||
|
await this.invoke(functionToSpawn, [data]);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop() {
|
||||||
|
for (const [functionname, worker] of Object.entries(
|
||||||
|
this.runningFunctions
|
||||||
|
)) {
|
||||||
|
console.log(`Stopping ${functionname}`);
|
||||||
|
worker.stop();
|
||||||
|
}
|
||||||
|
this.runningFunctions = new Map<string, FunctionWorker>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class System {
|
||||||
|
protected cartridges: Map<string, Cartridge>;
|
||||||
|
protected pathPrefix: string;
|
||||||
|
registeredSyscalls: SysCallMapping;
|
||||||
|
cartridgeLoader: CartridgeLoader;
|
||||||
|
|
||||||
|
constructor(cartridgeLoader: CartridgeLoader, pathPrefix: string) {
|
||||||
|
this.cartridgeLoader = cartridgeLoader;
|
||||||
|
this.pathPrefix = pathPrefix;
|
||||||
|
this.cartridges = new Map<string, Cartridge>();
|
||||||
|
this.registeredSyscalls = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
registerSyscalls(...registrationObjects: Array<SysCallMapping>) {
|
||||||
|
for (const registrationObject of registrationObjects) {
|
||||||
|
for (let p in registrationObject) {
|
||||||
|
this.registeredSyscalls[p] = registrationObject[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async syscall(
|
||||||
|
ctx: SyscallContext,
|
||||||
|
name: string,
|
||||||
|
args: Array<any>
|
||||||
|
): Promise<any> {
|
||||||
|
const callback = this.registeredSyscalls[name];
|
||||||
|
if (!name) {
|
||||||
|
throw Error(`Unregistered syscall ${name}`);
|
||||||
|
}
|
||||||
|
if (!callback) {
|
||||||
|
throw Error(`Registered but not implemented syscall ${name}`);
|
||||||
|
}
|
||||||
|
return Promise.resolve(callback(ctx, ...args));
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(name: string, manifest: Manifest): Promise<Cartridge> {
|
||||||
|
const cartridge = new Cartridge(this, this.pathPrefix, name);
|
||||||
|
await cartridge.load(manifest);
|
||||||
|
this.cartridges.set(name, cartridge);
|
||||||
|
return cartridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop(): Promise<void[]> {
|
||||||
|
return Promise.all(
|
||||||
|
Array.from(this.cartridges.values()).map((cartridge) => cartridge.stop())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Starting");
|
|
@ -0,0 +1,27 @@
|
||||||
|
export interface Manifest {
|
||||||
|
events: { [key: string]: string[] };
|
||||||
|
commands: {
|
||||||
|
[key: string]: CommandDef;
|
||||||
|
};
|
||||||
|
functions: {
|
||||||
|
[key: string]: FunctionDef;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommandDef {
|
||||||
|
// Function name to invoke
|
||||||
|
invoke: string;
|
||||||
|
|
||||||
|
// Bind to keyboard shortcut
|
||||||
|
key?: string;
|
||||||
|
mac?: string;
|
||||||
|
// Required context to be passed in as function arguments
|
||||||
|
requiredContext?: {
|
||||||
|
text?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FunctionDef {
|
||||||
|
path: string;
|
||||||
|
code?: string;
|
||||||
|
}
|
|
@ -51,6 +51,11 @@ export default function reducer(
|
||||||
...state,
|
...state,
|
||||||
showCommandPalette: false,
|
showCommandPalette: false,
|
||||||
};
|
};
|
||||||
|
case "update-commands":
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
commands: action.commands,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { SyscallContext } from "../plugins/runtime";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
"db.put": (ctx: SyscallContext, key: string, value: any) => {
|
||||||
|
localStorage.setItem(key, value);
|
||||||
|
},
|
||||||
|
"db.get": (ctx: SyscallContext, key: string) => {
|
||||||
|
return localStorage.getItem(key);
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { Editor } from "../editor";
|
||||||
|
import { SyscallContext } from "../plugins/runtime";
|
||||||
|
import { syntaxTree } from "@codemirror/language";
|
||||||
|
|
||||||
|
export default (editor: Editor) => ({
|
||||||
|
"editor.getText": (ctx: SyscallContext) => {
|
||||||
|
return editor.editorView?.state.sliceDoc();
|
||||||
|
},
|
||||||
|
"editor.getCursor": (ctx: SyscallContext): number => {
|
||||||
|
return editor.editorView!.state.selection.main.from;
|
||||||
|
},
|
||||||
|
"editor.navigate": async (ctx: SyscallContext, name: string) => {
|
||||||
|
await editor.navigate(name);
|
||||||
|
},
|
||||||
|
"editor.insertAtPos": (ctx: SyscallContext, text: string, pos: number) => {
|
||||||
|
editor.editorView!.dispatch({
|
||||||
|
changes: {
|
||||||
|
insert: text,
|
||||||
|
from: pos,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"editor.replaceRange": (
|
||||||
|
ctx: SyscallContext,
|
||||||
|
from: number,
|
||||||
|
to: number,
|
||||||
|
text: string
|
||||||
|
) => {
|
||||||
|
editor.editorView!.dispatch({
|
||||||
|
changes: {
|
||||||
|
insert: text,
|
||||||
|
from: from,
|
||||||
|
to: to,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"editor.moveCursor": (ctx: SyscallContext, pos: number) => {
|
||||||
|
editor.editorView!.dispatch({
|
||||||
|
selection: {
|
||||||
|
anchor: pos,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"editor.insertAtCursor": (ctx: SyscallContext, text: string) => {
|
||||||
|
let editorView = editor.editorView!;
|
||||||
|
let from = editorView.state.selection.main.from;
|
||||||
|
editorView.dispatch({
|
||||||
|
changes: {
|
||||||
|
insert: text,
|
||||||
|
from: from,
|
||||||
|
},
|
||||||
|
selection: {
|
||||||
|
anchor: from + text.length,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"editor.getSyntaxNodeUnderCursor": (
|
||||||
|
ctx: SyscallContext
|
||||||
|
): { name: string; text: string } | 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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { SyscallContext } from "../plugins/runtime";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
"event.publish": async (ctx: SyscallContext, name: string, data: any) => {
|
||||||
|
await ctx.cartridge.dispatchEvent(name, data);
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { SyscallContext } from "../plugins/runtime";
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
let frameTest = document.getElementById("main-frame");
|
||||||
|
|
||||||
|
window.addEventListener("message", async (event) => {
|
||||||
|
let messageEvent = event as MessageEvent;
|
||||||
|
let data = messageEvent.data;
|
||||||
|
if (data.type === "iframe_event") {
|
||||||
|
// @ts-ignore
|
||||||
|
window.mainCartridge.dispatchEvent(data.data.event, data.data.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
"ui.update": function (ctx: SyscallContext, doc: any) {
|
||||||
|
// frameTest.contentWindow.postMessage({
|
||||||
|
// type: "loadContent",
|
||||||
|
// doc: doc,
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,10 +1,16 @@
|
||||||
|
import { CommandDef } from "./plugins/types";
|
||||||
|
|
||||||
export type NoteMeta = {
|
export type NoteMeta = {
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CommandContext = {
|
||||||
|
text?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type AppCommand = {
|
export type AppCommand = {
|
||||||
name: string;
|
command: CommandDef;
|
||||||
run: () => void;
|
run: (ctx: CommandContext) => Promise<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AppViewState = {
|
export type AppViewState = {
|
||||||
|
@ -13,6 +19,15 @@ export type AppViewState = {
|
||||||
showNoteNavigator: boolean;
|
showNoteNavigator: boolean;
|
||||||
showCommandPalette: boolean;
|
showCommandPalette: boolean;
|
||||||
allNotes: NoteMeta[];
|
allNotes: NoteMeta[];
|
||||||
|
commands: Map<string, AppCommand>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initialViewState: AppViewState = {
|
||||||
|
isSaved: false,
|
||||||
|
showNoteNavigator: false,
|
||||||
|
showCommandPalette: false,
|
||||||
|
allNotes: [],
|
||||||
|
commands: new Map(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
|
@ -22,5 +37,6 @@ export type Action =
|
||||||
| { type: "notes-listed"; notes: NoteMeta[] }
|
| { type: "notes-listed"; notes: NoteMeta[] }
|
||||||
| { type: "start-navigate" }
|
| { type: "start-navigate" }
|
||||||
| { type: "stop-navigate" }
|
| { type: "stop-navigate" }
|
||||||
|
| { type: "update-commands"; commands: Map<string, AppCommand> }
|
||||||
| { type: "show-palette" }
|
| { type: "show-palette" }
|
||||||
| { type: "hide-palette" };
|
| { type: "hide-palette" };
|
||||||
|
|
|
@ -7,3 +7,17 @@ export function readingTime(wordCount: number): number {
|
||||||
// 225 is average word reading speed for adults
|
// 225 is average word reading speed for adults
|
||||||
return Math.ceil(wordCount / 225);
|
return Math.ceil(wordCount / 225);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function safeRun(fn: () => Promise<void>) {
|
||||||
|
fn().catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve();
|
||||||
|
}, ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
{
|
{
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2021",
|
"target": "esnext",
|
||||||
"strict": true ,
|
"strict": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"allowSyntheticDefaultImports": true,
|
"module": "ESNext",
|
||||||
"jsx": "react-jsx"
|
"allowSyntheticDefaultImports": true,
|
||||||
}
|
"resolveJsonModule": true,
|
||||||
}
|
"jsx": "react-jsx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,13 +23,6 @@
|
||||||
chalk "^2.0.0"
|
chalk "^2.0.0"
|
||||||
js-tokens "^4.0.0"
|
js-tokens "^4.0.0"
|
||||||
|
|
||||||
"@babel/runtime@^7.12.5":
|
|
||||||
version "7.17.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
|
|
||||||
integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime "^0.13.4"
|
|
||||||
|
|
||||||
"@codemirror/autocomplete@^0.19.0":
|
"@codemirror/autocomplete@^0.19.0":
|
||||||
version "0.19.12"
|
version "0.19.12"
|
||||||
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-0.19.12.tgz#4c9e4487b45e6877807e4f16c1fffd5e7639ae52"
|
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-0.19.12.tgz#4c9e4487b45e6877807e4f16c1fffd5e7639ae52"
|
||||||
|
@ -694,6 +687,11 @@
|
||||||
"@parcel/utils" "2.3.2"
|
"@parcel/utils" "2.3.2"
|
||||||
nullthrows "^1.1.1"
|
nullthrows "^1.1.1"
|
||||||
|
|
||||||
|
"@parcel/service-worker@^2.3.2":
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/service-worker/-/service-worker-2.3.2.tgz#c5d5ca876249fc39dbfd55e7f6be94645244cf5c"
|
||||||
|
integrity sha512-snBZYe8MV4suTtbQAABQ8OBWdccO07onxayReiDLUzTRffNB2V1ikLDYkngLMmpRAa1lp0bnB0KfvVX8jeLLOg==
|
||||||
|
|
||||||
"@parcel/source-map@^2.0.0":
|
"@parcel/source-map@^2.0.0":
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@parcel/source-map/-/source-map-2.0.2.tgz#9aa0b00518cee31d5634de6e9c924a5539b142c1"
|
resolved "https://registry.yarnpkg.com/@parcel/source-map/-/source-map-2.0.2.tgz#9aa0b00518cee31d5634de6e9c924a5539b142c1"
|
||||||
|
@ -897,28 +895,6 @@
|
||||||
chrome-trace-event "^1.0.2"
|
chrome-trace-event "^1.0.2"
|
||||||
nullthrows "^1.1.1"
|
nullthrows "^1.1.1"
|
||||||
|
|
||||||
"@reach/observe-rect@^1.1.0":
|
|
||||||
version "1.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@reach/observe-rect/-/observe-rect-1.2.0.tgz#d7a6013b8aafcc64c778a0ccb83355a11204d3b2"
|
|
||||||
integrity sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==
|
|
||||||
|
|
||||||
"@reach/portal@^0.16.0":
|
|
||||||
version "0.16.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@reach/portal/-/portal-0.16.2.tgz#ca83696215ee03acc2bb25a5ae5d8793eaaf2f64"
|
|
||||||
integrity sha512-9ur/yxNkuVYTIjAcfi46LdKUvH0uYZPfEp4usWcpt6PIp+WDF57F/5deMe/uGi/B/nfDweQu8VVwuMVrCb97JQ==
|
|
||||||
dependencies:
|
|
||||||
"@reach/utils" "0.16.0"
|
|
||||||
tiny-warning "^1.0.3"
|
|
||||||
tslib "^2.3.0"
|
|
||||||
|
|
||||||
"@reach/utils@0.16.0":
|
|
||||||
version "0.16.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.16.0.tgz#5b0777cf16a7cab1ddd4728d5d02762df0ba84ce"
|
|
||||||
integrity sha512-PCggBet3qaQmwFNcmQ/GqHSefadAFyNCUekq9RrWoaU9hh/S4iaFgf2MBMdM47eQj5i/Bk0Mm07cP/XPFlkN+Q==
|
|
||||||
dependencies:
|
|
||||||
tiny-warning "^1.0.3"
|
|
||||||
tslib "^2.3.0"
|
|
||||||
|
|
||||||
"@swc/helpers@^0.2.11":
|
"@swc/helpers@^0.2.11":
|
||||||
version "0.2.14"
|
version "0.2.14"
|
||||||
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.2.14.tgz#20288c3627442339dd3d743c944f7043ee3590f0"
|
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.2.14.tgz#20288c3627442339dd3d743c944f7043ee3590f0"
|
||||||
|
@ -1283,11 +1259,6 @@ escape-string-regexp@^1.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||||
|
|
||||||
fast-equals@^2.0.3:
|
|
||||||
version "2.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-2.0.4.tgz#3add9410585e2d7364c2deeb6a707beadb24b927"
|
|
||||||
integrity sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==
|
|
||||||
|
|
||||||
get-port@^4.2.0:
|
get-port@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119"
|
resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119"
|
||||||
|
@ -1329,6 +1300,11 @@ htmlparser2@^7.1.1:
|
||||||
domutils "^2.8.0"
|
domutils "^2.8.0"
|
||||||
entities "^3.0.1"
|
entities "^3.0.1"
|
||||||
|
|
||||||
|
idb@^7.0.0:
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/idb/-/idb-7.0.0.tgz#f349b418c128f625961147a7d6b0e4b526fd34ed"
|
||||||
|
integrity sha512-jSx0WOY9Nj+QzP6wX5e7g64jqh8ExtDs/IAuOrOEZCD/h6+0HqyrKsDMfdJc0hqhSvh0LsrwqrkDn+EtjjzSRA==
|
||||||
|
|
||||||
import-fresh@^3.2.1:
|
import-fresh@^3.2.1:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
||||||
|
@ -1369,17 +1345,6 @@ json5@^2.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
kbar@^0.1.0-beta.27:
|
|
||||||
version "0.1.0-beta.27"
|
|
||||||
resolved "https://registry.yarnpkg.com/kbar/-/kbar-0.1.0-beta.27.tgz#6fec637054599dc4c6aa5a0cfc4042a50b3e32d1"
|
|
||||||
integrity sha512-4knRJxDQqx3LUduhjuJh9EDGxnFpaQKjXt11UOsjKQ4ByXTTQpPjfAaKagVcTp9uVwEXGDhvGrsGbMfrI+6/Kg==
|
|
||||||
dependencies:
|
|
||||||
"@reach/portal" "^0.16.0"
|
|
||||||
fast-equals "^2.0.3"
|
|
||||||
match-sorter "^6.3.0"
|
|
||||||
react-virtual "^2.8.2"
|
|
||||||
tiny-invariant "^1.2.0"
|
|
||||||
|
|
||||||
lilconfig@^2.0.3:
|
lilconfig@^2.0.3:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082"
|
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082"
|
||||||
|
@ -1418,14 +1383,6 @@ loose-envify@^1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens "^3.0.0 || ^4.0.0"
|
js-tokens "^3.0.0 || ^4.0.0"
|
||||||
|
|
||||||
match-sorter@^6.3.0:
|
|
||||||
version "6.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda"
|
|
||||||
integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.12.5"
|
|
||||||
remove-accents "0.4.2"
|
|
||||||
|
|
||||||
mdn-data@2.0.14:
|
mdn-data@2.0.14:
|
||||||
version "2.0.14"
|
version "2.0.14"
|
||||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||||
|
@ -1804,13 +1761,6 @@ react-refresh@^0.9.0:
|
||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf"
|
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf"
|
||||||
integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==
|
integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==
|
||||||
|
|
||||||
react-virtual@^2.8.2:
|
|
||||||
version "2.10.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-virtual/-/react-virtual-2.10.4.tgz#08712f0acd79d7d6f7c4726f05651a13b24d8704"
|
|
||||||
integrity sha512-Ir6+oPQZTVHfa6+JL9M7cvMILstFZH/H3jqeYeKI4MSUX+rIruVwFC6nGVXw9wqAw8L0Kg2KvfXxI85OvYQdpQ==
|
|
||||||
dependencies:
|
|
||||||
"@reach/observe-rect" "^1.1.0"
|
|
||||||
|
|
||||||
react@^17.0.2:
|
react@^17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
|
@ -1819,16 +1769,11 @@ react@^17.0.2:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
|
regenerator-runtime@^0.13.7:
|
||||||
version "0.13.9"
|
version "0.13.9"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
|
||||||
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
||||||
|
|
||||||
remove-accents@0.4.2:
|
|
||||||
version "0.4.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
|
|
||||||
integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=
|
|
||||||
|
|
||||||
resolve-from@^4.0.0:
|
resolve-from@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||||
|
@ -1934,21 +1879,6 @@ timsort@^0.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
||||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||||
|
|
||||||
tiny-invariant@^1.2.0:
|
|
||||||
version "1.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9"
|
|
||||||
integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==
|
|
||||||
|
|
||||||
tiny-warning@^1.0.3:
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
|
||||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
|
||||||
|
|
||||||
tslib@^2.3.0:
|
|
||||||
version "2.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
|
||||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
|
||||||
|
|
||||||
type-fest@^0.20.2:
|
type-fest@^0.20.2:
|
||||||
version "0.20.2"
|
version "0.20.2"
|
||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||||
|
|
Loading…
Reference in New Issue