diff --git a/plugs/core/core.plug.yaml b/plugs/core/core.plug.yaml index e1a5587c..55eafb2d 100644 --- a/plugs/core/core.plug.yaml +++ b/plugs/core/core.plug.yaml @@ -210,7 +210,7 @@ functions: path: ./page.ts:newPageCommand command: name: "Page: New" - key: "Alt-n" + key: "Alt-Shift-n" insertHRTemplate: redirect: insertTemplateText diff --git a/web/cm_plugins/image.ts b/web/cm_plugins/block.ts similarity index 60% rename from web/cm_plugins/image.ts rename to web/cm_plugins/block.ts index 58e84ef1..27dd48e1 100644 --- a/web/cm_plugins/image.ts +++ b/web/cm_plugins/block.ts @@ -1,7 +1,3 @@ -// Forked from https://codeberg.org/retronav/ixora -// Original author: Pranav Karawale -// License: Apache License 2.0. - import { Decoration, DecorationSet, @@ -19,12 +15,6 @@ function hideNodes(view: EditorView) { const widgets: any[] = []; iterateTreeInVisibleRanges(view, { enter(node) { - if ( - node.name === "Image" && - !isCursorInRange(view.state, [node.from, node.to]) - ) { - widgets.push(invisibleDecoration.range(node.from, node.to)); - } if ( node.name === "HorizontalRule" && !isCursorInRange(view.state, [node.from, node.to]) @@ -36,12 +26,37 @@ function hideNodes(view: EditorView) { }).range(node.from), ); } + if ( + node.name === "FrontMatterMarker" + ) { + const parent = node.node.parent!; + if (!isCursorInRange(view.state, [parent.from, parent.to])) { + widgets.push( + Decoration.line({ + class: "sb-line-frontmatter-outside", + }).range(node.from), + ); + } + } + + if ( + node.name === "CodeMark" + ) { + const parent = node.node.parent!; + if (!isCursorInRange(view.state, [parent.from, parent.to])) { + widgets.push( + Decoration.line({ + class: "sb-line-code-outside", + }).range(node.from), + ); + } + } }, }); return Decoration.set(widgets, true); } -export const hideImageNodePlugin = ViewPlugin.fromClass( +export const cleanBlockPlugin = ViewPlugin.fromClass( class { decorations: DecorationSet; diff --git a/web/cm_plugins/clean.ts b/web/cm_plugins/clean.ts index 06340eb8..c46bc2b7 100644 --- a/web/cm_plugins/clean.ts +++ b/web/cm_plugins/clean.ts @@ -4,7 +4,7 @@ import { Editor } from "../editor.tsx"; import { blockquotePlugin } from "./block_quote.ts"; import { directivePlugin } from "./directive.ts"; import { hideHeaderMarkPlugin, hideMarks } from "./hide_mark.ts"; -import { hideImageNodePlugin } from "./image.ts"; +import { cleanBlockPlugin } from "./block.ts"; import { goToLinkPlugin } from "./link.ts"; import { listBulletPlugin } from "./list.ts"; import { tablePlugin } from "./table.ts"; @@ -18,7 +18,7 @@ export function cleanModePlugins(editor: Editor) { blockquotePlugin, hideMarks(), hideHeaderMarkPlugin, - hideImageNodePlugin, + cleanBlockPlugin, taskListPlugin({ // TODO: Move this logic elsewhere? onCheckboxClick: (pos) => { diff --git a/web/cm_plugins/inline_image.ts b/web/cm_plugins/inline_image.ts index a3b8854d..87f50d1e 100644 --- a/web/cm_plugins/inline_image.ts +++ b/web/cm_plugins/inline_image.ts @@ -8,6 +8,7 @@ import { ViewUpdate, WidgetType, } from "../deps.ts"; +import { invisibleDecoration, isCursorInRange } from "./util.ts"; class InlineImageWidget extends WidgetType { constructor(readonly url: string, readonly title: string) { @@ -47,6 +48,12 @@ const inlineImages = (view: EditorView) => { return; } + if ( + !isCursorInRange(view.state, [node.from, node.to]) + ) { + widgets.push(invisibleDecoration.range(node.from, node.to)); + } + const imageRexexResult = imageRegex.exec( view.state.sliceDoc(node.from, node.to), ); @@ -56,10 +63,11 @@ const inlineImages = (view: EditorView) => { const url = imageRexexResult.groups.url; const title = imageRexexResult.groups.title; - const deco = Decoration.widget({ - widget: new InlineImageWidget(url, title), - }); - widgets.push(deco.range(node.to)); + widgets.push( + Decoration.widget({ + widget: new InlineImageWidget(url, title), + }).range(node.to), + ); }, }); } diff --git a/web/cm_plugins/table.ts b/web/cm_plugins/table.ts index 00c07641..12aa5c60 100644 --- a/web/cm_plugins/table.ts +++ b/web/cm_plugins/table.ts @@ -2,7 +2,6 @@ import { Decoration, DecorationSet, EditorView, - syntaxTree, ViewPlugin, ViewUpdate, WidgetType, @@ -30,15 +29,22 @@ class TableViewWidget extends WidgetType { toDOM(): HTMLElement { const dom = document.createElement("span"); dom.classList.add("sb-table-widget"); - dom.addEventListener("click", () => { + dom.addEventListener("click", (e) => { + // Pulling data-pos to put the cursor in the right place, falling back + // to the start of the table. + const dataAttributes = (e.target as any).dataset; this.editorView.dispatch({ selection: { - anchor: this.pos, + anchor: dataAttributes.pos ? +dataAttributes.pos : this.pos, }, }); }); - dom.innerHTML = renderMarkdownToHtml(this.t); + dom.innerHTML = renderMarkdownToHtml(this.t, { + // Annotate every element with its position so we can use it to put + // the cursor there when the user clicks on the table. + annotationPositions: true, + }); return dom; } } diff --git a/web/styles/editor.scss b/web/styles/editor.scss index bd4b4552..690c1a5d 100644 --- a/web/styles/editor.scss +++ b/web/styles/editor.scss @@ -155,6 +155,10 @@ opacity: 0.4; } + .sb-line-frontmatter-outside, .sb-line-code-outside { + display: none; + } + .sb-blockquote-outside { text-indent: -1ch; min-height: 1em; diff --git a/web/styles/theme.scss b/web/styles/theme.scss index 08d7f25e..33a0d0a2 100644 --- a/web/styles/theme.scss +++ b/web/styles/theme.scss @@ -37,11 +37,11 @@ background-color: rgb(255, 84, 84); } -.sb-saved { +.sb-saved input { color: #111; } -.sb-unsaved { +.sb-unsaved input { color: #5e5e5e; }