got a basic working text area

This commit is contained in:
miggymofongo 2025-01-04 21:30:00 -04:00
parent 2dfbc08788
commit d914db1c1d
9 changed files with 888 additions and 58 deletions

144
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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
},
};
},
});

View file

@ -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 {
<ul>
<li>
<a href="https://www.npmjs.com/package/nostr-tools?activeTab=dependencies">Nostr Tools</a>
I am using <a href="https://www.npmjs.com/package/nostr-tools?activeTab=dependencies">Nostr Tools</a> to implement an extension
sign in and pull notes from my personal relay.
</li>
<li>

View file

@ -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
<div id="welcomeBar">
<sl-card id="welcomeCard">
<div slot="header">
<h2>Find me on any nostr client by searching ${this.nostrAddy || 'Guest'}</h2>
<h2>Welcome to ${this.nostrAddy || 'Guest'}'s Profile</h2>
</div>
<p>
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.
</p>
<div class="profile-picture-container">
<img class="profile-pic" src="${this.profilePic || '/assets/img/default_pfp.png'}" alt="Profile Picture" width="200px" height="200px">
@ -237,7 +217,9 @@ Profile Picture Container
<sl-button href="${resolveRouterPath('about')}" variant="primary">Navigate to About</sl-button>
<sl-button href="${resolveRouterPath('note-wall')}" variant="primary">Navigate to Note Wall</sl-button>
</div>
<sl-button href="${resolveRouterPath('write')}" variant="primary">Navigate to Note Compose</sl-button>
</div>
</main>
`;
}

View file

@ -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`
<app-header ?enableBack="${true}"></app-header>
<main>
<div id="welcomeBar">
<sl-card id="welcomeCard">
<div slot="header">
<h2>Editor Demo</h2>
</div>
<p>
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! </p>
<p>
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.
</p>
<h3>Basic Intructions</h3>
<p>click inside the white text area to place your curson there.
You can type and press enter to go to the next line down.
<br>
Click and hold down to select a piece of text, then CTRL+X to cut and
CTRL+V to paste.</p>
</sl-card>
<sl-card>
<div id="editor" class="ProseMirror"></div>
</sl-card>
</main>
`}
}

View file

@ -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`<note-wall></note-wall>`
}
},
{
path: resolveRouterPath('write'),
title: 'Write',
plugins: [
lazy(() => import('./pages/app-write/app-write.js')),
],
render: () => html`<app-write><app-write>`
},
]
});

View file

@ -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;
}
`;

View file

@ -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"]
}