diff --git a/common/languages.ts b/common/languages.ts index 018e74b3..c1c5c7c8 100644 --- a/common/languages.ts +++ b/common/languages.ts @@ -1,9 +1,14 @@ import { LRLanguage } from "@codemirror/language"; import { cLanguage, + cmakeLanguage, cppLanguage, csharpLanguage, + cssLanguage, dartLanguage, + diffLanguage, + dockerfileLanguage, + goLanguage, htmlLanguage, javaLanguage, javascriptLanguage, @@ -12,7 +17,9 @@ import { Language, objectiveCLanguage, objectiveCppLanguage, + perlLanguage, postgresqlLanguage, + powerShellLanguage, protobufLanguage, pythonLanguage, rustLanguage, @@ -20,19 +27,13 @@ import { shellLanguage, sqlLanguage, StreamLanguage, + tclLanguage, tomlLanguage, typescriptLanguage, - xmlLanguage, - yamlLanguage, - goLanguage, - diffLanguage, - powerShellLanguage, - perlLanguage, - tclLanguage, verilogLanguage, vhdlLanguage, - dockerfileLanguage, - cmakeLanguage, + xmlLanguage, + yamlLanguage, } from "./deps.ts"; import { extendedMarkdownLanguage, @@ -58,7 +59,8 @@ export const builtinLanguages: Record = { "postgres": StreamLanguage.define(postgresqlLanguage), "rust": StreamLanguage.define(rustLanguage), "rs": StreamLanguage.define(rustLanguage), - "css": StreamLanguage.define(sqlLanguage), + "css": StreamLanguage.define(cssLanguage), + "space-style": StreamLanguage.define(cssLanguage), "html": htmlLanguage, "python": StreamLanguage.define(pythonLanguage), "py": StreamLanguage.define(pythonLanguage), diff --git a/common/syscalls/system.ts b/common/syscalls/system.ts index 55b30f93..29d2d117 100644 --- a/common/syscalls/system.ts +++ b/common/syscalls/system.ts @@ -94,6 +94,12 @@ export function systemSyscalls( } } }, + "system.loadSpaceStyles": async () => { + if (!client) { + throw new Error("Not supported on server"); + } + await client.loadCustomStyles(); + }, "system.invokeSpaceFunction": (_ctx, name: string, ...args: any[]) => { return commonSystem.invokeSpaceFunction(name, args); }, diff --git a/plugs/index/index.plug.yaml b/plugs/index/index.plug.yaml index 79fea678..daa0bf09 100644 --- a/plugs/index/index.plug.yaml +++ b/plugs/index/index.plug.yaml @@ -132,6 +132,12 @@ functions: events: - page:index + # Style + indexSpaceStyle: + path: style.ts:indexSpaceStyle + events: + - page:index + # Hashtags indexTags: path: tags.ts:indexTags diff --git a/plugs/index/style.ts b/plugs/index/style.ts new file mode 100644 index 00000000..00e0cac1 --- /dev/null +++ b/plugs/index/style.ts @@ -0,0 +1,62 @@ +import { IndexTreeEvent } from "../../plug-api/types.ts"; +import { collectNodesOfType, findNodeOfType } from "../../plug-api/lib/tree.ts"; +import { ObjectValue } from "../../plug-api/types.ts"; +import { indexObjects } from "./api.ts"; +import { readSetting } from "$sb/lib/settings_page.ts"; +import { cleanPageRef } from "$sb/lib/resolve.ts"; + +export type StyleObject = ObjectValue<{ + style: string; + origin: string; +}>; + +export async function indexSpaceStyle({ name, tree }: IndexTreeEvent) { + const allStyles: StyleObject[] = []; + + // Also collect CSS from custom styles in settings + let customStylePages = await readSetting("customStyles", []); + if (!Array.isArray(customStylePages)) { + customStylePages = [customStylePages]; + } + customStylePages = customStylePages.map((page: string) => cleanPageRef(page)); + + collectNodesOfType(tree, "FencedCode").map((t) => { + const codeInfoNode = findNodeOfType(t, "CodeInfo"); + if (!codeInfoNode) { + return; + } + + const fenceType = codeInfoNode.children![0].text!; + if (fenceType !== "space-style") { + if ( + !customStylePages.includes(name) || fenceType !== "css" + ) { + return; + } + } + + const codeTextNode = findNodeOfType(t, "CodeText"); + if (!codeTextNode) { + // Honestly, this shouldn't happen + return; + } + const codeText = codeTextNode.children![0].text!; + let codeOrigin = ""; + if (customStylePages.includes(name)) { + codeOrigin = "settings"; + } else if (name.startsWith("Library/")) { + codeOrigin = "library"; + } else { + codeOrigin = "user"; + } + + allStyles.push({ + ref: `${name}@${t.from!}`, + tag: "space-style", + style: codeText, + origin: codeOrigin, + }); + }); + + await indexObjects(name, allStyles); +} diff --git a/web/client.ts b/web/client.ts index 748d9ab7..b1efd88e 100644 --- a/web/client.ts +++ b/web/client.ts @@ -25,6 +25,7 @@ import type { CompleteEvent, SlashCompletions, } from "../plug-api/types.ts"; +import { StyleObject } from "../plugs/index/style.ts"; import { throttle } from "$lib/async.ts"; import { PlugSpacePrimitives } from "$common/spaces/plug_space_primitives.ts"; import { EventedSpacePrimitives } from "$common/spaces/evented_space_primitives.ts"; @@ -1095,36 +1096,32 @@ export class Client { } async loadCustomStyles() { - if (this.settings.customStyles) { - const accumulatedCSS: string[] = []; - let customStylePages = this.settings.customStyles; - if (!Array.isArray(customStylePages)) { - customStylePages = [customStylePages]; - } - for (const customStylesPage of customStylePages) { - try { - const { text: stylesText } = await this.space.readPage( - cleanPageRef(customStylesPage), - ); - // Analogous to yamlSettingsRegex in settings.ts - const cssBlockRegex = /^(```+|~~~+)css\r?\n([\S\s]+)\1/m; - const match = cssBlockRegex.exec(stylesText); - if (!match) { - return; - } - accumulatedCSS.push(match[2]); - } catch (e: any) { - console.error("Failed to load custom styles", e); - } - } - const customStylesContent = accumulatedCSS.join("\n\n"); - this.ui.viewDispatch({ - type: "set-ui-option", - key: "customStyles", - value: customStylesContent, - }); - document.getElementById("custom-styles")!.innerHTML = customStylesContent; + const spaceStyles = await this.clientSystem.queryObjects( + "space-style", + {}, + ); + if (!spaceStyles) { + return; } + + // Sort stylesheets (last declared styles take precedence) + // Order is 1: Imported styles, 2: Other styles, 3: customStyles from Settings + const sortOrder = ["library", "user", "settings"]; + spaceStyles.sort((a, b) => + sortOrder.indexOf(a.origin) - sortOrder.indexOf(b.origin) + ); + + const accumulatedCSS: string[] = []; + for (const s of spaceStyles) { + accumulatedCSS.push(s.style); + } + const customStylesContent = accumulatedCSS.join("\n\n"); + this.ui.viewDispatch({ + type: "set-ui-option", + key: "customStyles", + value: customStylesContent, + }); + document.getElementById("custom-styles")!.innerHTML = customStylesContent; } async runCommandByName(name: string, args?: any[]) { diff --git a/web/syscalls/editor.ts b/web/syscalls/editor.ts index 1b0c2c8a..413bb662 100644 --- a/web/syscalls/editor.ts +++ b/web/syscalls/editor.ts @@ -66,6 +66,10 @@ export function editorSyscalls(client: Client): SysCallMapping { "system.loadSpaceScripts", [], ); + await client.clientSystem.system.localSyscall( + "system.loadSpaceStyles", + [], + ); }, "editor.openUrl": (_ctx, url: string, existingWindow = false) => { if (!existingWindow) { diff --git a/website/Blocks.md b/website/Blocks.md index 0aacbbd1..e2d334d7 100644 --- a/website/Blocks.md +++ b/website/Blocks.md @@ -12,5 +12,7 @@ These are the block types that ship with SilverBullet, but [[Plugs]] can define * `query`: [[Live Queries]] * `toc`: [[Table of Contents]] * `embed`: [[Live Embeds]] +* `space-script`: [[Space Script]] +* `space-style`: [[Space Style]] The fenced code block syntax is also used to get [[Markdown/Syntax Highlighting]] for numerous programming languages. \ No newline at end of file diff --git a/website/Markdown/Admonitions.md b/website/Markdown/Admonitions.md index c8bd2ead..f9572e67 100644 --- a/website/Markdown/Admonitions.md +++ b/website/Markdown/Admonitions.md @@ -6,9 +6,9 @@ Silverbullet supports [admonitions](https://github.com/community/community/discu > **warning** This is a > warning admonition -Custom admonitions can be added in [[STYLES]] using the following format: +Custom admonitions can be added in a [[Space Style]] using the following format: -```css +```space-style // Replace the keyword with a word or phrase of your choice .sb-admonition[admonition="keyword"] { // The icon can be a link or an embedded image like shown here diff --git a/website/SETTINGS.md b/website/SETTINGS.md index a7526052..be51e12c 100644 --- a/website/SETTINGS.md +++ b/website/SETTINGS.md @@ -1,12 +1,9 @@ -This page contains settings for configuring SilverBullet and its Plugs. Changing any of these will go into effect immediately in most cases except `indexPage` and `customStyles`, which require a reload. +This page contains settings for configuring SilverBullet and its Plugs. Changing any of these will go into effect immediately in most cases except `indexPage` which requires a reload. ```yaml # Initial page to load when launching SB, can contain template variables indexPage: "[[SilverBullet]]" -# Load custom CSS styles from the following page, can also be an array -customStyles: "[[STYLES]]" - # Hide the sync button hideSyncButton: false diff --git a/website/STYLES.md b/website/STYLES.md deleted file mode 100644 index 812c8748..00000000 --- a/website/STYLES.md +++ /dev/null @@ -1,49 +0,0 @@ -In [[SETTINGS]] you can define a `customStyles` setting that links to a page like this. SilverBullet will load the specified page, find a CSS code block inside that page and load it upon boot (an example can be found below). - -This can be used to achieve various things, such as overriding the default editor font or setting wider page widths. What CSS styles you can override is not very well documented, you’ll have to reverse-engineer things a bit for now, unfortunately. - -```css -#sb-root { - /* Uncomment the next line to set the editor font to Courier */ - /* --editor-font: "Courier" !important; */ - /* Uncomment the next line to set the editor width to 1400px */ - /* --editor-width: 1400px !important; */ -} - -/* Choose another header color */ -#sb-top { - /* background-color: #ffe54a !important; */ -} - -/* You can even change the appearance of buttons */ -button { - /* align-items: center; - background-color: #fff; - border-radius: 6px; - box-shadow: transparent 0 0 0 3px,rgba(18, 18, 18, .1) 0 6px 20px; - box-sizing: border-box; - color: #121212; - cursor: pointer; - display: inline-flex; - flex: 1 1 auto; - font-family: Inter,sans-serif; - font-size: 0.6rem; - justify-content: center; - line-height: 1; - margin: 0.2rem; - outline: none; - padding: 0.3rem 0.4rem; - text-align: center; - text-decoration: none; - transition: box-shadow .2s,-webkit-box-shadow .2s; - white-space: nowrap; - border: 0; - user-select: none; - -webkit-user-select: none; - touch-action: manipulation; */ -} - -button:hover { - /* box-shadow: #121212 0 0 0 3px, transparent 0 0 0 0; */ -} -``` diff --git a/website/Space Style.md b/website/Space Style.md new file mode 100644 index 00000000..cd228852 --- /dev/null +++ b/website/Space Style.md @@ -0,0 +1,70 @@ +Space Style is [[Space Script]]‘s stylish sibling. It enables you to add your own styling to SilverBullet with `space-style` [[Blocks]]. + +This can be used to achieve various things, such as overriding the default editor font or setting wider page widths. It is also possible to develop custom themes this way. + +To apply the updated styles, either reload the client or run the {[System: Reload]} command. + +Many styles can be set with [variables](https://github.com/silverbulletmd/silverbullet/blob/main/web/styles/theme.scss) but not everything is covered. You’ll have to reverse-engineer those parts, unfortunately. + +# Examples +All the actual CSS in these examples is commented out as to not affect this very website. +```space-style +html { + /* Changes to the default theme */ + /* Such as the accent color */ + /*--ui-accent-color: #464cfc;*/ +} + +html[data-theme="dark"] { + /* Changes to the dark theme */ + /*--ui-accent-color: #464cfc;*/ +} + +html { + /* Uncomment the next line to set the editor font to Courier */ + /* --editor-font: "Courier" !important; */ + /* Uncomment the next line to set the editor width to 1400px */ + /* --editor-width: 1400px !important; */ +} + +/* Choose another header color */ +html { + /*--top-background-color: #eee;*/ +} +/* Or modify the element directly */ +#sb-top { + /*background-color: #eee !important;*/ +} + +/* You can even change the appearance of buttons */ +button { + /* align-items: center; + background-color: #fff; + border-radius: 6px; + box-shadow: transparent 0 0 0 3px,rgba(18, 18, 18, .1) 0 6px 20px; + box-sizing: border-box; + color: #121212; + cursor: pointer; + display: inline-flex; + flex: 1 1 auto; + font-family: Inter,sans-serif; + font-size: 0.6rem; + justify-content: center; + line-height: 1; + margin: 0.2rem; + outline: none; + padding: 0.3rem 0.4rem; + text-align: center; + text-decoration: none; + transition: box-shadow .2s,-webkit-box-shadow .2s; + white-space: nowrap; + border: 0; + user-select: none; + -webkit-user-select: none; + touch-action: manipulation; */ +} + +button:hover { + /* box-shadow: #121212 0 0 0 3px, transparent 0 0 0 0; */ +} +``` diff --git a/website/Theme/Devise-Dark.md b/website/Theme/Devise-Dark.md index ba2f9f11..eb9a22d8 100644 --- a/website/Theme/Devise-Dark.md +++ b/website/Theme/Devise-Dark.md @@ -1,6 +1,6 @@ Dark theme, originally [shared here](https://discord.com/channels/1028543811191836782/1028543811984568373/1141715540755361833). Make sure you switch SilverBullet to {[Editor: Toggle Dark Mode|dark mode]} to use. -```css +```space-style html[data-theme="dark"], html[data-theme="dark"]{ --root-background-color: rgba(30,33,38,255); --root-color: rgba(255,255,255,0.6);