diff --git a/package-lock.json b/package-lock.json
index 96d2db5..cb7acb5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,14 +9,20 @@
"version": "0.0.1",
"license": "ISC",
"dependencies": {
+ "@editorjs/editorjs": "^2.30.7",
"@lit/localize": "^0.12.2",
"@shoelace-style/shoelace": "^2.18.0",
"@thepassle/app-tools": "^0.9.12",
"axios": "^1.7.7",
"cors": "^2.8.5",
"express": "^4.21.1",
+ "lazy.js": "^0.5.1",
"lit": "^3.2.1",
"nostr-tools": "^2.10.1",
+ "prosemirror-markdown": "^1.13.1",
+ "prosemirror-model": "^1.24.1",
+ "prosemirror-state": "^1.4.3",
+ "prosemirror-view": "^1.37.1",
"urlpattern-polyfill": "^10.0.0",
"workbox-build": "^7.3.0",
"workbox-core": "^7.3.0",
@@ -1505,6 +1511,11 @@
"node": ">=14"
}
},
+ "node_modules/@editorjs/editorjs": {
+ "version": "2.30.7",
+ "resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.30.7.tgz",
+ "integrity": "sha512-FfdeUqrgcKWC+Cy2GW6Dxup6s2TaRKokR4FL+HKXshu6h9Y//rrx4SQkURgkZOCSbV77t9btbmAXdFXWGB+diw=="
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@@ -2464,6 +2475,25 @@
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"license": "MIT"
},
+ "node_modules/@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="
+ },
+ "node_modules/@types/markdown-it": {
+ "version": "14.1.2",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
+ "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
+ "dependencies": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
+ }
+ },
+ "node_modules/@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="
+ },
"node_modules/@types/prop-types": {
"version": "15.7.13",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
@@ -2558,6 +2588,11 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+ },
"node_modules/array-buffer-byte-length": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
@@ -3203,6 +3238,17 @@
"node": ">= 0.8"
}
},
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/es-abstract": {
"version": "1.23.3",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
@@ -4302,6 +4348,11 @@
"node": ">=0.10.0"
}
},
+ "node_modules/lazy.js": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/lazy.js/-/lazy.js-0.5.1.tgz",
+ "integrity": "sha512-p9v24vaKrzS2mEx3yuzva/3M6I3+HwvXd0pB1Xf/IvsFIMdhQgmym7JBO0e7c0OZmKTo07sCuiCIm6jazSWFNw=="
+ },
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -4311,6 +4362,14 @@
"node": ">=6"
}
},
+ "node_modules/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
"node_modules/lit": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/lit/-/lit-3.2.1.tgz",
@@ -4378,6 +4437,27 @@
"sourcemap-codec": "^1.4.8"
}
},
+ "node_modules/markdown-it": {
+ "version": "14.1.0",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+ "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="
+ },
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -4580,6 +4660,11 @@
"wrappy": "1"
}
},
+ "node_modules/orderedmap": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
+ "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="
+ },
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -4677,6 +4762,52 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/prosemirror-markdown": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.1.tgz",
+ "integrity": "sha512-Sl+oMfMtAjWtlcZoj/5L/Q39MpEnVZ840Xo330WJWUvgyhNmLBLN7MsHn07s53nG/KImevWHSE6fEj4q/GihHw==",
+ "dependencies": {
+ "@types/markdown-it": "^14.0.0",
+ "markdown-it": "^14.0.0",
+ "prosemirror-model": "^1.20.0"
+ }
+ },
+ "node_modules/prosemirror-model": {
+ "version": "1.24.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.24.1.tgz",
+ "integrity": "sha512-YM053N+vTThzlWJ/AtPtF1j0ebO36nvbmDy4U7qA2XQB8JVaQp1FmB9Jhrps8s+z+uxhhVTny4m20ptUvhk0Mg==",
+ "dependencies": {
+ "orderedmap": "^2.0.0"
+ }
+ },
+ "node_modules/prosemirror-state": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
+ "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-transform": "^1.0.0",
+ "prosemirror-view": "^1.27.0"
+ }
+ },
+ "node_modules/prosemirror-transform": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.2.tgz",
+ "integrity": "sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==",
+ "dependencies": {
+ "prosemirror-model": "^1.21.0"
+ }
+ },
+ "node_modules/prosemirror-view": {
+ "version": "1.37.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.37.1.tgz",
+ "integrity": "sha512-MEAnjOdXU1InxEmhjgmEzQAikaS6lF3hD64MveTPpjOGNTl87iRLA1HupC/DEV6YuK7m4Q9DHFNTjwIVtqz5NA==",
+ "dependencies": {
+ "prosemirror-model": "^1.20.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.1.0"
+ }
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -4703,6 +4834,14 @@
"node": ">=6"
}
},
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/qr-creator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/qr-creator/-/qr-creator-1.0.0.tgz",
@@ -5534,6 +5673,11 @@
"node": ">=14.17"
}
},
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="
+ },
"node_modules/unbox-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
diff --git a/package.json b/package.json
index 97d23e6..46ab7e8 100644
--- a/package.json
+++ b/package.json
@@ -15,14 +15,20 @@
"author": "",
"license": "ISC",
"dependencies": {
+ "@editorjs/editorjs": "^2.30.7",
"@lit/localize": "^0.12.2",
"@shoelace-style/shoelace": "^2.18.0",
"@thepassle/app-tools": "^0.9.12",
"axios": "^1.7.7",
"cors": "^2.8.5",
"express": "^4.21.1",
+ "lazy.js": "^0.5.1",
"lit": "^3.2.1",
"nostr-tools": "^2.10.1",
+ "prosemirror-markdown": "^1.13.1",
+ "prosemirror-model": "^1.24.1",
+ "prosemirror-state": "^1.4.3",
+ "prosemirror-view": "^1.37.1",
"urlpattern-polyfill": "^10.0.0",
"workbox-build": "^7.3.0",
"workbox-core": "^7.3.0",
diff --git a/src/components/menu-plugin.ts b/src/components/menu-plugin.ts
new file mode 100644
index 0000000..e0476ad
--- /dev/null
+++ b/src/components/menu-plugin.ts
@@ -0,0 +1,40 @@
+import { Plugin } from 'prosemirror-state';
+
+export const plusButtonPlugin = new Plugin({
+ view(editorView) {
+ const button = document.createElement('button');
+ button.textContent = '+';
+ button.className = 'plus-button'
+
+ button.style.position = 'absolute';
+ button.style.zIndex = '10';
+ button.style.display = 'none'; // Hide initially
+
+ editorView.dom.appendChild(button);
+
+ button.addEventListener('click', () => {
+ console.log('Plus button clicked');
+ // Add logic to open dropdown or execute commands
+ });
+
+ return {
+ update(view) {
+ const { $from } = view.state.selection;
+
+ // Only show the button when the cursor is in a text block with no content
+ if ($from.parent.isTextblock && $from.parent.content.size === 0) {
+ const coords = view.coordsAtPos($from.pos);
+ // Position the button relative to the cursor
+ button.style.top = `${coords.top + window.scrollY}px`; // Added scroll offset for better positioning
+ button.style.left = `${coords.left + window.scrollX - 30}px`; // Added scroll offset
+ button.style.display = 'block'; // Make the button visible
+ } else {
+ button.style.display = 'none'; // Hide the button when not in valid position
+ }
+ },
+ destroy() {
+ button.remove(); // Cleanup on destroy
+ },
+ };
+ },
+});
diff --git a/src/pages/app-about/app-about.ts b/src/pages/app-about/app-about.ts
index 4ee9564..e825583 100644
--- a/src/pages/app-about/app-about.ts
+++ b/src/pages/app-about/app-about.ts
@@ -1,12 +1,9 @@
import { LitElement, html, css } from 'lit';
import { customElement } from 'lit/decorators.js';
-// You can also import styles from another file
-// if you prefer to keep your CSS seperate from your component
import { styles } from './about-styles';
import { styles as sharedStyles } from '../../styles/shared-styles'
-
import '@shoelace-style/shoelace/dist/components/card/card.js';
@customElement('app-about')
@@ -16,29 +13,6 @@ export class AppAbout extends LitElement {
styles,
css`
- #welcomeBar {
- display: flex;
- justify-content: center;
- align-items: center;
- flex-direction: column;
- }
-
- #welcomeCard,
- #infoCard {
- padding: 18px;
- padding-top: 0px;
- }
-
- sl-card::part(footer) {
- display: flex;
- justify-content: flex-end;
- }
-
- @media(min-width: 750px) {
- sl-card {
- width: 70vw;
- }
- }
`
]
@@ -74,7 +48,8 @@ export class AppAbout extends LitElement {
-
- Nostr Tools
+ I am using Nostr Tools to implement an extension
+ sign in and pull notes from my personal relay.
-
diff --git a/src/pages/app-home.ts b/src/pages/app-home.ts
index 20e5253..8f85eca 100644
--- a/src/pages/app-home.ts
+++ b/src/pages/app-home.ts
@@ -4,11 +4,13 @@ import { resolveRouterPath } from '../router';
import { Relay } from 'nostr-tools';
import { WindowNostr } from 'nostr-tools/nip07';
+
import '@shoelace-style/shoelace/dist/components/card/card.js';
import '@shoelace-style/shoelace/dist/components/button/button.js';
import { styles } from '../styles/shared-styles';
+
declare global {
interface Window {
nostr?: WindowNostr;
@@ -31,29 +33,7 @@ export class AppHome extends LitElement {
static styles = [
styles,
css`
- #welcomeBar {
- display: flex;
- justify-content: center;
- align-items: center;
- flex-direction: column;
- }
- #welcomeCard,
- #infoCard {
- padding: 18px;
- padding-top: 0px;
- }
-
- sl-card::part(footer) {
- display: flex;
- justify-content: flex-end;
- }
-
- @media(min-width: 750px) {
- sl-card {
- width: 70vw;
- }
- }
@media (horizontal-viewport-segments: 2) {
@@ -209,16 +189,16 @@ Profile Picture Container
-
Find me on any nostr client by searching ${this.nostrAddy || 'Guest'}
+ Welcome to ${this.nostrAddy || 'Guest'}'s Profile
-
You can use the Nostr protocol to transform your personal website into
- a micro social media client. It eliminates the need to use a corporate
- social media account. I am using nostr-tools to implement an extension
- sign in and pull notes from my personal relay.
+ a micro blog client. You can compose a note and post it to your social
+ network.
+
+
@@ -237,7 +217,9 @@ Profile Picture Container
Navigate to About
Navigate to Note Wall
-
+ Navigate to Note Compose
+
+
`;
}
diff --git a/src/pages/app-write/app-write.ts b/src/pages/app-write/app-write.ts
new file mode 100644
index 0000000..ef744e3
--- /dev/null
+++ b/src/pages/app-write/app-write.ts
@@ -0,0 +1,242 @@
+import { LitElement, css, html } from 'lit';
+import { property, customElement, query } from 'lit/decorators.js';
+
+import '@shoelace-style/shoelace/dist/components/card/card.js';
+
+import '@shoelace-style/shoelace/dist/components/button/button.js';
+
+import { styles } from '../../styles/shared-styles';
+
+import {EditorState} from 'prosemirror-state'
+import { Transaction } from 'prosemirror-state';
+import { EditorView } from 'prosemirror-view';
+import { Schema } from 'prosemirror-model';
+import { keymap } from 'prosemirror-keymap';
+import { toggleMark } from 'prosemirror-commands';
+import {undo, redo, history} from 'prosemirror-history'
+import { baseKeymap } from 'prosemirror-commands';
+
+import { plusButtonPlugin } from '../../components/menu-plugin';
+
+
+
+const customSchema = new Schema({
+ nodes: {
+ text: {
+ group: 'inline',
+ },
+ star: {
+ inline: true,
+ group: 'inline',
+ toDOM() {
+ return ['star', '⭐'];
+ },
+ parseDOM: [{ tag: 'star' }],
+ },
+ paragraph: {
+ group: 'block',
+ content: 'inline*',
+ toDOM() {
+ return ['p', 0];
+ },
+ parseDOM: [{ tag: 'p' }],
+ },
+ boring_paragraph: {
+ group: 'block',
+ content: 'text*',
+ marks: '',
+ toDOM() {
+ return ['p', { class: 'boring' }, 0];
+ },
+ parseDOM: [{ tag: 'p.boring', priority: 60 }],
+ },
+ doc: {
+ content: 'block+',
+ },
+ },
+ marks: {
+ shouting: {
+ toDOM() {
+ return ['shouting', 0];
+ },
+ parseDOM: [{ tag: 'shouting' }],
+ },
+ link: {
+ attrs: { href: {} },
+ toDOM(node) {
+ return ['a', { href: node.attrs.href }, 0];
+ },
+ parseDOM: [
+ {
+ tag: 'a',
+ getAttrs(dom) {
+ return { href: dom };
+ },
+ },
+ ],
+ inclusive: false,
+ },
+ },
+ });
+
+ // Commands
+ function insertStar(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
+ const type = customSchema.nodes.star;
+ const { $from } = state.selection;
+
+ // If the parent cannot replace with the 'star' node, return false
+ if (!$from.parent.canReplaceWith($from.index(), $from.index(), type)) {
+ return false;
+ }
+
+ // If dispatch is provided, apply the transaction
+ if (dispatch) {
+ dispatch(state.tr.replaceSelectionWith(type.create()));
+ }
+
+ // Always return true if insertion conditions are met
+ return true;
+ }
+
+ function toggleLink(state: EditorState, dispatch?: (tr: Transaction) => void): boolean {
+ let {doc, selection} = state
+ if (selection.empty) return false
+ let attrs = null
+ if (!doc.rangeHasMark(selection.from, selection.to, customSchema.marks.link)) {
+ attrs = {href: prompt("Link to where?", "")}
+ if (!attrs.href) return false
+ }
+ return toggleMark(customSchema.marks.link, attrs)(state, dispatch)
+ }
+
+
+ // Keymap
+ const customKeymap = keymap({
+ 'Ctrl-Space': insertStar,
+ "Ctrl-b": (state, dispatch) => {
+ console.log("Ctrl-b pressed, toggling shouting mark...");
+ return toggleMark(customSchema.marks.shouting)(state, dispatch);
+ },
+ 'Ctrl-q': (state, dispatch) => {
+ console.log("you should have just gotten an alert");
+ return toggleLink(state, dispatch);
+ },
+
+ });
+
+@customElement('app-write')
+export class AppWrite extends LitElement {
+ @property({ type: String }) placeholder: string = 'Compose your note...';
+ @property({ type: String }) public value?: string;
+
+ // Reference to the ProseMirror container
+ @query('#editor') editorContainer!: HTMLElement;
+
+ private editorView!: EditorView
+
+
+
+
+ static styles = [
+ styles, css`
+ :host {
+ display: block;
+ }
+
+ shouting {
+ all: unset; /* Remove inherited or conflicting styles */
+ font-weight: bold;
+ text-transform: uppercase;
+ color: red; /* Add a visible color for debugging */
+}
+
+ `
+
+ ];
+
+ protected async firstUpdated() {
+ console.log("Welcome to the compose page");
+ await this.updateComplete;
+ this.initializeEditor()
+
+}
+ private initializeEditor() {
+ if (!this.editorContainer) {
+ console.error('Editor container not here');
+ return
+ }
+ const state = EditorState.create({
+ schema: customSchema,
+ plugins: [
+
+ history(),
+ keymap({
+ 'Mod-z': undo,
+ 'Mod-y': redo,
+ }),
+ keymap(baseKeymap),
+ // Add the plus-button plugin here
+ plusButtonPlugin
+ ],
+ });
+
+ let view = new EditorView(this.editorContainer, {
+ state,
+ dispatchTransaction(transaction) {
+ console.log('Document size went from', transaction.before.content.size, "to",
+ transaction.doc.content.size
+ )
+ let newState = view.state.apply(transaction)
+ view.updateState(newState)
+ }
+ });
+ this.editorView = view;
+ console.log('editor initialized')
+
+ }
+
+ disconnectedCallback(): void {
+ super.disconnectedCallback();
+ if (this.editorView) {
+ this.editorView.destroy();
+ }
+ }
+
+
+
+ protected render() {
+ return html`
+
+
+
+
+
+
+
+
Editor Demo
+
+
+
+ Aren't you tired of distracting interfaces on mainstream blogging platforms?
+ So many buttons and advertising makes a single task very draining. This is
+ a platform-agnostic browser application that is installable to any homescreen!
+
+ You can use a protocol to transform your personal website into
+ a micro blog client. Using WYSIWYG style editor builders like
+ ProseMirror, you can render a simple rich text editor for visitors to compose
+ notes with. You can type out a note and post it to a set of relays.
+
+ Basic Intructions
+ click inside the white text area to place your curson there.
+ You can type and press enter to go to the next line down.
+
+ Click and hold down to select a piece of text, then CTRL+X to cut and
+ CTRL+V to paste.
+
+
+
+
+
+
+ `}
+ }
\ No newline at end of file
diff --git a/src/router.ts b/src/router.ts
index 636b6c0..08a1eed 100644
--- a/src/router.ts
+++ b/src/router.ts
@@ -14,6 +14,7 @@ import { title } from '@thepassle/app-tools/router/plugins/title.js';
import './pages/app-home.js';
+
const baseURL: string = (import.meta as any).env.BASE_URL;
export const router = new Router({
@@ -35,7 +36,15 @@ export const router = new Router({
path: resolveRouterPath('note-wall'),
title: 'Note Wall',
render: () => html`
`
- }
+ },
+ {
+ path: resolveRouterPath('write'),
+ title: 'Write',
+ plugins: [
+ lazy(() => import('./pages/app-write/app-write.js')),
+ ],
+ render: () => html`
`
+ },
]
});
diff --git a/src/styles/shared-styles.ts b/src/styles/shared-styles.ts
index b6091b6..57edc8a 100644
--- a/src/styles/shared-styles.ts
+++ b/src/styles/shared-styles.ts
@@ -10,7 +10,439 @@ export const styles = css`
}
main {
- margin-top: 34px;
+ margin-top: 70px;
padding: 12px;
}
+
+.ProseMirror {
+ position: relative;
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ -webkit-font-variant-ligatures: none;
+ font-variant-ligatures: none;
+ padding: 1rem;
+ line-height: 1.2;
+ outline: none;
+ font-family: var(
+ --markdown-editor-typography-font-family,
+ var(--mdc-typography-font-family, Montserrat, sans-serif)
+ );
+ font-size: var(
+ --markdown-editor-typography-font-size,
+ var(--mdc-typography-subtitle1-font-size, 1rem)
+ );
+ font-weight: var(
+ --markdown-editor-typography-font-weight,
+ var(--mdc-typography-subtitle1-font-weight, 400)
+ );
+ letter-spacing: var(
+ --markdown-editor-typography-letter-spacing,
+ var(--mdc-typography-subtitle1-letter-spacing, 0.009375em)
+ );
+ }
+
+ .ProseMirror pre {
+ white-space: pre-wrap;
+ }
+
+ .ProseMirror li {
+ position: relative;
+ }
+
+ .ProseMirror p:first-child,
+ .ProseMirror h1:first-child,
+ .ProseMirror h2:first-child,
+ .ProseMirror h3:first-child,
+ .ProseMirror h4:first-child,
+ .ProseMirror h5:first-child,
+ .ProseMirror h6:first-child {
+ margin-top: 10px;
+ }
+
+ .ProseMirror a {
+ color: var(--markdown-editor-typography-anchor-color, -webkit-link);
+ text-decoration: var(--markdown-editor-typography-anchor-text-decoration);
+ }
+
+ .ProseMirror p {
+ margin-bottom: 1em;
+ }
+
+ .ProseMirror-hideselection {
+ caret-color: transparent;
+ }
+
+ .ProseMirror-hideselection *::selection,
+ .ProseMirror-hideselection *::-moz-selection {
+ background: transparent;
+ }
+
+ .ProseMirror-selectednode {
+ outline: 2px solid #8cf;
+ }
+
+ /* Make sure li selections wrap around markers */
+ li.ProseMirror-selectednode {
+ outline: none;
+ }
+
+ li.ProseMirror-selectednode:after {
+ content: '';
+ position: absolute;
+ left: -32px;
+ right: -2px;
+ top: -2px;
+ bottom: -2px;
+ border: 2px solid #8cf;
+ pointer-events: none;
+ }
+
+ .ProseMirror-textblock-dropdown {
+ min-width: 3em;
+ }
+
+ .ProseMirror-menu {
+ margin: 0 -4px;
+ line-height: 1;
+ }
+
+ .ProseMirror-tooltip .ProseMirror-menu {
+ width: -webkit-fit-content;
+ width: fit-content;
+ white-space: pre;
+ }
+
+ .ProseMirror-menuitem {
+ margin: 0.25rem 0.25rem 0.25rem 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 4px;
+ overflow: hidden;
+ }
+ .ProseMirror-menuitem:hover {
+ background-color: #f5f5f5;
+ }
+
+ .ProseMirror-menuseparator {
+ margin: 0 8px;
+ }
+
+ .ProseMirror-menu-dropdown,
+ .ProseMirror-menu-dropdown-menu {
+ font-size: 90%;
+ white-space: nowrap;
+ }
+
+ .ProseMirror-menu-dropdown {
+ vertical-align: 1px;
+ cursor: pointer;
+ position: relative;
+ padding-right: 15px;
+ }
+
+ .ProseMirror-menu-dropdown-wrap {
+ padding: 1px 0 1px 4px;
+ display: inline-block;
+ position: relative;
+ }
+
+ .ProseMirror-menu-dropdown:after {
+ content: '';
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ border-top: 4px solid currentColor;
+ opacity: 0.6;
+ position: absolute;
+ right: 4px;
+ top: calc(50% - 2px);
+ }
+
+ .ProseMirror-menu-dropdown-menu,
+ .ProseMirror-menu-submenu {
+ position: absolute;
+ background: white;
+ color: #666;
+ border: 1px solid #aaa;
+ padding: 2px;
+ }
+
+ .ProseMirror-menu-dropdown-menu {
+ z-index: 1;
+ min-width: 6em;
+ }
+
+ .ProseMirror-menu-dropdown-item {
+ cursor: pointer;
+ }
+
+ .ProseMirror-menu-dropdown-item:hover {
+ background: #f2f2f2;
+ }
+
+ .ProseMirror-menu-dropdown-item > div {
+ padding: 0.375rem 0.5rem;
+ }
+
+ .ProseMirror-menu-submenu-wrap {
+ position: relative;
+ margin-right: -4px;
+ }
+
+ .ProseMirror-menu-submenu-label:after {
+ content: '';
+ border-top: 4px solid transparent;
+ border-bottom: 4px solid transparent;
+ border-left: 4px solid currentColor;
+ opacity: 0.6;
+ position: absolute;
+ right: 4px;
+ top: calc(50% - 4px);
+ }
+
+ .ProseMirror-menu-submenu {
+ display: none;
+ min-width: 4em;
+ left: 100%;
+ top: -3px;
+ }
+
+ .ProseMirror-menu-active {
+ background: #eee;
+ border-radius: 4px;
+ color: black;
+ }
+
+ .ProseMirror-menu-submenu-wrap:hover .ProseMirror-menu-submenu,
+ .ProseMirror-menu-submenu-wrap-active .ProseMirror-menu-submenu {
+ display: block;
+ }
+
+ .ProseMirror-menubar {
+ display: flex;
+ flex-wrap: wrap;
+ position: relative;
+ background: white;
+ min-height: 1em;
+ overflow: visible;
+ z-index: 2;
+ top: 0;
+ left: 0;
+ right: 0;
+ color: #666;
+ padding: 1px 6px;
+ border-top-left-radius: inherit;
+ border-top-right-radius: inherit;
+ border-bottom: 1px solid
+ var(--markdown-editor-outline-idle-border-color, rgba(0, 0, 0, 0.38));
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ }
+
+ .ProseMirror-icon {
+ display: inline-block;
+ line-height: 0.8;
+ vertical-align: -2px;
+ /* Compensate for padding */
+ padding: 2px 8px;
+ cursor: pointer;
+ }
+
+ .ProseMirror-menu-disabled {
+ color: rgba(0,0,0,0.37);
+ background-color: rgba(0,0,0,0.12);
+ cursor: not-allowed;
+ }
+
+ .ProseMirror-menu-disabled.ProseMirror-icon {
+ cursor: not-allowed;
+ }
+
+ .ProseMirror-icon svg {
+ fill: currentColor;
+ height: 1em;
+ }
+
+ .ProseMirror-icon span {
+ vertical-align: text-top;
+ }
+
+ .ProseMirror-gapcursor {
+ display: none;
+ pointer-events: none;
+ position: absolute;
+ }
+
+ .ProseMirror-gapcursor:after {
+ content: '';
+ display: block;
+ position: absolute;
+ top: -2px;
+ width: 20px;
+ border-top: 1px solid black;
+ animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
+ }
+
+ @keyframes ProseMirror-cursor-blink {
+ to {
+ visibility: hidden;
+ }
+ }
+
+ .ProseMirror-focused .ProseMirror-gapcursor {
+ display: block;
+ }
+
+ /* Add space around the hr to make clicking it easier */
+ .ProseMirror-example-setup-style hr {
+ padding: 2px 10px;
+ border: none;
+ margin: 1em 0;
+ }
+
+ .ProseMirror-example-setup-style hr:after {
+ content: '';
+ display: block;
+ height: 1px;
+ background-color: silver;
+ line-height: 2px;
+ }
+
+ .ProseMirror ul,
+ .ProseMirror ol {
+ padding-left: 30px;
+ }
+
+ .ProseMirror blockquote {
+ padding-left: 1em;
+ border-left: 3px solid #eee;
+ margin-left: 0;
+ margin-right: 0;
+ }
+
+ .ProseMirror-example-setup-style img {
+ cursor: default;
+ }
+
+ .ProseMirror-invalid {
+ background: #ffc;
+ border: 1px solid #cc7;
+ border-radius: 4px;
+ padding: 5px 10px;
+ position: absolute;
+ min-width: 10em;
+ }
+
+ .ProseMirror h1.title.empty-node::before,
+ .ProseMirror h2.instructional-prompt.empty-node::before,
+ .ProseMirror h3.mechanical-promp.empty-node::before {
+ content: 'Enter title here...';
+ }
+
+ .ProseMirror div.passage-subtitle.empty-node:first-child::before {
+ content: 'Enter subtitle here...';
+ }
+
+ .ProseMirror div.passage-author.empty-node:first-child::before,
+ .ProseMirror div.passage-cast-title.empty-node:first-child::before,
+ .ProseMirror div.passage-act-title.empty-node:first-child::before,
+ .ProseMirror div.passage-scene-title.empty-node:first-child::before,
+ .ProseMirror div.passage-verse.empty-node:first-child::before,
+ .ProseMirror div.passage-footnotes.empty-node:first-child::before,
+ .ProseMirror div.paragraph.empty-node:first-child::before {
+ content: 'Enter text here...';
+ }
+
+ div[contenteditable]:focus h1.title.empty-node::before,
+ div[contenteditable]:focus h2.instructional-prompt.empty-node::before,
+ div[contenteditable]:focus h3.mechanical-promp.empty-node::before,
+ div[contenteditable]:focus
+ div.passage-subtitle.empty-node:first-child::before,
+ div[contenteditable]:focus div.passage-author.empty-node:first-child::before,
+ div[contenteditable]:focus
+ div.passage-cast-title.empty-node:first-child::before,
+ div[contenteditable]:focus
+ div.passage-act-title.empty-node:first-child::before,
+ div[contenteditable]:focus
+ div.passage-scene-title.empty-node:first-child::before,
+ div[contenteditable]:focus div.passage-verse.empty-node:first-child::before,
+ div[contenteditable]:focus
+ div.passage-footnotes.empty-node:first-child::before,
+ div[contenteditable]:focus div.paragraph.empty-node:first-child::before {
+ content: '';
+ }
+
+ .ProseMirror .empty-node::before {
+ position: absolute;
+ color: #aaa;
+ cursor: text;
+ font-style: italic;
+ }
+
+ #editor, .editor {
+ background: white;
+ color: black;
+ background-clip: padding-box;
+ padding: 5px 0;
+ }
+
+ #editor[disabled] .ProseMirror-menubar {
+ display: none!important;
+ }
+
+ drop-down-editor rich-text .inline-component-button,
+ expand-collapse rich-text .inline-component-button,
+ flip-reveal rich-text .inline-component-button,
+ hint-list rich-text .inline-component-button,
+ option-list rich-text .inline-component-button,
+ plankton-passage rich-text .inline-component-button {
+ pointer-events: none;
+ color: lightgray;
+ }
+
+ #ProseMirror-icon-collection path {
+ fill-rule: evenodd;
+ }
+ #welcomeBar {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ }
+
+ #welcomeCard,
+ #infoCard {
+ padding: 18px;
+ padding-top: 0px;
+ }
+
+ sl-card::part(footer) {
+ display: flex;
+ justify-content: flex-end;
+ }
+
+ @media(min-width: 750px) {
+ sl-card {
+ width: 70vw;
+ }
+ }
+
+ .boring {
+ background: grey;
+ }
+
+ .plus {
+ position: absolute;
+
+ padding: 8px;
+ background-color: #4CAF50;
+ color: red;
+ border: none;
+ cursor: pointer;
+}
+
+.plus:hover {
+ background-color: #45a049;
+}
+
`;
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 08abbc4..4a67f20 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -23,6 +23,6 @@
"vite/client"
]
},
- "include": ["src/**/*.ts", "router.d.ts"],
+ "include": ["src/**/*.ts", "router.d.ts", "src/pages/app-write"],
"exclude": ["node_modules"]
}