parent
cafd001214
commit
7c23e8d622
|
@ -172,7 +172,7 @@ export type CodeWidgetButton = {
|
|||
widgetTarget?: boolean;
|
||||
description: string;
|
||||
svg: string;
|
||||
invokeFunction: string;
|
||||
invokeFunction: string[];
|
||||
};
|
||||
|
||||
export type LintDiagnostic = {
|
||||
|
|
|
@ -82,19 +82,19 @@ export async function widget(
|
|||
description: "Bake result",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-align-left"><line x1="17" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="17" y1="18" x2="3" y2="18"></line></svg>`,
|
||||
invokeFunction: "query.bakeButton",
|
||||
invokeFunction: ["query.bakeButton", bodyText],
|
||||
},
|
||||
{
|
||||
description: "Edit",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>`,
|
||||
invokeFunction: "query.editButton",
|
||||
invokeFunction: ["query.editButton", bodyText],
|
||||
},
|
||||
{
|
||||
description: "Reload",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
|
||||
invokeFunction: "index.refreshWidgets",
|
||||
invokeFunction: ["index.refreshWidgets"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -88,7 +88,7 @@ export async function renderTemplateWidgets(side: "top" | "bottom"): Promise<
|
|||
description: "Reload",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
|
||||
invokeFunction: "index.refreshWidgets",
|
||||
invokeFunction: ["index.refreshWidgets"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
import {
|
||||
addParentPointers,
|
||||
collectNodesOfType,
|
||||
findNodeMatching,
|
||||
findNodeOfType,
|
||||
findParentMatching,
|
||||
type ParseTree,
|
||||
|
@ -20,6 +19,10 @@ import type { CodeWidgetContent } from "../../plug-api/types.ts";
|
|||
import { jsonToMDTable } from "../template/util.ts";
|
||||
import { renderQuery } from "./api.ts";
|
||||
import type { ChangeSpec } from "@codemirror/state";
|
||||
import {
|
||||
findNodeMatching,
|
||||
nodeAtPos,
|
||||
} from "@silverbulletmd/silverbullet/lib/tree";
|
||||
|
||||
export async function widget(
|
||||
bodyText: string,
|
||||
|
@ -50,19 +53,19 @@ export async function widget(
|
|||
description: "Bake result",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-align-left"><line x1="17" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="17" y1="18" x2="3" y2="18"></line></svg>`,
|
||||
invokeFunction: "query.bakeButton",
|
||||
invokeFunction: ["query.bakeButton", bodyText],
|
||||
},
|
||||
{
|
||||
description: "Edit",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>`,
|
||||
invokeFunction: "query.editButton",
|
||||
invokeFunction: ["query.editButton", bodyText],
|
||||
},
|
||||
{
|
||||
description: "Reload",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
|
||||
invokeFunction: "query.refreshAllWidgets",
|
||||
invokeFunction: ["query.refreshAllWidgets"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -94,13 +97,14 @@ export async function bakeButton(bodyText: string) {
|
|||
addParentPointers(tree);
|
||||
|
||||
// Need to find it in page to make the replacement, see editButton for comment about finding by content
|
||||
const textNode = findNodeMatching(tree, (n) => n.text === bodyText);
|
||||
const textNode = findNodeMatching(tree, (n) => n.text === bodyText) ||
|
||||
nodeAtPos(tree, text.indexOf(bodyText));
|
||||
if (!textNode) {
|
||||
throw new Error(`Could not find node to bake`);
|
||||
}
|
||||
const blockNode = findParentMatching(
|
||||
textNode,
|
||||
(n) => n.type === "FencedCode",
|
||||
(n) => n.type === "FencedCode" || n.type === "Image",
|
||||
);
|
||||
if (!blockNode) {
|
||||
removeParentPointers(textNode);
|
||||
|
@ -135,18 +139,25 @@ export async function bakeAllWidgets() {
|
|||
|
||||
/**
|
||||
* Create change description to replace a widget source with its markdown output
|
||||
* @param codeBlockNode node of type FencedCode for a markdown widget (eg. query, template, toc)
|
||||
* @param nodeToReplace node of type FencedCode or Image for a markdown widget (eg. query, template, toc)
|
||||
* @returns single replacement for the editor, or null if the widget didn't render to markdown
|
||||
*/
|
||||
async function changeForBake(
|
||||
codeBlockNode: ParseTree,
|
||||
nodeToReplace: ParseTree,
|
||||
): Promise<ChangeSpec | null> {
|
||||
const lang = renderToText(
|
||||
findNodeOfType(codeBlockNode, "CodeInfo") ?? undefined,
|
||||
);
|
||||
const body = renderToText(
|
||||
findNodeOfType(codeBlockNode, "CodeText") ?? undefined,
|
||||
);
|
||||
const lang = nodeToReplace.type === "FencedCode"
|
||||
? renderToText(findNodeOfType(nodeToReplace, "CodeInfo") ?? undefined)
|
||||
: nodeToReplace.type === "Image"
|
||||
? "transclusion"
|
||||
: undefined;
|
||||
|
||||
let body: string | undefined = undefined;
|
||||
if (nodeToReplace.type === "FencedCode") {
|
||||
body = renderToText(findNodeOfType(nodeToReplace, "CodeText") ?? undefined);
|
||||
} else if (nodeToReplace.type === "Image") {
|
||||
body = renderToText(nodeToReplace);
|
||||
}
|
||||
|
||||
if (!lang || body === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
@ -158,15 +169,15 @@ async function changeForBake(
|
|||
);
|
||||
if (
|
||||
!content || !content.markdown === undefined ||
|
||||
codeBlockNode.from === undefined ||
|
||||
codeBlockNode.to === undefined
|
||||
nodeToReplace.from === undefined ||
|
||||
nodeToReplace.to === undefined
|
||||
) { // Check attributes for undefined because 0 or empty string could be valid
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
from: codeBlockNode.from,
|
||||
to: codeBlockNode.to,
|
||||
from: nodeToReplace.from,
|
||||
to: nodeToReplace.to,
|
||||
insert: content.markdown,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -48,6 +48,14 @@ functions:
|
|||
codeWidget: template
|
||||
renderMode: markdown
|
||||
|
||||
transclusionWidget:
|
||||
path: widget.ts:transclusionWidget
|
||||
codeWidget: transclusion
|
||||
renderMode: markdown
|
||||
|
||||
navigateButton:
|
||||
path: widget.ts:navigateButton
|
||||
|
||||
# API invoked when a new page is created
|
||||
newPage:
|
||||
path: page.ts:newPage
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
editor,
|
||||
markdown,
|
||||
space,
|
||||
system,
|
||||
|
@ -9,11 +10,14 @@ import type { CodeWidgetContent, PageMeta } from "../../plug-api/types.ts";
|
|||
import { renderTemplate } from "./plug_api.ts";
|
||||
import { renderToText } from "@silverbulletmd/silverbullet/lib/tree";
|
||||
import {
|
||||
isFederationPath,
|
||||
resolvePath,
|
||||
rewritePageRefs,
|
||||
rewritePageRefsInString,
|
||||
} from "@silverbulletmd/silverbullet/lib/resolve";
|
||||
import { queryParsed } from "../query/api.ts";
|
||||
import { parseQuery } from "../../plug-api/lib/parse_query.ts";
|
||||
import { parsePageRef } from "@silverbulletmd/silverbullet/lib/page_ref";
|
||||
|
||||
type TemplateWidgetConfig = {
|
||||
// Pull the template from a page
|
||||
|
@ -99,19 +103,19 @@ export async function includeWidget(
|
|||
description: "Bake result",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-align-left"><line x1="17" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="17" y1="18" x2="3" y2="18"></line></svg>`,
|
||||
invokeFunction: "query.bakeButton",
|
||||
invokeFunction: ["query.bakeButton", bodyText],
|
||||
},
|
||||
{
|
||||
description: "Edit",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>`,
|
||||
invokeFunction: "query.editButton",
|
||||
invokeFunction: ["query.editButton", bodyText],
|
||||
},
|
||||
{
|
||||
description: "Reload",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
|
||||
invokeFunction: "query.refreshAllWidgets",
|
||||
invokeFunction: ["query.refreshAllWidgets"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -160,19 +164,19 @@ export async function templateWidget(
|
|||
description: "Bake result",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-align-left"><line x1="17" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="17" y1="18" x2="3" y2="18"></line></svg>`,
|
||||
invokeFunction: "query.bakeButton",
|
||||
invokeFunction: ["query.bakeButton", bodyText],
|
||||
},
|
||||
{
|
||||
description: "Edit",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>`,
|
||||
invokeFunction: "query.editButton",
|
||||
invokeFunction: ["query.editButton", bodyText],
|
||||
},
|
||||
{
|
||||
description: "Reload",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
|
||||
invokeFunction: "query.refreshAllWidgets",
|
||||
invokeFunction: ["query.refreshAllWidgets"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -182,3 +186,73 @@ export async function templateWidget(
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function transclusionWidget(
|
||||
bodyText: string,
|
||||
pageName: string,
|
||||
): Promise<CodeWidgetContent> {
|
||||
const config = await system.getSpaceConfig();
|
||||
const pageMeta: PageMeta = await loadPageObject(pageName);
|
||||
let url: string | undefined = undefined;
|
||||
let match: RegExpExecArray | null;
|
||||
if ((match = /!?\[([^\]]*)\]\((.+)\)/g.exec(bodyText))) {
|
||||
[/* fullMatch */, /* alias */ , url] = match;
|
||||
} else if (
|
||||
(match = /(!?\[\[)([^\]\|]+)(?:\|([^\]]+))?(\]\])/g.exec(bodyText))
|
||||
) {
|
||||
[/* fullMatch */, /* firstMark */ , url /* alias */] = match;
|
||||
if (!isFederationPath(url)) {
|
||||
url = "/" + url;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (!url) {
|
||||
throw new Error("Could not parse link");
|
||||
}
|
||||
url = resolvePath(pageName, url, true);
|
||||
|
||||
const templateText =
|
||||
`{{rewriteRefsAndFederationLinks([[${url}]], "${url}")}}`;
|
||||
|
||||
const { text: rendered } = await renderTemplate(
|
||||
templateText,
|
||||
pageMeta,
|
||||
{ page: pageMeta, config },
|
||||
);
|
||||
|
||||
return {
|
||||
markdown: rendered,
|
||||
buttons: [
|
||||
{
|
||||
description: "Bake result",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-align-left"><line x1="17" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="17" y1="18" x2="3" y2="18"></line></svg>`,
|
||||
invokeFunction: ["query.bakeButton", bodyText],
|
||||
},
|
||||
{
|
||||
description: "Open Page",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>`,
|
||||
invokeFunction: ["template.navigateButton", url],
|
||||
},
|
||||
{
|
||||
description: "Reload",
|
||||
svg:
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-refresh-cw"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
|
||||
invokeFunction: ["query.refreshAllWidgets", bodyText],
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (e: any) {
|
||||
return {
|
||||
markdown: `**Error:** ${e.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate to page in a transclusion widget
|
||||
export async function navigateButton(url: string) {
|
||||
const pageRef = parsePageRef(url);
|
||||
await editor.navigate(pageRef, false, false);
|
||||
}
|
||||
|
|
|
@ -160,8 +160,7 @@ export function inlineContentPlugin(client: Client) {
|
|||
}
|
||||
|
||||
const text = state.sliceDoc(node.from, node.to);
|
||||
let [url, alias]: (string | null)[] = [null, null];
|
||||
let dim: ContentDimensions | undefined;
|
||||
let [url, alias]: (string | undefined)[] = [undefined, undefined];
|
||||
let match: RegExpExecArray | null;
|
||||
if ((match = /!?\[([^\]]*)\]\((.+)\)/g.exec(text))) {
|
||||
[/* fullMatch */, alias, url] = match;
|
||||
|
@ -172,10 +171,12 @@ export function inlineContentPlugin(client: Client) {
|
|||
if (!isFederationPath(url)) {
|
||||
url = "/" + url;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
let dim: ContentDimensions | undefined;
|
||||
if (alias) {
|
||||
const { alias: parsedAlias, dim: parsedDim } = parseAlias(alias);
|
||||
if (parsedAlias) {
|
||||
|
@ -187,7 +188,11 @@ export function inlineContentPlugin(client: Client) {
|
|||
}
|
||||
|
||||
if (isLocalPath(url)) {
|
||||
url = resolvePath(client.currentPage, decodeURI(url), true);
|
||||
url = resolvePath(
|
||||
client.currentPage,
|
||||
decodeURI(url),
|
||||
true,
|
||||
);
|
||||
const pageRef = parsePageRef(url);
|
||||
if (
|
||||
isFederationPath(pageRef.page) ||
|
||||
|
@ -195,30 +200,24 @@ export function inlineContentPlugin(client: Client) {
|
|||
) {
|
||||
// This is a page reference, let's inline the content
|
||||
const codeWidgetCallback = client.clientSystem.codeWidgetHook
|
||||
.codeWidgetCallbacks.get("template");
|
||||
.codeWidgetCallbacks.get("transclusion");
|
||||
|
||||
if (!codeWidgetCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
widgets.push(
|
||||
Decoration.line({
|
||||
class: "sb-fenced-code-iframe",
|
||||
}).range(node.to),
|
||||
);
|
||||
|
||||
widgets.push(
|
||||
Decoration.widget({
|
||||
widget: new MarkdownWidget(
|
||||
node.from,
|
||||
client,
|
||||
`widget:${client.currentPage}:${text}`,
|
||||
`{{rewriteRefsAndFederationLinks([[${url}]], "${url}")}}`,
|
||||
text,
|
||||
codeWidgetCallback,
|
||||
"sb-markdown-widget sb-markdown-widget-inline",
|
||||
),
|
||||
block: true,
|
||||
}).range(node.to),
|
||||
}).range(node.to + 1),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -226,7 +225,12 @@ export function inlineContentPlugin(client: Client) {
|
|||
|
||||
widgets.push(
|
||||
Decoration.widget({
|
||||
widget: new InlineContentWidget(url, alias, dim, client),
|
||||
widget: new InlineContentWidget(
|
||||
url,
|
||||
alias,
|
||||
dim,
|
||||
client,
|
||||
),
|
||||
block: true,
|
||||
}).range(node.to + 1),
|
||||
);
|
||||
|
|
|
@ -257,7 +257,7 @@ export class MarkdownWidget extends WidgetType {
|
|||
div.addEventListener("click", () => {
|
||||
console.log("Widget clicked");
|
||||
this.client.clientSystem.localSyscall("system.invokeFunction", [
|
||||
button.invokeFunction,
|
||||
button.invokeFunction[0],
|
||||
this.from,
|
||||
]).catch(console.error);
|
||||
});
|
||||
|
@ -266,10 +266,10 @@ export class MarkdownWidget extends WidgetType {
|
|||
"click",
|
||||
(e) => {
|
||||
e.stopPropagation();
|
||||
this.client.clientSystem.localSyscall("system.invokeFunction", [
|
||||
this.client.clientSystem.localSyscall(
|
||||
"system.invokeFunction",
|
||||
button.invokeFunction,
|
||||
this.bodyText,
|
||||
]).then((newContent: string | undefined) => {
|
||||
).then((newContent: string | undefined) => {
|
||||
if (newContent) {
|
||||
div.innerText = newContent;
|
||||
}
|
||||
|
|
|
@ -590,10 +590,6 @@
|
|||
|
||||
.sb-markdown-widget-inline {
|
||||
margin: 0 0 0 0;
|
||||
|
||||
&:hover .button-bar {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.sb-fenced-code-iframe {
|
||||
|
|
Loading…
Reference in New Issue