From c9f4266d34533b4daed7e9400df318139179eb74 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Mon, 21 Feb 2022 13:25:41 +0100 Subject: [PATCH] Cleanup, functional --- notes/Another note.md | 1 + notes/Super complicated, note.md | 1 + notes/Zef is cool.md | 1 + notes/start.md | 1 + notes/test.md | 4 +- notes/test3.md | 5 ++ server/server.ts | 18 ++++- webapp/src/app.js | 130 ------------------------------- webapp/src/app.tsx | 128 ++++++++++++------------------ webapp/src/commands.js | 38 --------- webapp/src/components/filter.tsx | 78 ++++++++++++------- webapp/src/customtags.js | 4 - webapp/src/fs.js | 24 ------ webapp/src/fs.ts | 5 +- webapp/src/index.html | 4 +- webapp/src/lineWrapper.js | 45 ----------- webapp/src/parser.js | 57 -------------- webapp/src/reducer.ts | 38 +++++++++ webapp/src/style.js | 31 -------- webapp/src/styles.css | 55 ++++++++++--- webapp/src/types.ts | 20 +++++ 21 files changed, 237 insertions(+), 451 deletions(-) create mode 100644 notes/Another note.md create mode 100644 notes/Super complicated, note.md create mode 100644 notes/Zef is cool.md create mode 100644 notes/test3.md delete mode 100644 webapp/src/app.js delete mode 100644 webapp/src/commands.js delete mode 100644 webapp/src/customtags.js delete mode 100644 webapp/src/fs.js delete mode 100644 webapp/src/lineWrapper.js delete mode 100644 webapp/src/parser.js create mode 100644 webapp/src/reducer.ts delete mode 100644 webapp/src/style.js create mode 100644 webapp/src/types.ts diff --git a/notes/Another note.md b/notes/Another note.md new file mode 100644 index 0000000..a130f7a --- /dev/null +++ b/notes/Another note.md @@ -0,0 +1 @@ +Sup yo \ No newline at end of file diff --git a/notes/Super complicated, note.md b/notes/Super complicated, note.md new file mode 100644 index 0000000..1f52d50 --- /dev/null +++ b/notes/Super complicated, note.md @@ -0,0 +1 @@ +Yo!! \ No newline at end of file diff --git a/notes/Zef is cool.md b/notes/Zef is cool.md new file mode 100644 index 0000000..47ef1a1 --- /dev/null +++ b/notes/Zef is cool.md @@ -0,0 +1 @@ +I know he is! **Bold** \ No newline at end of file diff --git a/notes/start.md b/notes/start.md index 8e3b89b..0ee7ac1 100644 --- a/notes/start.md +++ b/notes/start.md @@ -9,6 +9,7 @@ For the rest of you, if you are not a parent, have no plan to be, or have absolu Thank you for sharing that perspective. However, in your case specifically, I have to insist you keep reading. It’s kids with your independent mindset that we’re trying to raise here. Although, perhaps you already know how to do that. Get in touch. + Hello there ----- diff --git a/notes/test.md b/notes/test.md index db486cb..e7409dc 100644 --- a/notes/test.md +++ b/notes/test.md @@ -1 +1,3 @@ -# Sappie \ No newline at end of file +# Sappie + +Sup \ No newline at end of file diff --git a/notes/test3.md b/notes/test3.md new file mode 100644 index 0000000..ede42e1 --- /dev/null +++ b/notes/test3.md @@ -0,0 +1,5 @@ +# Hello + +## Second level header + +bla \ No newline at end of file diff --git a/server/server.ts b/server/server.ts index 070220e..e135486 100644 --- a/server/server.ts +++ b/server/server.ts @@ -27,14 +27,24 @@ fsRouter.get('/', async context => { fsRouter.get('/:note', async context => { const noteName = context.params.note; const localPath = `${notesPath}/${noteName}.md`; - const text = await Deno.readTextFile(localPath); - context.response.body = text; + try { + const text = await Deno.readTextFile(localPath); + context.response.body = text; + } catch (e) { + context.response.status = 404; + context.response.body = ""; + } }); fsRouter.options('/:note', async context => { const localPath = `${notesPath}/${context.params.note}.md`; - const stat = await Deno.stat(localPath); - context.response.headers.set('Content-length', `${stat.size}`); + try { + const stat = await Deno.stat(localPath); + context.response.headers.set('Content-length', `${stat.size}`); + } catch (e) { + context.response.status = 200; + context.response.body = ""; + } }) fsRouter.put('/:note', async context => { diff --git a/webapp/src/app.js b/webapp/src/app.js deleted file mode 100644 index bb59f7f..0000000 --- a/webapp/src/app.js +++ /dev/null @@ -1,130 +0,0 @@ -import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; -import { autocompletion, completionKeymap } from "@codemirror/autocomplete"; -import { closeBrackets, closeBracketsKeymap } from "@codemirror/closebrackets"; -import { indentWithTab, standardKeymap } from "@codemirror/commands"; -import { history, historyKeymap } from "@codemirror/history"; -import { indentOnInput } from "@codemirror/language"; -import { bracketMatching } from "@codemirror/matchbrackets"; -import { searchKeymap } from "@codemirror/search"; -import { EditorState, StateField } from "@codemirror/state"; -import { drawSelection, dropCursor, EditorView, highlightSpecialChars, keymap, } from "@codemirror/view"; -import * as commands from "./commands"; -import { markdown } from "./markdown"; -import { lineWrapper } from "./lineWrapper"; -import customMarkDown from "./parser"; -import customMarkdownStyle from "./style"; -import { HttpFileSystem } from "./fs"; -import ReactDOM from "react-dom"; -import { useEffect, useRef } from "react"; -const fs = new HttpFileSystem("http://localhost:2222/fs"); -class Editor { - constructor(parent, currentNote, text) { - this.view = new EditorView({ - state: this.createEditorState(text), - parent: parent, - }); - this.currentNote = currentNote; - } - load(name, text) { - this.currentNote = name; - this.view.setState(this.createEditorState(text)); - } - async save() { - await fs.writeNote(this.currentNote, this.view.state.sliceDoc()); - } - focus() { - this.view.focus(); - } - createEditorState(text) { - return EditorState.create({ - doc: text, - extensions: [ - highlightSpecialChars(), - history(), - drawSelection(), - dropCursor(), - indentOnInput(), - customMarkdownStyle, - bracketMatching(), - closeBrackets(), - autocompletion(), - EditorView.lineWrapping, - lineWrapper([ - { selector: "ATXHeading1", class: "line-h1" }, - { selector: "ATXHeading2", class: "line-h2" }, - { selector: "ListItem", class: "line-li" }, - { selector: "Blockquote", class: "line-blockquote" }, - { selector: "CodeBlock", class: "line-code" }, - { selector: "FencedCode", class: "line-fenced-code" }, - ]), - keymap.of([ - ...closeBracketsKeymap, - ...standardKeymap, - ...searchKeymap, - ...historyKeymap, - ...completionKeymap, - indentWithTab, - { - key: "Ctrl-b", - mac: "Cmd-b", - run: commands.insertMarker("**"), - }, - { - key: "Ctrl-i", - mac: "Cmd-i", - run: commands.insertMarker("_"), - }, - { - key: "Ctrl-s", - mac: "Cmd-s", - run: (target) => { - Promise.resolve() - .then(async () => { - console.log("Saving"); - await this.save(); - }) - .catch((e) => console.error(e)); - return true; - }, - }, - ]), - EditorView.domEventHandlers({ - click: (event, view) => { - if (event.metaKey || event.ctrlKey) { - console.log("Navigate click"); - let coords = view.posAtCoords(event); - console.log("Coords", view.state.doc.sliceString(coords, coords + 1)); - return false; - } - }, - }), - markdown({ - base: customMarkDown, - }), - StateField.define({ - create: () => null, - update: (value, transaction) => { - if (transaction.docChanged) { - console.log("Something changed"); - } - return null; - }, - }), - ], - }); - } -} -export const App = () => { - const editorRef = useRef(); - useEffect(() => { - let editor = new Editor(editorRef.current, "", ""); - editor.focus(); - // @ts-ignore - window.editor = editor; - fs.readNote("start").then((text) => { - editor.load("start", text); - }); - }, []); - return (_jsxs(_Fragment, { children: [_jsx("div", { id: "top", children: "Hello" }, void 0), _jsx("div", { id: "editor", ref: editorRef }, void 0), _jsx("div", { id: "bottom", children: "Bottom" }, void 0)] }, void 0)); -}; -ReactDOM.render(_jsx(App, {}, void 0), document.body); diff --git a/webapp/src/app.tsx b/webapp/src/app.tsx index 54996cf..d05405d 100644 --- a/webapp/src/app.tsx +++ b/webapp/src/app.tsx @@ -24,19 +24,11 @@ import customMarkdownStyle from "./style"; import { FilterList } from "./components/filter"; +import { NoteMeta, AppViewState, Action } from "./types"; +import reducer from "./reducer"; + const fs = new HttpFileSystem("http://localhost:2222/fs"); -type NoteMeta = { - name: string; -}; - -type AppViewState = { - currentNote: string; - isSaved: boolean; - isFiltering: boolean; - allNotes: NoteMeta[]; -}; - const initialViewState = { currentNote: "", isSaved: false, @@ -44,50 +36,6 @@ const initialViewState = { allNotes: [], }; -type Action = - | { type: "loaded"; name: string } - | { type: "saved" } - | { type: "start-navigate" } - | { type: "stop-navigate" } - | { type: "updated" } - | { type: "notes-list"; notes: NoteMeta[] }; - -function reducer(state: AppViewState, action: Action): AppViewState { - switch (action.type) { - case "loaded": - return { - ...state, - currentNote: action.name, - isSaved: true, - }; - case "saved": - return { - ...state, - isSaved: true, - }; - case "updated": - return { - ...state, - isSaved: false, - }; - case "start-navigate": - return { - ...state, - isFiltering: true, - }; - case "stop-navigate": - return { - ...state, - isFiltering: false, - }; - case "notes-list": - return { - ...state, - allNotes: action.notes, - }; - } -} - class Editor { view: EditorView; currentNote: string; @@ -184,7 +132,6 @@ class Editor { update(value: null, transaction: Transaction): null { if (transaction.docChanged) { - console.log("Something changed"); this.dispatch({ type: "updated", }); @@ -215,6 +162,10 @@ class Editor { focus() { this.view.focus(); } + + navigate(name: string) { + location.hash = encodeURIComponent(name); + } } function TopBar({ @@ -223,28 +174,32 @@ function TopBar({ isFiltering, allNotes, onNavigate, + onClick, }: { currentNote: string; isSaved: boolean; isFiltering: boolean; allNotes: NoteMeta[]; - onNavigate: (note: string) => void; + onNavigate: (note: string | undefined) => void; + onClick: () => void; }) { return (
- {currentNote} - {isSaved ? "" : "*"} +
+ » {currentNote} + {isSaved ? "" : "*"} +
- {isFiltering ? ( + {isFiltering && ( { console.log("Selected", opt); - onNavigate(opt.name); + onNavigate(opt?.name); }} > - ) : null} + )}
); } @@ -260,13 +215,9 @@ function AppView() { editor.focus(); // @ts-ignore window.editor = editor; - fs.readNote("start").then((text) => { - editor!.load("start", text); - dispatch({ - type: "loaded", - name: "start", - }); - }); + if (!location.hash) { + editor.navigate("start"); + } }, []); useEffect(() => { @@ -280,6 +231,30 @@ function AppView() { .catch((e) => console.error(e)); }, []); + useEffect(() => { + function hashChange() { + const noteName = decodeURIComponent(location.hash.substring(1)); + console.log("Now navigating to", noteName); + + fs.readNote(noteName) + .then((text) => { + editor!.load(noteName, text); + dispatch({ + type: "loaded", + name: noteName, + }); + }) + .catch((e) => { + console.error("Error loading note", e); + }); + } + hashChange(); + window.addEventListener("hashchange", hashChange); + return () => { + window.removeEventListener("hashchange", hashChange); + }; + }, []); + return ( <> { + dispatch({ type: "start-navigate" }); + }} onNavigate={(note) => { dispatch({ type: "stop-navigate" }); editor!.focus(); - fs.readNote(note).then((text) => { - editor!.load(note, text); - dispatch({ - type: "loaded", - name: note, - }); - }); + if (note) { + editor!.navigate(note); + } }} />
@@ -305,4 +279,4 @@ function AppView() { ); } -ReactDOM.render(, document.body); +ReactDOM.render(, document.getElementById("root")); diff --git a/webapp/src/commands.js b/webapp/src/commands.js deleted file mode 100644 index 178c7dc..0000000 --- a/webapp/src/commands.js +++ /dev/null @@ -1,38 +0,0 @@ -import { EditorSelection, Transaction } from "@codemirror/state"; -import { Text } from "@codemirror/text"; -export function insertMarker(marker) { - return ({ state, dispatch }) => { - const changes = state.changeByRange((range) => { - const isBoldBefore = state.sliceDoc(range.from - marker.length, range.from) === marker; - const isBoldAfter = state.sliceDoc(range.to, range.to + marker.length) === marker; - const changes = []; - changes.push(isBoldBefore ? { - from: range.from - marker.length, - to: range.from, - insert: Text.of(['']) - } : { - from: range.from, - insert: Text.of([marker]), - }); - changes.push(isBoldAfter ? { - from: range.to, - to: range.to + marker.length, - insert: Text.of(['']) - } : { - from: range.to, - insert: Text.of([marker]), - }); - const extendBefore = isBoldBefore ? -marker.length : marker.length; - const extendAfter = isBoldAfter ? -marker.length : marker.length; - return { - changes, - range: EditorSelection.range(range.from + extendBefore, range.to + extendAfter), - }; - }); - dispatch(state.update(changes, { - scrollIntoView: true, - annotations: Transaction.userEvent.of('input'), - })); - return true; - }; -} diff --git a/webapp/src/components/filter.tsx b/webapp/src/components/filter.tsx index 9cb4b62..912ccd5 100644 --- a/webapp/src/components/filter.tsx +++ b/webapp/src/components/filter.tsx @@ -2,16 +2,19 @@ import React, { useEffect, useRef, useState } from "react"; type Option = { name: string; + hint?: string; }; export function FilterList({ initialText, options, onSelect, + allowNew = false, }: { initialText: string; options: Option[]; - onSelect: (option: Option) => void; + onSelect: (option: Option | undefined) => void; + allowNew?: boolean; }) { const searchBoxRef = useRef(null); const [text, setText] = useState(initialText); @@ -19,18 +22,23 @@ export function FilterList({ const [selectedOption, setSelectionOption] = useState(0); const filter = (e: React.ChangeEvent) => { - const keyword = e.target.value.toLowerCase(); + const originalPhrase = e.target.value; + const searchPhrase = originalPhrase.toLowerCase(); - if (keyword) { - const results = options.filter((option) => { - return option.name.toLowerCase().indexOf(keyword) !== -1; + if (searchPhrase) { + let results = options.filter((option) => { + return option.name.toLowerCase().indexOf(searchPhrase) !== -1; + }); + results.splice(0, 0, { + name: originalPhrase, + hint: "Create new", }); setMatchingOptions(results); } else { setMatchingOptions(options); } - setText(keyword); + setText(originalPhrase); setSelectionOption(0); }; @@ -38,10 +46,22 @@ export function FilterList({ searchBoxRef.current!.focus(); }, []); + useEffect(() => { + function closer() { + onSelect(undefined); + } + document.addEventListener("click", closer); + + return () => { + console.log("Unsubscribing"); + document.removeEventListener("click", closer); + }; + }, []); + return (
- {matchingOptions && matchingOptions.length > 0 ? ( - matchingOptions.map((option, idx) => ( -
  • { - setSelectionOption(idx); - }} - onClick={(e) => { - onSelect(option); - e.preventDefault(); - }} - > - {option.name} -
  • - )) - ) : ( -

    No results found!

    - )} + {matchingOptions && matchingOptions.length > 0 + ? matchingOptions.map((option, idx) => ( +
    { + setSelectionOption(idx); + }} + onClick={(e) => { + e.preventDefault(); + onSelect(option); + }} + > + {option.name} + {option.hint && {option.hint}} +
    + )) + : null}
    ); diff --git a/webapp/src/customtags.js b/webapp/src/customtags.js deleted file mode 100644 index c5d9164..0000000 --- a/webapp/src/customtags.js +++ /dev/null @@ -1,4 +0,0 @@ -import { Tag } from '@codemirror/highlight'; -export const WikiLinkTag = Tag.define(); -export const TagTag = Tag.define(); -export const MentionTag = Tag.define(); diff --git a/webapp/src/fs.js b/webapp/src/fs.js deleted file mode 100644 index a32539d..0000000 --- a/webapp/src/fs.js +++ /dev/null @@ -1,24 +0,0 @@ -export class HttpFileSystem { - constructor(url) { - this.url = url; - } - async listNotes() { - let req = await fetch(this.url, { - method: 'GET' - }); - return (await req.json()).map((name) => ({ name })); - } - async readNote(name) { - let req = await fetch(`${this.url}/${name}`, { - method: 'GET' - }); - return await req.text(); - } - async writeNote(name, text) { - let req = await fetch(`${this.url}/${name}`, { - method: 'PUT', - body: text - }); - await req.text(); - } -} diff --git a/webapp/src/fs.ts b/webapp/src/fs.ts index 1454aec..d9ce5d5 100644 --- a/webapp/src/fs.ts +++ b/webapp/src/fs.ts @@ -1,7 +1,6 @@ -export interface NoteMeta { - name: string; -} +import { NoteMeta } from "./types"; + export interface FileSystem { listNotes(): Promise; diff --git a/webapp/src/index.html b/webapp/src/index.html index 733d73c..04cb059 100644 --- a/webapp/src/index.html +++ b/webapp/src/index.html @@ -8,5 +8,7 @@ - + +
    + diff --git a/webapp/src/lineWrapper.js b/webapp/src/lineWrapper.js deleted file mode 100644 index 71b5d9f..0000000 --- a/webapp/src/lineWrapper.js +++ /dev/null @@ -1,45 +0,0 @@ -import { syntaxTree } from '@codemirror/language'; -import { Decoration, ViewPlugin } from '@codemirror/view'; -function wrapLines(view, wrapElements) { - let widgets = []; - for (let { from, to } of view.visibleRanges) { - const doc = view.state.doc; - syntaxTree(view.state).iterate({ - from, to, - enter: (type, from, to) => { - const bodyText = doc.sliceString(from, to); - // console.log("Enter", type.name, bodyText); - for (let wrapElement of wrapElements) { - if (type.name == wrapElement.selector) { - const bodyText = doc.sliceString(from, to); - // console.log("Found", type.name, "with: ", bodyText); - let idx = from; - for (let line of bodyText.split("\n")) { - widgets.push(Decoration.line({ - class: wrapElement.class, - }).range(doc.lineAt(idx).from)); - idx += line.length + 1; - } - } - } - }, - leave(type, from, to) { - // console.log("Leaving", type.name); - } - }); - } - // console.log("All widgets", widgets); - return Decoration.set(widgets); -} -export const lineWrapper = (wrapElements) => ViewPlugin.fromClass(class { - constructor(view) { - this.decorations = wrapLines(view, wrapElements); - } - update(update) { - if (update.docChanged || update.viewportChanged) { - this.decorations = wrapLines(update.view, wrapElements); - } - } -}, { - decorations: v => v.decorations, -}); diff --git a/webapp/src/parser.js b/webapp/src/parser.js deleted file mode 100644 index 1297881..0000000 --- a/webapp/src/parser.js +++ /dev/null @@ -1,57 +0,0 @@ -import { styleTags } from '@codemirror/highlight'; -import { commonmark, mkLang } from "./markdown/markdown"; -import * as ct from './customtags'; -const WikiLink = { - defineNodes: ["WikiLink"], - parseInline: [{ - name: "WikiLink", - parse(cx, next, pos) { - let match; - if (next != 91 /* '[' */ || !(match = /^\[[^\]]+\]\]/.exec(cx.slice(pos + 1, cx.end)))) { - return -1; - } - return cx.addElement(cx.elt("WikiLink", pos, pos + 1 + match[0].length)); - }, - after: "Emphasis" - }] -}; -const AtMention = { - defineNodes: ["AtMention"], - parseInline: [{ - name: "AtMention", - parse(cx, next, pos) { - let match; - if (next != 64 /* '@' */ || !(match = /^[A-Za-z\.]+/.exec(cx.slice(pos + 1, cx.end)))) { - return -1; - } - return cx.addElement(cx.elt("AtMention", pos, pos + 1 + match[0].length)); - }, - after: "Emphasis" - }] -}; -const TagLink = { - defineNodes: ["TagLink"], - parseInline: [{ - name: "TagLink", - parse(cx, next, pos) { - let match; - if (next != 35 /* '#' */ || !(match = /^[A-Za-z\.]+/.exec(cx.slice(pos + 1, cx.end)))) { - return -1; - } - return cx.addElement(cx.elt("TagLink", pos, pos + 1 + match[0].length)); - }, - after: "Emphasis" - }] -}; -const WikiMarkdown = commonmark.configure([WikiLink, AtMention, TagLink, { - props: [ - styleTags({ - WikiLink: ct.WikiLinkTag, - AtMention: ct.MentionTag, - TagLink: ct.TagTag, - }) - ] - }]); -/// Language support for [GFM](https://github.github.com/gfm/) plus -/// subscript, superscript, and emoji syntax. -export default mkLang(WikiMarkdown); diff --git a/webapp/src/reducer.ts b/webapp/src/reducer.ts new file mode 100644 index 0000000..730abb5 --- /dev/null +++ b/webapp/src/reducer.ts @@ -0,0 +1,38 @@ +import { Action, AppViewState } from "./types"; + +export default function reducer(state: AppViewState, action: Action): AppViewState { + console.log("Got action", action) + switch (action.type) { + case "loaded": + return { + ...state, + currentNote: action.name, + isSaved: true, + }; + case "saved": + return { + ...state, + isSaved: true, + }; + case "updated": + return { + ...state, + isSaved: false, + }; + case "start-navigate": + return { + ...state, + isFiltering: true, + }; + case "stop-navigate": + return { + ...state, + isFiltering: false, + }; + case "notes-list": + return { + ...state, + allNotes: action.notes, + }; + } +} \ No newline at end of file diff --git a/webapp/src/style.js b/webapp/src/style.js deleted file mode 100644 index bb75d3b..0000000 --- a/webapp/src/style.js +++ /dev/null @@ -1,31 +0,0 @@ -import { HighlightStyle, tags as t } from '@codemirror/highlight'; -import * as ct from './customtags'; -export default HighlightStyle.define([ - { tag: t.heading1, class: "h1" }, - { tag: t.heading2, class: "h2" }, - { tag: t.link, class: "link" }, - { tag: t.meta, class: "meta" }, - { tag: t.quote, class: "quote" }, - { tag: t.monospace, class: "code" }, - { tag: t.url, class: "url" }, - { tag: ct.WikiLinkTag, class: "wiki-link" }, - { tag: ct.TagTag, class: "tag" }, - { tag: ct.MentionTag, class: "mention" }, - { tag: t.emphasis, class: "emphasis" }, - { tag: t.strong, class: "strong" }, - { tag: t.atom, class: "atom" }, - { tag: t.bool, class: "bool" }, - { tag: t.url, class: "url" }, - { tag: t.inserted, class: "inserted" }, - { tag: t.deleted, class: "deleted" }, - { tag: t.literal, class: "literal" }, - { tag: t.list, class: "list" }, - { tag: t.definition, class: "li" }, - { tag: t.string, class: "string" }, - { tag: t.number, class: "number" }, - { tag: [t.regexp, t.escape, t.special(t.string)], class: "string2" }, - { tag: t.variableName, class: "variableName" }, - { tag: t.comment, class: "comment" }, - { tag: t.invalid, class: "invalid" }, - { tag: t.punctuation, class: "punctuation" } -]); diff --git a/webapp/src/styles.css b/webapp/src/styles.css index 7e1f132..200c13a 100644 --- a/webapp/src/styles.css +++ b/webapp/src/styles.css @@ -5,25 +5,31 @@ body { padding: 0; } -body { - display: flex; - flex-direction: column; -} - #top { height: 40px; background-color: #eee; + position: absolute; + top: 0; + left: 0; + right: 0; } #bottom { height: 40px; background-color: #eee; margin: 0; + bottom: 0; + left: 0; + right: 0; + position: absolute; } #editor { - flex-grow: 1; - width: 100%; + position: absolute; + top: 40px; + bottom: 40px; + left: 0; + right: 0; overflow-y: hidden; } @@ -39,7 +45,7 @@ body { .cm-editor .cm-content { font-family: "Menlo"; - margin: 25px; + margin: 5px; } .cm-editor .cm-selectionBackground { @@ -142,15 +148,46 @@ reach-portal > div > div { padding: 5px; } +.current-note { + font-family: "Menlo"; + margin-left: 10px; + margin-top: 10px; + font-weight: bold; +} + .filter-container { + font-family: "Menlo"; background-color: white; display: block; position: absolute; left: 10px; top: 10px; + right: 10px; z-index: 1000; + border: #333 1px solid; +} + +.filter-container input { + font-family: "Menlo"; + width: 100%; + /* border: 1px #333 solid; */ + border: 0; + border-bottom: 1px #333 dotted; + padding: 3px; +} + +.filter-container .option, +.filter-container .selected-option { + padding: 3px 3px 3px 3px; + cursor: pointer; } .filter-container .selected-option { - background-color: #520130; + background-color: #b1b1b1; +} + +.filter-container .hint { + float: right; + margin-right: 10px; + color: #333; } diff --git a/webapp/src/types.ts b/webapp/src/types.ts new file mode 100644 index 0000000..a10d6b6 --- /dev/null +++ b/webapp/src/types.ts @@ -0,0 +1,20 @@ + +export type NoteMeta = { + name: string; +}; + +export type AppViewState = { + currentNote: string; + isSaved: boolean; + isFiltering: boolean; + allNotes: NoteMeta[]; +}; + +export type Action = + | { type: "loaded"; name: string } + | { type: "saved" } + | { type: "start-navigate" } + | { type: "stop-navigate" } + | { type: "updated" } + | { type: "notes-list"; notes: NoteMeta[] }; +