diff --git a/common/markdown_parser/parser.ts b/common/markdown_parser/parser.ts index ce9f5c9c..6d262a06 100644 --- a/common/markdown_parser/parser.ts +++ b/common/markdown_parser/parser.ts @@ -584,15 +584,13 @@ const NakedURL = regexParser( }, ); -const Hashtag = regexParser( - { - firstCharCode: 35, // # - regex: new RegExp(`^${tagRegex.source}`), - nodeType: "Hashtag", - className: "sb-hashtag", - tag: ct.HashtagTag, - }, -); +const Hashtag = regexParser({ + firstCharCode: 35, // # + regex: new RegExp(`^${tagRegex.source}`), + nodeType: "Hashtag", + className: "sb-hashtag-text", + tag: ct.HashtagTag, +}); const TaskDeadline = regexParser({ firstCharCode: 55357, // 📅 diff --git a/plugs/markdown/markdown_render.ts b/plugs/markdown/markdown_render.ts index 8a85a464..5b8b822e 100644 --- a/plugs/markdown/markdown_render.ts +++ b/plugs/markdown/markdown_render.ts @@ -14,6 +14,7 @@ import { import { Fragment, renderHtml, type Tag } from "./html_render.ts"; import { isLocalPath } from "@silverbulletmd/silverbullet/lib/resolve"; import type { PageMeta } from "@silverbulletmd/silverbullet/types"; +import * as TagConstants from "../../plugs/index/constants.ts"; export type MarkdownRenderOptions = { failOnUnknown?: true; @@ -335,15 +336,18 @@ function render( body: url, }; } - case "Hashtag": + case "Hashtag": { + const tagText: string = t.children![0].text!; return { - name: "span", + name: "a", attrs: { - class: "hashtag", + class: "hashtag sb-hashtag", + "data-tag-name": tagText.replace("#", ""), + href: `/${TagConstants.tagPrefix}${tagText.replace("#", "")}`, }, - body: t.children![0].text!, + body: tagText, }; - + } case "Task": { let externalTaskRef = ""; collectNodesOfType(t, "WikiLinkPage").forEach((wikilink) => { diff --git a/web/cm_plugins/clean.ts b/web/cm_plugins/clean.ts index 88b7305a..b134615a 100644 --- a/web/cm_plugins/clean.ts +++ b/web/cm_plugins/clean.ts @@ -15,6 +15,7 @@ import { fencedCodePlugin } from "./fenced_code.ts"; import { frontmatterPlugin } from "./frontmatter.ts"; import { cleanEscapePlugin } from "./escapes.ts"; import { luaDirectivePlugin } from "./lua_directive.ts"; +import { hashtagPlugin } from "./hashtag.ts"; export function cleanModePlugins(client: Client) { return [ @@ -46,5 +47,6 @@ export function cleanModePlugins(client: Client) { cleanCommandLinkPlugin(client), cleanEscapePlugin(), luaDirectivePlugin(client), + hashtagPlugin(), ] as Extension[]; } diff --git a/web/cm_plugins/hashtag.ts b/web/cm_plugins/hashtag.ts new file mode 100644 index 00000000..f9280487 --- /dev/null +++ b/web/cm_plugins/hashtag.ts @@ -0,0 +1,43 @@ +import { syntaxTree } from "@codemirror/language"; +import { Decoration } from "@codemirror/view"; +import { decoratorStateField } from "./util.ts"; +import * as Constants from "../../plugs/index/constants.ts"; +import { extractHashtag } from "../../plug-api/lib/tags.ts"; + +export function hashtagPlugin() { + return decoratorStateField((state) => { + const widgets: any[] = []; + + syntaxTree(state).iterate({ + enter: ({ type, from, to }) => { + if (type.name !== "Hashtag") { + return; + } + + const tag = state.sliceDoc(from, to); + + if (tag.length === 1) { + // Invalid Hashtag, a length of 1 means its just # + return; + } + + const tagName = extractHashtag(tag); + + // Wrap the tag in html anchor element + widgets.push( + Decoration.mark({ + tagName: "a", + class: "sb-hashtag", + attributes: { + href: `/${Constants.tagPrefix}${tagName}`, + rel: "tag", + "data-tag-name": tagName, + }, + }).range(from, to), + ); + }, + }); + + return Decoration.set(widgets, true); + }); +} diff --git a/web/cm_plugins/hide_mark.ts b/web/cm_plugins/hide_mark.ts index 0cceb714..ed11ba79 100644 --- a/web/cm_plugins/hide_mark.ts +++ b/web/cm_plugins/hide_mark.ts @@ -97,7 +97,7 @@ export function hideHeaderMarkPlugin() { widgets.push( Decoration.mark({ tagName: "span", - class: "sb-hashtag", + class: "sb-hashtag-text", }).range(from, from + 1), ); diff --git a/web/style.ts b/web/style.ts index c59505e9..865752e5 100644 --- a/web/style.ts +++ b/web/style.ts @@ -49,7 +49,7 @@ export default function highlightStyles() { { tag: t.processingInstruction, class: "sb-meta" }, { tag: t.punctuation, class: "sb-punctuation" }, { tag: ct.HorizontalRuleTag, class: "sb-hr" }, - { tag: ct.HashtagTag, class: "sb-hashtag" }, + { tag: ct.HashtagTag, class: "sb-hashtag-text" }, { tag: ct.NakedURLTag, class: "sb-naked-url" }, { tag: ct.TaskDeadlineTag, class: "sb-task-deadline" }, { tag: ct.NamedAnchorTag, class: "sb-named-anchor" }, diff --git a/web/styles/editor.scss b/web/styles/editor.scss index 8b61fa66..b4864e61 100644 --- a/web/styles/editor.scss +++ b/web/styles/editor.scss @@ -57,7 +57,7 @@ } // If a header only contains a tag, it's likely a line containging "#" which may turn into a hashtag, so style it as such instead of a header - .sb-line-h1:has(> span.sb-hashtag:only-child) { + .sb-line-h1:has(> a.sb-hashtag:only-child) { font-size: 1em; padding: 0; font-weight: normal; @@ -276,6 +276,10 @@ font-size: 0.9em; } + a.sb-hashtag { + text-decoration: none; + } + .sb-strikethrough { text-decoration: line-through; diff --git a/website/Objects.md b/website/Objects.md index eead518c..2ab617f8 100644 --- a/website/Objects.md +++ b/website/Objects.md @@ -22,9 +22,32 @@ Beside these, any number of additional tag-specific and custom [[Attributes]] ca # Tags Every object has a main `tag`, which signifies the type of object being described. In addition, any number of additional tags can be assigned as well via the `tags` attribute. You can use either the main `tag` or any of the `tags` as query sources in [[Live Queries]] — examples below. -Here are the currently built-in tags: - -## page +## Styling +You can add custom styles to a tag by leveraging the `data-tag-name` attribute, [CSS Attribute Selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) and custom [[Speace Style]]'s. Every tag gets an attribute added to it called `data-tag-name` that is set to the tag name with the `#` symbol stripped out. So given the tag #my-cool-tag the `data-tag-name` attribute would look like: + `data-tag-name="my-cool-tag"`. +This allows us to do things like change the color of the #my-cool-tag to have a purple background, limegreen text and bold font by adding the following [[Space Style]]: +```css +.sb-hashtag[data-tag-name="my-cool-tag"] { + background: purple; + color: limegreen; + font-weight: bolder; +} +``` +Additionally tags written using angle brackets, such as... + # +...can be styled via [[Space Style]] like this: +```css +.sb-hashtag[data-tag-name="my cool tag"] { + background: purple; + color: limegreen; + font-weight: bolder; +} +``` + + +## Built-in tags + +### page Every page in your space is available via the `page` tag. You can attach _additional_ tags to a page, by either specifying them in the `tags` attribute [[Frontmatter]], or by putting additional [[Tags]] in a stand alone paragraph with no other (textual) content in them, for instance check the very first line of this page that says `#level/intermediate`. In addition to `ref` and `tags`, the `page` tag defines a bunch of additional attributes as can be seen in this example query: @@ -39,7 +62,7 @@ Note that you can also query this page using the `level/intermediate` directly: level/intermediate ``` -## aspiring-page +### aspiring-page [[Aspiring Pages]] are pages that are linked to, but not yet created. ```query @@ -47,7 +70,7 @@ aspiring-page ``` -## table +### table Markdown table rows are indexed using the `table` tag, any additional tags can be added using [[Tags]] in any of its cells. | Title | Description Text | @@ -62,7 +85,7 @@ table Table headers will be normalized by converting them to lowercase and replacing all non alphanumeric characters with `_`. -## item +### item List items (both bullet point and numbered items) are indexed with the `item` tag, and additional tags can be added using [[Tags]]. Here is an example of a #quote item using a custom [[Attributes|attribute]]: @@ -108,7 +131,7 @@ upnext render [[Library/Core/Query/Task]] Similar to [[#item]], `task` objects have a `parent` attribute when nested (pointing to their parent `item`), and inherit their ancestor’s tags in `itags`. -## taskstate +### taskstate [[Plugs/Tasks]] support the default `x` and ` ` states (done and not done), but custom states as well. Custom states used across your space are kept in `taskstate`: * [NOT STARTED] Task 1 @@ -150,7 +173,7 @@ Which then becomes queriable via the `person` tag: person ``` -## link +### link All page _links_ are tagged with `link`. You cannot attach additional tags to links. The main two attributes of a link are: * `toPage` the page the link is linking _to_ @@ -166,7 +189,7 @@ Here is a query that shows some links that appear in this particular page: link where page = @page.name limit 5 ``` -## anchor +### anchor [[Markdown/Anchors]] use the $myanchor notation to allow deeplinking into a page and are also indexed and queryable. It is not possible to attach additional tags to an anchor. Here is an example query: @@ -175,7 +198,7 @@ Here is an example query: anchor where page = @page.name ``` -## header +### header Headers (lines starting with `#`, `##` etc.) are indexed as well and queriable. ```query @@ -183,7 +206,7 @@ header where page = @page.name limit 3 ``` -## tag +### tag The ultimate meta tag is _tag_ itself, which indexes for all tags used, in which page they appear and what their “parent tag” is (the context of the tag: either `page`, `item` or `task`). Here are the tags used/defined in this page: @@ -192,7 +215,7 @@ Here are the tags used/defined in this page: tag where page = @page.name select name, parent ``` -## space-config +### space-config This stores all configuration picked up as part of [[Space Config]] ```query @@ -200,16 +223,16 @@ space-config select key ``` -# System tags +## System tags The following tags are technically implemented a bit differently than the rest, but they are still available to be queried. -## command +### command Enables querying of all [[Commands]] available in SilverBullet as well as their assigned keyboard shortcuts. ```query command order by name limit 5 ``` -## syscall +### syscall Enables querying of all [[PlugOS]] syscalls enabled in your space. Mostly useful in the context of [[Plugs]] and [[Space Script]] development. ```query diff --git a/website/Space Style.md b/website/Space Style.md index 693a8613..d049f422 100644 --- a/website/Space Style.md +++ b/website/Space Style.md @@ -69,6 +69,26 @@ button { button:hover { /* box-shadow: #121212 0 0 0 3px, transparent 0 0 0 0; */ } + +/* Add custom styling to a specific tag +--> Example Tag: #my-cool-tag */ +.sb-hashtag[data-tag-name="my-cool-tag"] { + background: purple; + color: limegreen; + font-weight: bolder; +} +/* --> Example Tag with angle brackets: # */ +.sb-hashtag[data-tag-name="my cool tag"] { + background: purple; + color: limegreen; + font-weight: bolder; +} +/* Custom Styling to all tags +.sb-hashtag[data-tag-name] { + background: yellow; + color: goldenrod; + border: 2px dashed goldenrod; */ +} ``` Another example can be found in [[Page Decorations#Use case: pimp my page]]