diff --git a/common/spaces/disk_space_primitives.ts b/common/spaces/disk_space_primitives.ts index a7618aae..34171b89 100644 --- a/common/spaces/disk_space_primitives.ts +++ b/common/spaces/disk_space_primitives.ts @@ -207,11 +207,11 @@ export class DiskSpacePrimitives implements SpacePrimitives { this.fileListCache = allFiles; this.fileListCacheTime = performance.now(); - console.info( - "Updated file list cache in background:", - allFiles.length, - "files found", - ); + // console.info( + // "Updated file list cache in background:", + // allFiles.length, + // "files found", + // ); }).catch((error) => { if (abortController.signal.aborted) return; diff --git a/plug-api/types.ts b/plug-api/types.ts index 0e687c5d..33e15b98 100644 --- a/plug-api/types.ts +++ b/plug-api/types.ts @@ -29,6 +29,7 @@ export type PageDecoration = { prefix?: string; cssClasses?: string[]; hide?: boolean; + renderWidgets?: boolean; // Defaults to true }; export type AttachmentMeta = ObjectValue< diff --git a/web/cm_plugins/block.ts b/web/cm_plugins/block.ts index d1503351..dad87b19 100644 --- a/web/cm_plugins/block.ts +++ b/web/cm_plugins/block.ts @@ -25,13 +25,6 @@ export function cleanBlockPlugin() { }).range(node.from), ); } - - if ( - node.name === "Image" && - !isCursorInRange(state, [node.from, node.to]) - ) { - widgets.push(invisibleDecoration.range(node.from, node.to)); - } }, }); return Decoration.set(widgets, true); diff --git a/web/cm_plugins/fenced_code.ts b/web/cm_plugins/fenced_code.ts index 3891c73b..1cc4a84e 100644 --- a/web/cm_plugins/fenced_code.ts +++ b/web/cm_plugins/fenced_code.ts @@ -6,12 +6,12 @@ import { decoratorStateField, invisibleDecoration, isCursorInRange, + shouldRenderWidgets, } from "./util.ts"; import { MarkdownWidget } from "./markdown_widget.ts"; import { IFrameWidget } from "./iframe_widget.ts"; -import { isTemplate } from "../../lib/cheap_yaml.ts"; -export function fencedCodePlugin(editor: Client) { +export function fencedCodePlugin(client: Client) { return decoratorStateField((state: EditorState) => { const widgets: any[] = []; syntaxTree(state).iterate({ @@ -23,15 +23,15 @@ export function fencedCodePlugin(editor: Client) { } const text = state.sliceDoc(from, to); const [_, lang] = text.match(/^(?:```+|~~~+)(\w+)?/)!; - const codeWidgetCallback = editor.clientSystem.codeWidgetHook + const codeWidgetCallback = client.clientSystem.codeWidgetHook .codeWidgetCallbacks .get(lang); - const renderMode = editor.clientSystem.codeWidgetHook.codeWidgetModes + const renderMode = client.clientSystem.codeWidgetHook.codeWidgetModes .get( lang, ); // Only custom render when we have a custom renderer, and the current page is not a template - if (codeWidgetCallback && !isTemplate(state.sliceDoc(0, from))) { + if (codeWidgetCallback && shouldRenderWidgets(client)) { // We got a custom renderer! const lineStrings = text.split("\n"); @@ -81,8 +81,8 @@ export function fencedCodePlugin(editor: Client) { const widget = renderMode === "markdown" ? new MarkdownWidget( from + lineStrings[0].length + 1, - editor, - `widget:${editor.currentPage}:${bodyText}`, + client, + `widget:${client.currentPage}:${bodyText}`, bodyText, codeWidgetCallback, "sb-markdown-widget", @@ -90,7 +90,7 @@ export function fencedCodePlugin(editor: Client) { : new IFrameWidget( from + lineStrings[0].length + 1, to - lineStrings[lineStrings.length - 1].length - 1, - editor, + client, lineStrings.slice(1, lineStrings.length - 1).join("\n"), codeWidgetCallback, ); diff --git a/web/cm_plugins/inline_content.ts b/web/cm_plugins/inline_content.ts index cb8fcb75..c6e66de6 100644 --- a/web/cm_plugins/inline_content.ts +++ b/web/cm_plugins/inline_content.ts @@ -2,7 +2,12 @@ import type { EditorState, Range } from "@codemirror/state"; import { syntaxTree } from "@codemirror/language"; import { Decoration, WidgetType } from "@codemirror/view"; import { MarkdownWidget } from "./markdown_widget.ts"; -import { decoratorStateField } from "./util.ts"; +import { + decoratorStateField, + invisibleDecoration, + isCursorInRange, + shouldRenderWidgets, +} from "./util.ts"; import type { Client } from "../client.ts"; import { isFederationPath, @@ -152,6 +157,9 @@ function parseAlias( export function inlineContentPlugin(client: Client) { return decoratorStateField((state: EditorState) => { const widgets: Range[] = []; + if (!shouldRenderWidgets(client)) { + return Decoration.set([]); + } syntaxTree(state).iterate({ enter: (node) => { @@ -232,8 +240,12 @@ export function inlineContentPlugin(client: Client) { client, ), block: true, - }).range(node.to + 1), + }).range(node.to), ); + + if (!isCursorInRange(state, [node.from, node.to])) { + widgets.push(invisibleDecoration.range(node.from, node.to)); + } }, }); diff --git a/web/cm_plugins/table.ts b/web/cm_plugins/table.ts index e5bbd857..3d619331 100644 --- a/web/cm_plugins/table.ts +++ b/web/cm_plugins/table.ts @@ -5,6 +5,7 @@ import { decoratorStateField, invisibleDecoration, isCursorInRange, + shouldRenderWidgets, } from "./util.ts"; import { renderMarkdownToHtml } from "../../plugs/markdown/markdown_render.ts"; @@ -89,6 +90,8 @@ export function tablePlugin(editor: Client) { if (name !== "Table") return; if (isCursorInRange(state, [from, to])) return; + if (!shouldRenderWidgets(editor)) return; + const tableText = state.sliceDoc(from, to); const lineStrings = tableText.split("\n"); diff --git a/web/cm_plugins/util.ts b/web/cm_plugins/util.ts index 1cf107f9..4b567c5d 100644 --- a/web/cm_plugins/util.ts +++ b/web/cm_plugins/util.ts @@ -8,6 +8,8 @@ import { } from "@codemirror/state"; import type { DecorationSet } from "@codemirror/view"; import { Decoration, EditorView, WidgetType } from "@codemirror/view"; +import type { Client } from "../client.ts"; + type LinkOptions = { text: string; href?: string; @@ -16,6 +18,7 @@ type LinkOptions = { from: number; callback: (e: MouseEvent) => void; }; + export class LinkWidget extends WidgetType { constructor( readonly options: LinkOptions, @@ -172,3 +175,9 @@ export function isCursorInRange(state: EditorState, range: [number, number]) { * Decoration to simply hide anything. */ export const invisibleDecoration = Decoration.replace({}); + +export function shouldRenderWidgets(client: Client) { + const currentPageMeta = client.ui.viewState.currentPageMeta; + return !currentPageMeta?.tags?.includes("template") && + currentPageMeta?.pageDecoration?.renderWidgets !== false; +} diff --git a/website/Page Decorations.md b/website/Page Decorations.md index 0d1ccfc6..964ee52b 100644 --- a/website/Page Decorations.md +++ b/website/Page Decorations.md @@ -15,6 +15,7 @@ Page decorations allow you to “decorate” pages in various fun ways. * `cssClasses`: (list of strings) Attaches one or more CSS classes the page's `` tag, wiki links, auto complete items and [[Page Picker]] entries for more advanced styling through a [[Space Style]] (see [[#Use case: pimp my page]] for an example). * `hide` When this is set to `true`, the page will not be shown in [[Page Picker]], [[Meta Picker]], or suggested for completion of [[Links]]. It will otherwise behave as normal - will be [[Plugs/Index|indexed]] and found in [[Live Queries]]. The page can be opened through [[All Pages Picker]], or linked normally when the full name is typed out without completion. * `disableTOC` (not technically built-in, but a feature of the [[^Library/Core/Widget/Table of Contents]] widget): disable the [[Table of Contents]] for this particular page. +* `renderWidgets`: when set to `false` disables the [[Live Preview]] rendering of elements like [[Transclusions]], [[Live Queries]], [[Live Templates]] for this page. There are two ways to apply decorations: