Notes -> Nuggets
This commit is contained in:
parent
29cbb74508
commit
0527430626
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,5 +1 @@
|
|||||||
/webapp/node_modules/
|
nuggets
|
||||||
/webapp/dist/
|
|
||||||
/.parcel-cache/
|
|
||||||
/.idea
|
|
||||||
/notes
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
## Lots of
|
|
||||||
|
|
||||||
Content #here
|
|
||||||
|
|
||||||
@zef.hemel
|
|
@ -1 +0,0 @@
|
|||||||
Sup yo
|
|
@ -1,2 +0,0 @@
|
|||||||
Yo!!
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
I know he is! **Bold**
|
|
@ -1,6 +0,0 @@
|
|||||||
Home page
|
|
||||||
|
|
||||||
[[Great Parenting]]
|
|
||||||
[[1:1s]]
|
|
||||||
|
|
||||||
Sup
|
|
@ -1,3 +0,0 @@
|
|||||||
# Sappie
|
|
||||||
|
|
||||||
Sup
|
|
@ -1,5 +0,0 @@
|
|||||||
# Hello
|
|
||||||
|
|
||||||
## Second level header
|
|
||||||
|
|
||||||
bla
|
|
@ -3,3 +3,5 @@ build: *
|
|||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
$(DENO_BUNDLE) core/core.plugin.json dist/core.plugin.json
|
$(DENO_BUNDLE) core/core.plugin.json dist/core.plugin.json
|
||||||
|
|
||||||
|
entr:
|
||||||
|
ls core/* | entr make
|
@ -1,26 +1,9 @@
|
|||||||
import { parse } from "https://deno.land/std@0.121.0/flags/mod.ts";
|
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 * as path from "https://deno.land/std@0.121.0/path/mod.ts";
|
||||||
import { Manifest, FunctionDef } from "../webapp/src/plugins/types.ts";
|
import { Manifest, FunctionDef } from "../webapp/src/plugins/types.ts";
|
||||||
|
|
||||||
async function compile(
|
async function compile(filePath: string, sourceMaps: boolean): Promise<string> {
|
||||||
filePath: string,
|
|
||||||
prettyFunctionName: string,
|
|
||||||
jsFunctionName: string,
|
|
||||||
sourceMaps: boolean
|
|
||||||
): Promise<string> {
|
|
||||||
// @ts-ignore for Deno.emit (unstable API)
|
// @ts-ignore for Deno.emit (unstable API)
|
||||||
let { files, diagnostics } = await Deno.emit(filePath, {
|
let { files, diagnostics } = await Deno.emit(filePath, {
|
||||||
bundle: "classic",
|
bundle: "classic",
|
||||||
@ -47,18 +30,7 @@ async function compile(
|
|||||||
}
|
}
|
||||||
throw new Error("Diagnostics");
|
throw new Error("Diagnostics");
|
||||||
}
|
}
|
||||||
return `const mod = ${bundleSource}
|
return 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(
|
async function bundle(
|
||||||
@ -73,23 +45,19 @@ async function bundle(
|
|||||||
for (let [name, def] of Object.entries(manifest.functions) as Array<
|
for (let [name, def] of Object.entries(manifest.functions) as Array<
|
||||||
[string, FunctionDef]
|
[string, FunctionDef]
|
||||||
>) {
|
>) {
|
||||||
let jsFunctionName,
|
let jsFunctionName = def.functionName,
|
||||||
filePath = path.join(rootPath, def.path);
|
filePath = path.join(rootPath, def.path);
|
||||||
if (filePath.indexOf(":") !== 0) {
|
if (filePath.indexOf(":") !== -1) {
|
||||||
[filePath, jsFunctionName] = filePath.split(":");
|
[filePath, jsFunctionName] = filePath.split(":");
|
||||||
} else {
|
} else if (!jsFunctionName) {
|
||||||
jsFunctionName = "default";
|
jsFunctionName = "default";
|
||||||
}
|
}
|
||||||
|
|
||||||
def.code = await compile(filePath, name, jsFunctionName, sourceMaps);
|
def.code = await compile(filePath, sourceMaps);
|
||||||
|
def.path = filePath;
|
||||||
|
def.functionName = jsFunctionName;
|
||||||
}
|
}
|
||||||
return manifest;
|
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, {
|
let commandLineArguments = parse(Deno.args, {
|
||||||
@ -103,13 +71,3 @@ await Deno.writeFile(
|
|||||||
outputPath,
|
outputPath,
|
||||||
new TextEncoder().encode(JSON.stringify(b, null, 2))
|
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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
@ -10,8 +10,7 @@
|
|||||||
"invoke": "link_navigate",
|
"invoke": "link_navigate",
|
||||||
"key": "Ctrl-Enter",
|
"key": "Ctrl-Enter",
|
||||||
"mac": "Cmd-Enter",
|
"mac": "Cmd-Enter",
|
||||||
"requiredContext": {
|
"requiredContext": {}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Insert Current Date": {
|
"Insert Current Date": {
|
||||||
"invoke": "insert_nice_date"
|
"invoke": "insert_nice_date"
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
deno run --allow-net --allow-read --allow-write server.ts
|
ls | entr -s 'deno run --allow-net --allow-read --allow-write server.ts'
|
@ -7,14 +7,14 @@ import { readAll } from "https://deno.land/std@0.126.0/streams/mod.ts";
|
|||||||
import { exists } from "https://deno.land/std@0.126.0/fs/mod.ts";
|
import { exists } from "https://deno.land/std@0.126.0/fs/mod.ts";
|
||||||
|
|
||||||
const fsPrefix = "/fs";
|
const fsPrefix = "/fs";
|
||||||
const notesPath = "../notes";
|
const nuggetsPath = "../nuggets";
|
||||||
|
|
||||||
const fsRouter = new Router();
|
const fsRouter = new Router();
|
||||||
|
|
||||||
fsRouter.use(oakCors());
|
fsRouter.use(oakCors());
|
||||||
|
|
||||||
fsRouter.get("/", async (context) => {
|
fsRouter.get("/", async (context) => {
|
||||||
const localPath = notesPath;
|
const localPath = nuggetsPath;
|
||||||
let fileNames: string[] = [];
|
let fileNames: string[] = [];
|
||||||
for await (const dirEntry of Deno.readDir(localPath)) {
|
for await (const dirEntry of Deno.readDir(localPath)) {
|
||||||
if (dirEntry.isFile) {
|
if (dirEntry.isFile) {
|
||||||
@ -29,9 +29,9 @@ fsRouter.get("/", async (context) => {
|
|||||||
context.response.body = JSON.stringify(fileNames);
|
context.response.body = JSON.stringify(fileNames);
|
||||||
});
|
});
|
||||||
|
|
||||||
fsRouter.get("/:note", async (context) => {
|
fsRouter.get("/:nugget", async (context) => {
|
||||||
const noteName = context.params.note;
|
const nuggetName = context.params.nugget;
|
||||||
const localPath = `${notesPath}/${noteName}.md`;
|
const localPath = `${nuggetsPath}/${nuggetName}.md`;
|
||||||
try {
|
try {
|
||||||
const text = await Deno.readTextFile(localPath);
|
const text = await Deno.readTextFile(localPath);
|
||||||
context.response.body = text;
|
context.response.body = text;
|
||||||
@ -41,8 +41,8 @@ fsRouter.get("/:note", async (context) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fsRouter.options("/:note", async (context) => {
|
fsRouter.options("/:nugget", async (context) => {
|
||||||
const localPath = `${notesPath}/${context.params.note}.md`;
|
const localPath = `${nuggetsPath}/${context.params.nugget}.md`;
|
||||||
try {
|
try {
|
||||||
const stat = await Deno.stat(localPath);
|
const stat = await Deno.stat(localPath);
|
||||||
context.response.headers.set("Content-length", `${stat.size}`);
|
context.response.headers.set("Content-length", `${stat.size}`);
|
||||||
@ -52,10 +52,10 @@ fsRouter.options("/:note", async (context) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fsRouter.put("/:note", async (context) => {
|
fsRouter.put("/:nugget", async (context) => {
|
||||||
const noteName = context.params.note;
|
const nuggetName = context.params.nugget;
|
||||||
const localPath = `${notesPath}/${noteName}.md`;
|
const localPath = `${nuggetsPath}/${nuggetName}.md`;
|
||||||
const existingNote = await exists(localPath);
|
const existingNugget = await exists(localPath);
|
||||||
let file;
|
let file;
|
||||||
try {
|
try {
|
||||||
file = await Deno.create(localPath);
|
file = await Deno.create(localPath);
|
||||||
@ -70,7 +70,7 @@ fsRouter.put("/:note", async (context) => {
|
|||||||
file.write(text);
|
file.write(text);
|
||||||
file.close();
|
file.close();
|
||||||
console.log("Wrote to", localPath);
|
console.log("Wrote to", localPath);
|
||||||
context.response.status = existingNote ? 200 : 201;
|
context.response.status = existingNugget ? 200 : 201;
|
||||||
context.response.body = "OK";
|
context.response.body = "OK";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
3
webapp/.gitignore
vendored
3
webapp/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
|
.parcel-cache
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "cp src/function_worker.js dist/ && parcel",
|
"start": "cp src/function_worker.js dist/ && parcel",
|
||||||
"build": "parcel build && cp src/function_worker.js dist/",
|
"build": "parcel build && cp src/function_worker.js dist/",
|
||||||
|
"clean": "rm -rf dist",
|
||||||
"check-watch": "tsc --noEmit --watch"
|
"check-watch": "tsc --noEmit --watch"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
export function NavigationBar({
|
export function NavigationBar({
|
||||||
currentNote,
|
currentNugget,
|
||||||
onClick,
|
onClick,
|
||||||
}: {
|
}: {
|
||||||
currentNote?: string;
|
currentNugget?: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div id="top">
|
<div id="top">
|
||||||
<div className="current-note" onClick={onClick}>
|
<div className="current-nugget" onClick={onClick}>
|
||||||
» {currentNote}
|
» {currentNugget}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
import { NoteMeta } from "../types";
|
|
||||||
import { FilterList } from "./filter";
|
|
||||||
|
|
||||||
export function NoteNavigator({
|
|
||||||
allNotes,
|
|
||||||
onNavigate,
|
|
||||||
}: {
|
|
||||||
allNotes: NoteMeta[];
|
|
||||||
onNavigate: (note: string | undefined) => void;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<FilterList
|
|
||||||
placeholder=""
|
|
||||||
options={allNotes}
|
|
||||||
allowNew={true}
|
|
||||||
newHint="Create note"
|
|
||||||
onSelect={(opt) => {
|
|
||||||
onNavigate(opt?.name);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
22
webapp/src/components/nugget_navigator.tsx
Normal file
22
webapp/src/components/nugget_navigator.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { NuggetMeta } from "../types";
|
||||||
|
import { FilterList } from "./filter";
|
||||||
|
|
||||||
|
export function NuggetNavigator({
|
||||||
|
allNuggets: allNuggets,
|
||||||
|
onNavigate,
|
||||||
|
}: {
|
||||||
|
allNuggets: NuggetMeta[];
|
||||||
|
onNavigate: (nugget: string | undefined) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<FilterList
|
||||||
|
placeholder=""
|
||||||
|
options={allNuggets}
|
||||||
|
allowNew={true}
|
||||||
|
newHint="Create nugget"
|
||||||
|
onSelect={(opt) => {
|
||||||
|
onNavigate(opt?.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -24,9 +24,9 @@ import ReactDOM from "react-dom";
|
|||||||
import coreManifest from "../../plugins/dist/core.plugin.json";
|
import coreManifest from "../../plugins/dist/core.plugin.json";
|
||||||
import { buildContext } from "./buildContext";
|
import { buildContext } from "./buildContext";
|
||||||
import * as commands from "./commands";
|
import * as commands from "./commands";
|
||||||
import { CommandPalette } from "./components/commandpalette";
|
import { CommandPalette } from "./components/command_palette";
|
||||||
import { NavigationBar } from "./components/navigation_bar";
|
import { NavigationBar } from "./components/navigation_bar";
|
||||||
import { NoteNavigator } from "./components/notenavigator";
|
import { NuggetNavigator } from "./components/nugget_navigator";
|
||||||
import { StatusBar } from "./components/status_bar";
|
import { StatusBar } from "./components/status_bar";
|
||||||
import { FileSystem, HttpFileSystem } from "./fs";
|
import { FileSystem, HttpFileSystem } from "./fs";
|
||||||
import { lineWrapper } from "./lineWrapper";
|
import { lineWrapper } from "./lineWrapper";
|
||||||
@ -47,7 +47,7 @@ import {
|
|||||||
} from "./types";
|
} from "./types";
|
||||||
import { safeRun } from "./util";
|
import { safeRun } from "./util";
|
||||||
|
|
||||||
class NoteState {
|
class NuggetState {
|
||||||
editorState: EditorState;
|
editorState: EditorState;
|
||||||
scrollTop: number;
|
scrollTop: number;
|
||||||
|
|
||||||
@ -62,13 +62,13 @@ export class Editor {
|
|||||||
viewState: AppViewState;
|
viewState: AppViewState;
|
||||||
viewDispatch: React.Dispatch<Action>;
|
viewDispatch: React.Dispatch<Action>;
|
||||||
$hashChange?: () => void;
|
$hashChange?: () => void;
|
||||||
openNotes: Map<string, NoteState>;
|
openNuggets: Map<string, NuggetState>;
|
||||||
fs: FileSystem;
|
fs: FileSystem;
|
||||||
editorCommands: Map<string, AppCommand>;
|
editorCommands: Map<string, AppCommand>;
|
||||||
|
|
||||||
constructor(fs: FileSystem, parent: Element) {
|
constructor(fs: FileSystem, parent: Element) {
|
||||||
this.editorCommands = new Map();
|
this.editorCommands = new Map();
|
||||||
this.openNotes = new Map();
|
this.openNuggets = new Map();
|
||||||
this.fs = fs;
|
this.fs = fs;
|
||||||
this.viewState = initialViewState;
|
this.viewState = initialViewState;
|
||||||
this.viewDispatch = () => {};
|
this.viewDispatch = () => {};
|
||||||
@ -81,7 +81,7 @@ export class Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
await this.loadNoteList();
|
await this.loadNuggetList();
|
||||||
await this.loadPlugins();
|
await this.loadPlugins();
|
||||||
this.$hashChange!();
|
this.$hashChange!();
|
||||||
this.focus();
|
this.focus();
|
||||||
@ -111,8 +111,8 @@ export class Editor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentNote(): string | undefined {
|
get currentNugget(): string | undefined {
|
||||||
return this.viewState.currentNote;
|
return this.viewState.currentNugget;
|
||||||
}
|
}
|
||||||
|
|
||||||
createEditorState(text: string): EditorState {
|
createEditorState(text: string): EditorState {
|
||||||
@ -146,7 +146,7 @@ export class Editor {
|
|||||||
bracketMatching(),
|
bracketMatching(),
|
||||||
closeBrackets(),
|
closeBrackets(),
|
||||||
autocompletion({
|
autocompletion({
|
||||||
override: [this.noteCompleter.bind(this)],
|
override: [this.nuggetCompleter.bind(this)],
|
||||||
}),
|
}),
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
lineWrapper([
|
lineWrapper([
|
||||||
@ -219,18 +219,18 @@ export class Editor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
noteCompleter(ctx: CompletionContext): CompletionResult | null {
|
nuggetCompleter(ctx: CompletionContext): CompletionResult | null {
|
||||||
let prefix = ctx.matchBefore(/\[\[\w*/);
|
let prefix = ctx.matchBefore(/\[\[\w*/);
|
||||||
if (!prefix) {
|
if (!prefix) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// TODO: Lots of optimization potential here
|
// TODO: Lots of optimization potential here
|
||||||
// TODO: put something in the cm-completionIcon-note style
|
// TODO: put something in the cm-completionIcon-nugget style
|
||||||
return {
|
return {
|
||||||
from: prefix.from + 2,
|
from: prefix.from + 2,
|
||||||
options: this.viewState.allNotes.map((noteMeta) => ({
|
options: this.viewState.allNuggets.map((nuggetMeta) => ({
|
||||||
label: noteMeta.name,
|
label: nuggetMeta.name,
|
||||||
type: "note",
|
type: "nugget",
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -238,7 +238,7 @@ export class Editor {
|
|||||||
update(value: null, transaction: Transaction): null {
|
update(value: null, transaction: Transaction): null {
|
||||||
if (transaction.docChanged) {
|
if (transaction.docChanged) {
|
||||||
this.viewDispatch({
|
this.viewDispatch({
|
||||||
type: "note-updated",
|
type: "nugget-updated",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,8 +250,8 @@ export class Editor {
|
|||||||
let coords = view.posAtCoords(event)!;
|
let coords = view.posAtCoords(event)!;
|
||||||
let node = syntaxTree(view.state).resolveInner(coords);
|
let node = syntaxTree(view.state).resolveInner(coords);
|
||||||
if (node && node.name === "WikiLinkPage") {
|
if (node && node.name === "WikiLinkPage") {
|
||||||
let noteName = view.state.sliceDoc(node.from, node.to);
|
let nuggetName = view.state.sliceDoc(node.from, node.to);
|
||||||
this.navigate(noteName);
|
this.navigate(nuggetName);
|
||||||
}
|
}
|
||||||
if (node && node.name === "TaskMarker") {
|
if (node && node.name === "TaskMarker") {
|
||||||
let checkBoxText = view.state.sliceDoc(node.from, node.to);
|
let checkBoxText = view.state.sliceDoc(node.from, node.to);
|
||||||
@ -272,35 +272,35 @@ export class Editor {
|
|||||||
async save() {
|
async save() {
|
||||||
const editorState = this.editorView!.state;
|
const editorState = this.editorView!.state;
|
||||||
|
|
||||||
if (!this.currentNote) {
|
if (!this.currentNugget) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Write to file system
|
// Write to file system
|
||||||
const created = await this.fs.writeNote(
|
const created = await this.fs.writeNugget(
|
||||||
this.currentNote,
|
this.currentNugget,
|
||||||
editorState.sliceDoc()
|
editorState.sliceDoc()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update in open note cache
|
// Update in open nugget cache
|
||||||
this.openNotes.set(
|
this.openNuggets.set(
|
||||||
this.currentNote,
|
this.currentNugget,
|
||||||
new NoteState(editorState, this.editorView!.scrollDOM.scrollTop)
|
new NuggetState(editorState, this.editorView!.scrollDOM.scrollTop)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Dispatch update to view
|
// Dispatch update to view
|
||||||
this.viewDispatch({ type: "note-saved" });
|
this.viewDispatch({ type: "nugget-saved" });
|
||||||
|
|
||||||
// If a new note was created, let's refresh the note list
|
// If a new nugget was created, let's refresh the nugget list
|
||||||
if (created) {
|
if (created) {
|
||||||
await this.loadNoteList();
|
await this.loadNuggetList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadNoteList() {
|
async loadNuggetList() {
|
||||||
let notesMeta = await this.fs.listNotes();
|
let nuggetsMeta = await this.fs.listNuggets();
|
||||||
this.viewDispatch({
|
this.viewDispatch({
|
||||||
type: "notes-listed",
|
type: "nuggets-listed",
|
||||||
notes: notesMeta,
|
nuggets: nuggetsMeta,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,25 +316,25 @@ export class Editor {
|
|||||||
Promise.resolve()
|
Promise.resolve()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await this.save();
|
await this.save();
|
||||||
const noteName = decodeURIComponent(location.hash.substring(1));
|
const nuggetName = decodeURIComponent(location.hash.substring(1));
|
||||||
console.log("Now navigating to", noteName);
|
console.log("Now navigating to", nuggetName);
|
||||||
|
|
||||||
if (!this.editorView) {
|
if (!this.editorView) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let noteState = this.openNotes.get(noteName);
|
let nuggetState = this.openNuggets.get(nuggetName);
|
||||||
if (!noteState) {
|
if (!nuggetState) {
|
||||||
let text = await this.fs.readNote(noteName);
|
let text = await this.fs.readNugget(nuggetName);
|
||||||
noteState = new NoteState(this.createEditorState(text), 0);
|
nuggetState = new NuggetState(this.createEditorState(text), 0);
|
||||||
}
|
}
|
||||||
this.openNotes.set(noteName, noteState!);
|
this.openNuggets.set(nuggetName, nuggetState!);
|
||||||
this.editorView!.setState(noteState!.editorState);
|
this.editorView!.setState(nuggetState!.editorState);
|
||||||
this.editorView.scrollDOM.scrollTop = noteState!.scrollTop;
|
this.editorView.scrollDOM.scrollTop = nuggetState!.scrollTop;
|
||||||
|
|
||||||
this.viewDispatch({
|
this.viewDispatch({
|
||||||
type: "note-loaded",
|
type: "nugget-loaded",
|
||||||
name: noteName,
|
name: nuggetName,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
@ -378,24 +378,28 @@ export class Editor {
|
|||||||
|
|
||||||
let editor = this;
|
let editor = this;
|
||||||
|
|
||||||
useEffect(() => {}, []);
|
useEffect(() => {
|
||||||
|
if (viewState.currentNugget) {
|
||||||
|
document.title = viewState.currentNugget;
|
||||||
|
}
|
||||||
|
}, [viewState.currentNugget]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{viewState.showNoteNavigator && (
|
{viewState.showNuggetNavigator && (
|
||||||
<NoteNavigator
|
<NuggetNavigator
|
||||||
allNotes={viewState.allNotes}
|
allNuggets={viewState.allNuggets}
|
||||||
onNavigate={(note) => {
|
onNavigate={(nugget) => {
|
||||||
dispatch({ type: "stop-navigate" });
|
dispatch({ type: "stop-navigate" });
|
||||||
editor!.focus();
|
editor!.focus();
|
||||||
if (note) {
|
if (nugget) {
|
||||||
editor
|
editor
|
||||||
?.save()
|
?.save()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
editor!.navigate(note);
|
editor!.navigate(nugget);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
alert("Could not save note, not switching");
|
alert("Could not save nugget, not switching");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -417,7 +421,7 @@ export class Editor {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<NavigationBar
|
<NavigationBar
|
||||||
currentNote={viewState.currentNote}
|
currentNugget={viewState.currentNugget}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch({ type: "start-navigate" });
|
dispatch({ type: "start-navigate" });
|
||||||
}}
|
}}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { NoteMeta } from "./types";
|
import { NuggetMeta } from "./types";
|
||||||
|
|
||||||
export interface FileSystem {
|
export interface FileSystem {
|
||||||
listNotes(): Promise<NoteMeta[]>;
|
listNuggets(): Promise<NuggetMeta[]>;
|
||||||
readNote(name: string): Promise<string>;
|
readNugget(name: string): Promise<string>;
|
||||||
// @return whether a new note was created for this
|
// @return whether a new nugget was created for this
|
||||||
writeNote(name: string, text: string): Promise<boolean>;
|
writeNugget(name: string, text: string): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HttpFileSystem implements FileSystem {
|
export class HttpFileSystem implements FileSystem {
|
||||||
@ -12,25 +12,25 @@ export class HttpFileSystem implements FileSystem {
|
|||||||
constructor(url: string) {
|
constructor(url: string) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
}
|
}
|
||||||
async listNotes(): Promise<NoteMeta[]> {
|
async listNuggets(): Promise<NuggetMeta[]> {
|
||||||
let req = await fetch(this.url, {
|
let req = await fetch(this.url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
});
|
});
|
||||||
|
|
||||||
return (await req.json()).map((name: string) => ({ name }));
|
return (await req.json()).map((name: string) => ({ name }));
|
||||||
}
|
}
|
||||||
async readNote(name: string): Promise<string> {
|
async readNugget(name: string): Promise<string> {
|
||||||
let req = await fetch(`${this.url}/${name}`, {
|
let req = await fetch(`${this.url}/${name}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
});
|
});
|
||||||
return await req.text();
|
return await req.text();
|
||||||
}
|
}
|
||||||
async writeNote(name: string, text: string): Promise<boolean> {
|
async writeNugget(name: string, text: string): Promise<boolean> {
|
||||||
let req = await fetch(`${this.url}/${name}`, {
|
let req = await fetch(`${this.url}/${name}`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: text,
|
body: text,
|
||||||
});
|
});
|
||||||
// 201 (Created) means a new note was created
|
// 201 (Created) means a new nugget was created
|
||||||
return req.status === 201;
|
return req.status === 201;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// Nugget: this file is not built by Parcel, it's simply copied to the distribution
|
||||||
|
// The reason is that somehow Parcel cannot accept using importScripts otherwise
|
||||||
function safeRun(fn) {
|
function safeRun(fn) {
|
||||||
fn().catch((e) => {
|
fn().catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Noot</title>
|
<title>Nugget</title>
|
||||||
<link rel="stylesheet" href="styles.css" />
|
<link rel="stylesheet" href="styles.css" />
|
||||||
<script type="module" src="editor.tsx"></script>
|
<script type="module" src="editor.tsx"></script>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
@ -25,7 +25,6 @@ self.addEventListener("install", (event) => {
|
|||||||
console.log("Installing");
|
console.log("Installing");
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
self.skipWaiting();
|
self.skipWaiting();
|
||||||
// event.waitUntil(fetchBundle());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handlePut(req: Request, path: string) {
|
async function handlePut(req: Request, path: string) {
|
||||||
@ -36,6 +35,21 @@ async function handlePut(req: Request, path: string) {
|
|||||||
return new Response("ok");
|
return new Response("ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapScript(functionName: string, code: string): string {
|
||||||
|
return `const mod = ${code}
|
||||||
|
|
||||||
|
self.addEventListener('invoke-function', async e => {
|
||||||
|
try {
|
||||||
|
let result = await mod['${functionName}'](...e.detail.args);
|
||||||
|
self.dispatchEvent(new CustomEvent('result', {detail: result}));
|
||||||
|
} catch(e) {
|
||||||
|
console.error(\`Error while running ${functionName}\`, e);
|
||||||
|
self.dispatchEvent(new CustomEvent('app-error', {detail: e.message}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
self.addEventListener("fetch", (event: any) => {
|
self.addEventListener("fetch", (event: any) => {
|
||||||
const req = event.request;
|
const req = event.request;
|
||||||
if (req.url.startsWith(rootUrl)) {
|
if (req.url.startsWith(rootUrl)) {
|
||||||
@ -75,7 +89,7 @@ self.addEventListener("fetch", (event: any) => {
|
|||||||
status: 404,
|
status: 404,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return new Response(func.code, {
|
return new Response(wrapScript(func.functionName!, func.code!), {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-type": "application/javascript",
|
"Content-type": "application/javascript",
|
||||||
|
@ -23,5 +23,6 @@ export interface CommandDef {
|
|||||||
|
|
||||||
export interface FunctionDef {
|
export interface FunctionDef {
|
||||||
path: string;
|
path: string;
|
||||||
|
functionName?: string;
|
||||||
code?: string;
|
code?: string;
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,18 @@ export default function reducer(
|
|||||||
): AppViewState {
|
): AppViewState {
|
||||||
console.log("Got action", action);
|
console.log("Got action", action);
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "note-loaded":
|
case "nugget-loaded":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentNote: action.name,
|
currentNugget: action.name,
|
||||||
isSaved: true,
|
isSaved: true,
|
||||||
};
|
};
|
||||||
case "note-saved":
|
case "nugget-saved":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isSaved: true,
|
isSaved: true,
|
||||||
};
|
};
|
||||||
case "note-updated":
|
case "nugget-updated":
|
||||||
// Minor rerender optimization, this is triggered a lot
|
// Minor rerender optimization, this is triggered a lot
|
||||||
if (!state.isSaved) {
|
if (!state.isSaved) {
|
||||||
return state;
|
return state;
|
||||||
@ -29,17 +29,17 @@ export default function reducer(
|
|||||||
case "start-navigate":
|
case "start-navigate":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
showNoteNavigator: true,
|
showNuggetNavigator: true,
|
||||||
};
|
};
|
||||||
case "stop-navigate":
|
case "stop-navigate":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
showNoteNavigator: false,
|
showNuggetNavigator: false,
|
||||||
};
|
};
|
||||||
case "notes-listed":
|
case "nuggets-listed":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
allNotes: action.notes,
|
allNuggets: action.nuggets,
|
||||||
};
|
};
|
||||||
case "show-palette":
|
case "show-palette":
|
||||||
return {
|
return {
|
||||||
|
@ -164,7 +164,7 @@ body {
|
|||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.current-note {
|
.current-nugget {
|
||||||
font-family: var(--editor-font);
|
font-family: var(--editor-font);
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { CommandDef } from "./plugins/types";
|
import { CommandDef } from "./plugins/types";
|
||||||
|
|
||||||
export type NoteMeta = {
|
export type NuggetMeta = {
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -14,27 +14,27 @@ export type AppCommand = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type AppViewState = {
|
export type AppViewState = {
|
||||||
currentNote?: string;
|
currentNugget?: string;
|
||||||
isSaved: boolean;
|
isSaved: boolean;
|
||||||
showNoteNavigator: boolean;
|
showNuggetNavigator: boolean;
|
||||||
showCommandPalette: boolean;
|
showCommandPalette: boolean;
|
||||||
allNotes: NoteMeta[];
|
allNuggets: NuggetMeta[];
|
||||||
commands: Map<string, AppCommand>;
|
commands: Map<string, AppCommand>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initialViewState: AppViewState = {
|
export const initialViewState: AppViewState = {
|
||||||
isSaved: false,
|
isSaved: false,
|
||||||
showNoteNavigator: false,
|
showNuggetNavigator: false,
|
||||||
showCommandPalette: false,
|
showCommandPalette: false,
|
||||||
allNotes: [],
|
allNuggets: [],
|
||||||
commands: new Map(),
|
commands: new Map(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
| { type: "note-loaded"; name: string }
|
| { type: "nugget-loaded"; name: string }
|
||||||
| { type: "note-saved" }
|
| { type: "nugget-saved" }
|
||||||
| { type: "note-updated" }
|
| { type: "nugget-updated" }
|
||||||
| { type: "notes-listed"; notes: NoteMeta[] }
|
| { type: "nuggets-listed"; nuggets: NuggetMeta[] }
|
||||||
| { type: "start-navigate" }
|
| { type: "start-navigate" }
|
||||||
| { type: "stop-navigate" }
|
| { type: "stop-navigate" }
|
||||||
| { type: "update-commands"; commands: Map<string, AppCommand> }
|
| { type: "update-commands"; commands: Map<string, AppCommand> }
|
||||||
|
Loading…
Reference in New Issue
Block a user