Notes -> Nuggets
This commit is contained in:
+2
-1
@@ -1,2 +1,3 @@
|
||||
.parcel-cache
|
||||
dist
|
||||
node_modules
|
||||
node_modules
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"scripts": {
|
||||
"start": "cp src/function_worker.js dist/ && parcel",
|
||||
"build": "parcel build && cp src/function_worker.js dist/",
|
||||
"clean": "rm -rf dist",
|
||||
"check-watch": "tsc --noEmit --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export function NavigationBar({
|
||||
currentNote,
|
||||
currentNugget,
|
||||
onClick,
|
||||
}: {
|
||||
currentNote?: string;
|
||||
currentNugget?: string;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div id="top">
|
||||
<div className="current-note" onClick={onClick}>
|
||||
» {currentNote}
|
||||
<div className="current-nugget" onClick={onClick}>
|
||||
» {currentNugget}
|
||||
</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);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
+55
-51
@@ -24,9 +24,9 @@ import ReactDOM from "react-dom";
|
||||
import coreManifest from "../../plugins/dist/core.plugin.json";
|
||||
import { buildContext } from "./buildContext";
|
||||
import * as commands from "./commands";
|
||||
import { CommandPalette } from "./components/commandpalette";
|
||||
import { CommandPalette } from "./components/command_palette";
|
||||
import { NavigationBar } from "./components/navigation_bar";
|
||||
import { NoteNavigator } from "./components/notenavigator";
|
||||
import { NuggetNavigator } from "./components/nugget_navigator";
|
||||
import { StatusBar } from "./components/status_bar";
|
||||
import { FileSystem, HttpFileSystem } from "./fs";
|
||||
import { lineWrapper } from "./lineWrapper";
|
||||
@@ -47,7 +47,7 @@ import {
|
||||
} from "./types";
|
||||
import { safeRun } from "./util";
|
||||
|
||||
class NoteState {
|
||||
class NuggetState {
|
||||
editorState: EditorState;
|
||||
scrollTop: number;
|
||||
|
||||
@@ -62,13 +62,13 @@ export class Editor {
|
||||
viewState: AppViewState;
|
||||
viewDispatch: React.Dispatch<Action>;
|
||||
$hashChange?: () => void;
|
||||
openNotes: Map<string, NoteState>;
|
||||
openNuggets: Map<string, NuggetState>;
|
||||
fs: FileSystem;
|
||||
editorCommands: Map<string, AppCommand>;
|
||||
|
||||
constructor(fs: FileSystem, parent: Element) {
|
||||
this.editorCommands = new Map();
|
||||
this.openNotes = new Map();
|
||||
this.openNuggets = new Map();
|
||||
this.fs = fs;
|
||||
this.viewState = initialViewState;
|
||||
this.viewDispatch = () => {};
|
||||
@@ -81,7 +81,7 @@ export class Editor {
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.loadNoteList();
|
||||
await this.loadNuggetList();
|
||||
await this.loadPlugins();
|
||||
this.$hashChange!();
|
||||
this.focus();
|
||||
@@ -111,8 +111,8 @@ export class Editor {
|
||||
});
|
||||
}
|
||||
|
||||
get currentNote(): string | undefined {
|
||||
return this.viewState.currentNote;
|
||||
get currentNugget(): string | undefined {
|
||||
return this.viewState.currentNugget;
|
||||
}
|
||||
|
||||
createEditorState(text: string): EditorState {
|
||||
@@ -146,7 +146,7 @@ export class Editor {
|
||||
bracketMatching(),
|
||||
closeBrackets(),
|
||||
autocompletion({
|
||||
override: [this.noteCompleter.bind(this)],
|
||||
override: [this.nuggetCompleter.bind(this)],
|
||||
}),
|
||||
EditorView.lineWrapping,
|
||||
lineWrapper([
|
||||
@@ -219,18 +219,18 @@ export class Editor {
|
||||
});
|
||||
}
|
||||
|
||||
noteCompleter(ctx: CompletionContext): CompletionResult | null {
|
||||
nuggetCompleter(ctx: CompletionContext): CompletionResult | null {
|
||||
let prefix = ctx.matchBefore(/\[\[\w*/);
|
||||
if (!prefix) {
|
||||
return null;
|
||||
}
|
||||
// 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 {
|
||||
from: prefix.from + 2,
|
||||
options: this.viewState.allNotes.map((noteMeta) => ({
|
||||
label: noteMeta.name,
|
||||
type: "note",
|
||||
options: this.viewState.allNuggets.map((nuggetMeta) => ({
|
||||
label: nuggetMeta.name,
|
||||
type: "nugget",
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -238,7 +238,7 @@ export class Editor {
|
||||
update(value: null, transaction: Transaction): null {
|
||||
if (transaction.docChanged) {
|
||||
this.viewDispatch({
|
||||
type: "note-updated",
|
||||
type: "nugget-updated",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -250,8 +250,8 @@ export class Editor {
|
||||
let coords = view.posAtCoords(event)!;
|
||||
let node = syntaxTree(view.state).resolveInner(coords);
|
||||
if (node && node.name === "WikiLinkPage") {
|
||||
let noteName = view.state.sliceDoc(node.from, node.to);
|
||||
this.navigate(noteName);
|
||||
let nuggetName = view.state.sliceDoc(node.from, node.to);
|
||||
this.navigate(nuggetName);
|
||||
}
|
||||
if (node && node.name === "TaskMarker") {
|
||||
let checkBoxText = view.state.sliceDoc(node.from, node.to);
|
||||
@@ -272,35 +272,35 @@ export class Editor {
|
||||
async save() {
|
||||
const editorState = this.editorView!.state;
|
||||
|
||||
if (!this.currentNote) {
|
||||
if (!this.currentNugget) {
|
||||
return;
|
||||
}
|
||||
// Write to file system
|
||||
const created = await this.fs.writeNote(
|
||||
this.currentNote,
|
||||
const created = await this.fs.writeNugget(
|
||||
this.currentNugget,
|
||||
editorState.sliceDoc()
|
||||
);
|
||||
|
||||
// Update in open note cache
|
||||
this.openNotes.set(
|
||||
this.currentNote,
|
||||
new NoteState(editorState, this.editorView!.scrollDOM.scrollTop)
|
||||
// Update in open nugget cache
|
||||
this.openNuggets.set(
|
||||
this.currentNugget,
|
||||
new NuggetState(editorState, this.editorView!.scrollDOM.scrollTop)
|
||||
);
|
||||
|
||||
// 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) {
|
||||
await this.loadNoteList();
|
||||
await this.loadNuggetList();
|
||||
}
|
||||
}
|
||||
|
||||
async loadNoteList() {
|
||||
let notesMeta = await this.fs.listNotes();
|
||||
async loadNuggetList() {
|
||||
let nuggetsMeta = await this.fs.listNuggets();
|
||||
this.viewDispatch({
|
||||
type: "notes-listed",
|
||||
notes: notesMeta,
|
||||
type: "nuggets-listed",
|
||||
nuggets: nuggetsMeta,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -316,25 +316,25 @@ export class Editor {
|
||||
Promise.resolve()
|
||||
.then(async () => {
|
||||
await this.save();
|
||||
const noteName = decodeURIComponent(location.hash.substring(1));
|
||||
console.log("Now navigating to", noteName);
|
||||
const nuggetName = decodeURIComponent(location.hash.substring(1));
|
||||
console.log("Now navigating to", nuggetName);
|
||||
|
||||
if (!this.editorView) {
|
||||
return;
|
||||
}
|
||||
|
||||
let noteState = this.openNotes.get(noteName);
|
||||
if (!noteState) {
|
||||
let text = await this.fs.readNote(noteName);
|
||||
noteState = new NoteState(this.createEditorState(text), 0);
|
||||
let nuggetState = this.openNuggets.get(nuggetName);
|
||||
if (!nuggetState) {
|
||||
let text = await this.fs.readNugget(nuggetName);
|
||||
nuggetState = new NuggetState(this.createEditorState(text), 0);
|
||||
}
|
||||
this.openNotes.set(noteName, noteState!);
|
||||
this.editorView!.setState(noteState!.editorState);
|
||||
this.editorView.scrollDOM.scrollTop = noteState!.scrollTop;
|
||||
this.openNuggets.set(nuggetName, nuggetState!);
|
||||
this.editorView!.setState(nuggetState!.editorState);
|
||||
this.editorView.scrollDOM.scrollTop = nuggetState!.scrollTop;
|
||||
|
||||
this.viewDispatch({
|
||||
type: "note-loaded",
|
||||
name: noteName,
|
||||
type: "nugget-loaded",
|
||||
name: nuggetName,
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -378,24 +378,28 @@ export class Editor {
|
||||
|
||||
let editor = this;
|
||||
|
||||
useEffect(() => {}, []);
|
||||
useEffect(() => {
|
||||
if (viewState.currentNugget) {
|
||||
document.title = viewState.currentNugget;
|
||||
}
|
||||
}, [viewState.currentNugget]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{viewState.showNoteNavigator && (
|
||||
<NoteNavigator
|
||||
allNotes={viewState.allNotes}
|
||||
onNavigate={(note) => {
|
||||
{viewState.showNuggetNavigator && (
|
||||
<NuggetNavigator
|
||||
allNuggets={viewState.allNuggets}
|
||||
onNavigate={(nugget) => {
|
||||
dispatch({ type: "stop-navigate" });
|
||||
editor!.focus();
|
||||
if (note) {
|
||||
if (nugget) {
|
||||
editor
|
||||
?.save()
|
||||
.then(() => {
|
||||
editor!.navigate(note);
|
||||
editor!.navigate(nugget);
|
||||
})
|
||||
.catch((e) => {
|
||||
alert("Could not save note, not switching");
|
||||
alert("Could not save nugget, not switching");
|
||||
});
|
||||
}
|
||||
}}
|
||||
@@ -417,7 +421,7 @@ export class Editor {
|
||||
/>
|
||||
)}
|
||||
<NavigationBar
|
||||
currentNote={viewState.currentNote}
|
||||
currentNugget={viewState.currentNugget}
|
||||
onClick={() => {
|
||||
dispatch({ type: "start-navigate" });
|
||||
}}
|
||||
|
||||
+9
-9
@@ -1,10 +1,10 @@
|
||||
import { NoteMeta } from "./types";
|
||||
import { NuggetMeta } from "./types";
|
||||
|
||||
export interface FileSystem {
|
||||
listNotes(): Promise<NoteMeta[]>;
|
||||
readNote(name: string): Promise<string>;
|
||||
// @return whether a new note was created for this
|
||||
writeNote(name: string, text: string): Promise<boolean>;
|
||||
listNuggets(): Promise<NuggetMeta[]>;
|
||||
readNugget(name: string): Promise<string>;
|
||||
// @return whether a new nugget was created for this
|
||||
writeNugget(name: string, text: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
export class HttpFileSystem implements FileSystem {
|
||||
@@ -12,25 +12,25 @@ export class HttpFileSystem implements FileSystem {
|
||||
constructor(url: string) {
|
||||
this.url = url;
|
||||
}
|
||||
async listNotes(): Promise<NoteMeta[]> {
|
||||
async listNuggets(): Promise<NuggetMeta[]> {
|
||||
let req = await fetch(this.url, {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
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}`, {
|
||||
method: "GET",
|
||||
});
|
||||
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}`, {
|
||||
method: "PUT",
|
||||
body: text,
|
||||
});
|
||||
// 201 (Created) means a new note was created
|
||||
// 201 (Created) means a new nugget was created
|
||||
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) {
|
||||
fn().catch((e) => {
|
||||
console.error(e);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Noot</title>
|
||||
<title>Nugget</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
<script type="module" src="editor.tsx"></script>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
+16
-2
@@ -25,7 +25,6 @@ self.addEventListener("install", (event) => {
|
||||
console.log("Installing");
|
||||
// @ts-ignore
|
||||
self.skipWaiting();
|
||||
// event.waitUntil(fetchBundle());
|
||||
});
|
||||
|
||||
async function handlePut(req: Request, path: string) {
|
||||
@@ -36,6 +35,21 @@ async function handlePut(req: Request, path: string) {
|
||||
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) => {
|
||||
const req = event.request;
|
||||
if (req.url.startsWith(rootUrl)) {
|
||||
@@ -75,7 +89,7 @@ self.addEventListener("fetch", (event: any) => {
|
||||
status: 404,
|
||||
});
|
||||
}
|
||||
return new Response(func.code, {
|
||||
return new Response(wrapScript(func.functionName!, func.code!), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-type": "application/javascript",
|
||||
|
||||
@@ -23,5 +23,6 @@ export interface CommandDef {
|
||||
|
||||
export interface FunctionDef {
|
||||
path: string;
|
||||
functionName?: string;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
@@ -6,18 +6,18 @@ export default function reducer(
|
||||
): AppViewState {
|
||||
console.log("Got action", action);
|
||||
switch (action.type) {
|
||||
case "note-loaded":
|
||||
case "nugget-loaded":
|
||||
return {
|
||||
...state,
|
||||
currentNote: action.name,
|
||||
currentNugget: action.name,
|
||||
isSaved: true,
|
||||
};
|
||||
case "note-saved":
|
||||
case "nugget-saved":
|
||||
return {
|
||||
...state,
|
||||
isSaved: true,
|
||||
};
|
||||
case "note-updated":
|
||||
case "nugget-updated":
|
||||
// Minor rerender optimization, this is triggered a lot
|
||||
if (!state.isSaved) {
|
||||
return state;
|
||||
@@ -29,17 +29,17 @@ export default function reducer(
|
||||
case "start-navigate":
|
||||
return {
|
||||
...state,
|
||||
showNoteNavigator: true,
|
||||
showNuggetNavigator: true,
|
||||
};
|
||||
case "stop-navigate":
|
||||
return {
|
||||
...state,
|
||||
showNoteNavigator: false,
|
||||
showNuggetNavigator: false,
|
||||
};
|
||||
case "notes-listed":
|
||||
case "nuggets-listed":
|
||||
return {
|
||||
...state,
|
||||
allNotes: action.notes,
|
||||
allNuggets: action.nuggets,
|
||||
};
|
||||
case "show-palette":
|
||||
return {
|
||||
|
||||
@@ -164,7 +164,7 @@ body {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.current-note {
|
||||
.current-nugget {
|
||||
font-family: var(--editor-font);
|
||||
margin-left: 10px;
|
||||
margin-top: 10px;
|
||||
|
||||
+10
-10
@@ -1,6 +1,6 @@
|
||||
import { CommandDef } from "./plugins/types";
|
||||
|
||||
export type NoteMeta = {
|
||||
export type NuggetMeta = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
@@ -14,27 +14,27 @@ export type AppCommand = {
|
||||
};
|
||||
|
||||
export type AppViewState = {
|
||||
currentNote?: string;
|
||||
currentNugget?: string;
|
||||
isSaved: boolean;
|
||||
showNoteNavigator: boolean;
|
||||
showNuggetNavigator: boolean;
|
||||
showCommandPalette: boolean;
|
||||
allNotes: NoteMeta[];
|
||||
allNuggets: NuggetMeta[];
|
||||
commands: Map<string, AppCommand>;
|
||||
};
|
||||
|
||||
export const initialViewState: AppViewState = {
|
||||
isSaved: false,
|
||||
showNoteNavigator: false,
|
||||
showNuggetNavigator: false,
|
||||
showCommandPalette: false,
|
||||
allNotes: [],
|
||||
allNuggets: [],
|
||||
commands: new Map(),
|
||||
};
|
||||
|
||||
export type Action =
|
||||
| { type: "note-loaded"; name: string }
|
||||
| { type: "note-saved" }
|
||||
| { type: "note-updated" }
|
||||
| { type: "notes-listed"; notes: NoteMeta[] }
|
||||
| { type: "nugget-loaded"; name: string }
|
||||
| { type: "nugget-saved" }
|
||||
| { type: "nugget-updated" }
|
||||
| { type: "nuggets-listed"; nuggets: NuggetMeta[] }
|
||||
| { type: "start-navigate" }
|
||||
| { type: "stop-navigate" }
|
||||
| { type: "update-commands"; commands: Map<string, AppCommand> }
|
||||
|
||||
Reference in New Issue
Block a user