Merge pull request #95 from space-nuko/frontend-note
Frontend Markdown note
This commit is contained in:
Submodule litegraph updated: 9b8d28d3e2...7c38fa4aed
@@ -50,6 +50,7 @@
|
||||
"@codemirror/search": "^6.2.2",
|
||||
"@codemirror/state": "^6.1.2",
|
||||
"@codemirror/view": "^6.4.1",
|
||||
"@dogagenc/svelte-markdown": "^0.2.4",
|
||||
"@gradio/accordion": "workspace:*",
|
||||
"@gradio/atoms": "workspace:*",
|
||||
"@gradio/button": "workspace:*",
|
||||
@@ -85,6 +86,7 @@
|
||||
"framework7": "^8.0.3",
|
||||
"framework7-svelte": "^8.0.3",
|
||||
"img-comparison-slider": "^8.0.0",
|
||||
"marked": "^5.0.3",
|
||||
"pollen-css": "^4.6.2",
|
||||
"radix-icons-svelte": "^1.2.1",
|
||||
"style-mod": "^4.0.3",
|
||||
|
||||
35
pnpm-lock.yaml
generated
35
pnpm-lock.yaml
generated
@@ -25,6 +25,9 @@ importers:
|
||||
'@codemirror/view':
|
||||
specifier: ^6.4.1
|
||||
version: 6.11.0
|
||||
'@dogagenc/svelte-markdown':
|
||||
specifier: ^0.2.4
|
||||
version: 0.2.4(svelte@3.58.0)
|
||||
'@gradio/accordion':
|
||||
specifier: workspace:*
|
||||
version: link:gradio/js/accordion
|
||||
@@ -130,6 +133,9 @@ importers:
|
||||
img-comparison-slider:
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.0
|
||||
marked:
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3
|
||||
pollen-css:
|
||||
specifier: ^4.6.2
|
||||
version: 4.6.2
|
||||
@@ -1357,7 +1363,6 @@ packages:
|
||||
'@codemirror/language': ^6.0.0
|
||||
'@codemirror/state': ^6.0.0
|
||||
'@codemirror/view': ^6.0.0
|
||||
'@lezer/common': ^1.0.0
|
||||
dependencies:
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.2.0
|
||||
@@ -1501,6 +1506,16 @@ packages:
|
||||
w3c-keyname: 2.2.6
|
||||
dev: false
|
||||
|
||||
/@dogagenc/svelte-markdown@0.2.4(svelte@3.58.0):
|
||||
resolution: {integrity: sha512-UmmHHZ7rilAbBYiNsxuL5d8Ac79EhFXrhjsUNr30BPzn+T7ohJR8kHMFjDYDQc0tOQOfKbICvkPAQ6cprqS3Eg==}
|
||||
peerDependencies:
|
||||
svelte: ^3.0.0
|
||||
dependencies:
|
||||
'@types/marked': 4.3.1
|
||||
marked: 4.3.0
|
||||
svelte: 3.58.0
|
||||
dev: false
|
||||
|
||||
/@esbuild/android-arm64@0.17.18:
|
||||
resolution: {integrity: sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2423,6 +2438,10 @@ packages:
|
||||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||
dev: true
|
||||
|
||||
/@types/marked@4.3.1:
|
||||
resolution: {integrity: sha512-vSSbKZFbNktrQ15v7o1EaH78EbWV+sPQbPjHG+Cp8CaNcPFUEfjZ0Iml/V0bFDwsTlYe8o6XC5Hfdp91cqPV2g==}
|
||||
dev: false
|
||||
|
||||
/@types/node@10.17.60:
|
||||
resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==}
|
||||
dev: false
|
||||
@@ -3098,8 +3117,6 @@ packages:
|
||||
'@codemirror/search': 6.4.0
|
||||
'@codemirror/state': 6.2.0
|
||||
'@codemirror/view': 6.11.0
|
||||
transitivePeerDependencies:
|
||||
- '@lezer/common'
|
||||
dev: false
|
||||
|
||||
/codemirror@6.0.1(@lezer/common@1.0.2):
|
||||
@@ -5481,6 +5498,18 @@ packages:
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dev: false
|
||||
|
||||
/marked@4.3.0:
|
||||
resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==}
|
||||
engines: {node: '>= 12'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/marked@5.0.3:
|
||||
resolution: {integrity: sha512-KUONa43Uk74uUNWMxh6lfaNYmSAsRMiDAaX8QBCCRVXzEufR0zX6T33vrGbvTnQLL02ungDM3KSzZtO+chJaHg==}
|
||||
engines: {node: '>= 18'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/md5-hex@3.0.1:
|
||||
resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
export let elem_classes: string[] = []
|
||||
export let style: string = ""
|
||||
export let label: string = ""
|
||||
export let mask: ComfyImageLocation | null;
|
||||
// let propsChanged: Writable<number> | null = null;
|
||||
let dragging = false;
|
||||
let pending_upload = false;
|
||||
@@ -172,6 +173,15 @@
|
||||
bind:naturalWidth={imgWidth}
|
||||
bind:naturalHeight={imgHeight}
|
||||
/>
|
||||
{#key mask}
|
||||
{#if mask}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<img src={convertComfyOutputToComfyURL(mask)}
|
||||
alt={firstImage.filename}
|
||||
on:click={onImgClicked}
|
||||
/>
|
||||
{/if}
|
||||
{/key}
|
||||
{:else}
|
||||
<Upload
|
||||
file_count={fileCount}
|
||||
@@ -201,6 +211,9 @@
|
||||
}
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -68,7 +68,7 @@ function getConnectionPos(node: SerializedLGraphNode, is_input: boolean, slotNum
|
||||
return out;
|
||||
}
|
||||
|
||||
function createSerializedWidgetNode(vanillaWorkflow: ComfyVanillaWorkflow, node: SerializedLGraphNode, slotIndex: number, isInput: boolean, widgetNodeType: string, value: any): [ComfyWidgetNode, SerializedComfyWidgetNode] {
|
||||
function createSerializedWidgetNode(vanillaWorkflow: ComfyVanillaWorkflow, widgetNodeType: string, value: any, node?: SerializedLGraphNode, slotIndex?: number, isInput?: boolean): [ComfyWidgetNode, SerializedComfyWidgetNode] {
|
||||
const comfyWidgetNode = LiteGraph.createNode<ComfyWidgetNode>(widgetNodeType);
|
||||
comfyWidgetNode.flags.collapsed = true;
|
||||
const size: Vector2 = [0, 0];
|
||||
@@ -85,12 +85,15 @@ function createSerializedWidgetNode(vanillaWorkflow: ComfyVanillaWorkflow, node:
|
||||
const serWidgetNode = comfyWidgetNode.serialize() as SerializedComfyWidgetNode;
|
||||
serWidgetNode.comfyValue = value;
|
||||
serWidgetNode.shownOutputProperties = {};
|
||||
getConnectionPos(node, isInput, slotIndex, serWidgetNode.pos);
|
||||
if (isInput)
|
||||
serWidgetNode.pos[0] -= size[0] - 20;
|
||||
else
|
||||
serWidgetNode.pos[0] += 20;
|
||||
serWidgetNode.pos[1] += LiteGraph.NODE_TITLE_HEIGHT / 2;
|
||||
|
||||
if (node != null) {
|
||||
getConnectionPos(node, isInput, slotIndex, serWidgetNode.pos);
|
||||
if (isInput)
|
||||
serWidgetNode.pos[0] -= size[0] - 20;
|
||||
else
|
||||
serWidgetNode.pos[0] += 20;
|
||||
serWidgetNode.pos[1] += LiteGraph.NODE_TITLE_HEIGHT / 2;
|
||||
}
|
||||
|
||||
if (widgetNodeType === "ui/text" && typeof value === "string" && value.indexOf("\n") != -1) {
|
||||
const lineCount = countNewLines(value);
|
||||
@@ -260,11 +263,12 @@ function convertPrimitiveNode(vanillaWorkflow: ComfyVanillaWorkflow, node: Seria
|
||||
|
||||
const [comfyWidgetNode, serWidgetNode] = createSerializedWidgetNode(
|
||||
vanillaWorkflow,
|
||||
widgetNodeType,
|
||||
value,
|
||||
node,
|
||||
0, // first output on the PrimitiveNode
|
||||
false, // this is an output slot index
|
||||
widgetNodeType,
|
||||
value);
|
||||
false // this is an output slot index
|
||||
);
|
||||
|
||||
// Set the UI node's min/max/step from the node def
|
||||
configureWidgetNodeProperties(serWidgetNode, widgetOpts)
|
||||
@@ -381,6 +385,20 @@ export default function convertVanillaWorkflow(vanillaWorkflow: ComfyVanillaWork
|
||||
removeSerializedNode(vanillaWorkflow, node);
|
||||
continue
|
||||
}
|
||||
else if (node.type === "Note") {
|
||||
const [comfyWidgetNode, serWidgetNode] = createSerializedWidgetNode(
|
||||
vanillaWorkflow,
|
||||
"ui/markdown",
|
||||
node.widgets_values[0]
|
||||
);
|
||||
serWidgetNode.pos = [node.pos[0], node.pos[1]]
|
||||
|
||||
const group = layoutState.addContainer(left, { title: "" })
|
||||
layoutState.addWidget(group, comfyWidgetNode)
|
||||
|
||||
removeSerializedNode(vanillaWorkflow, node);
|
||||
continue
|
||||
}
|
||||
|
||||
const def = ComfyApp.knownBackendNodes[node.type];
|
||||
if (def == null) {
|
||||
@@ -449,11 +467,12 @@ export default function convertVanillaWorkflow(vanillaWorkflow: ComfyVanillaWork
|
||||
|
||||
const [comfyWidgetNode, serWidgetNode] = createSerializedWidgetNode(
|
||||
vanillaWorkflow,
|
||||
widgetNodeType,
|
||||
value,
|
||||
node,
|
||||
connInputIndex,
|
||||
true,
|
||||
widgetNodeType,
|
||||
value);
|
||||
true
|
||||
);
|
||||
|
||||
configureWidgetNodeProperties(serWidgetNode, inputOpts)
|
||||
|
||||
@@ -492,11 +511,12 @@ export default function convertVanillaWorkflow(vanillaWorkflow: ComfyVanillaWork
|
||||
// Let's create a gallery for this output node and hook it up
|
||||
const [comfyGalleryNode, serGalleryNode] = createSerializedWidgetNode(
|
||||
vanillaWorkflow,
|
||||
"ui/gallery",
|
||||
[],
|
||||
node,
|
||||
connOutputIndex,
|
||||
false,
|
||||
"ui/gallery",
|
||||
[]);
|
||||
);
|
||||
|
||||
if (group == null)
|
||||
group = layoutState.addContainer(isOutputNode ? right : left, { title: node.title || node.type })
|
||||
|
||||
57
src/lib/nodes/widgets/ComfyMarkdownNode.ts
Normal file
57
src/lib/nodes/widgets/ComfyMarkdownNode.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { BuiltInSlotType, LiteGraph, type ITextWidget, type SlotLayout } from "@litegraph-ts/core";
|
||||
|
||||
import MarkdownWidget from "$lib/widgets/MarkdownWidget.svelte";
|
||||
import ComfyWidgetNode, { type ComfyWidgetProperties } from "./ComfyWidgetNode";
|
||||
|
||||
export interface ComfyMarkdownProperties extends ComfyWidgetProperties {
|
||||
}
|
||||
|
||||
export default class ComfyMarkdownNode extends ComfyWidgetNode<string> {
|
||||
override properties: ComfyMarkdownProperties = {
|
||||
tags: [],
|
||||
defaultValue: false,
|
||||
}
|
||||
|
||||
static slotLayout: SlotLayout = {
|
||||
inputs: [
|
||||
{ name: "store", type: BuiltInSlotType.ACTION }
|
||||
],
|
||||
outputs: [
|
||||
{ name: "value", type: "string" },
|
||||
{ name: "changed", type: BuiltInSlotType.EVENT },
|
||||
]
|
||||
}
|
||||
|
||||
override svelteComponentType = MarkdownWidget;
|
||||
override defaultValue = "";
|
||||
|
||||
constructor(name?: string) {
|
||||
super(name, "")
|
||||
}
|
||||
|
||||
override createDisplayWidget(): ITextWidget {
|
||||
const widget = this.addWidget<ITextWidget>(
|
||||
"text",
|
||||
"Value",
|
||||
"",
|
||||
(v: string) => {
|
||||
if (v == null || v === this.getValue()) {
|
||||
return;
|
||||
}
|
||||
this.setValue(v);
|
||||
},
|
||||
{
|
||||
multiline: true,
|
||||
inputStyle: { fontFamily: "monospace" }
|
||||
}
|
||||
)
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType({
|
||||
class: ComfyMarkdownNode,
|
||||
title: "UI.Markdown",
|
||||
desc: "Displays Markdown in the UI",
|
||||
type: "ui/markdown"
|
||||
})
|
||||
@@ -106,13 +106,18 @@ export default abstract class ComfyWidgetNode<T = any> extends ComfyGraphNode {
|
||||
this.value = writable(value)
|
||||
this.color ||= color.color
|
||||
this.bgColor ||= color.bgColor
|
||||
this.displayWidget = this.addWidget<ITextWidget>(
|
||||
this.displayWidget = this.createDisplayWidget();
|
||||
this.unsubscribe = this.value.subscribe(this.onValueUpdated.bind(this))
|
||||
}
|
||||
|
||||
protected createDisplayWidget(): ITextWidget {
|
||||
const widget = this.addWidget<ITextWidget>(
|
||||
"text",
|
||||
"Value",
|
||||
""
|
||||
);
|
||||
this.displayWidget.disabled = true; // prevent editing
|
||||
this.unsubscribe = this.value.subscribe(this.onValueUpdated.bind(this))
|
||||
)
|
||||
widget.disabled = true; // prevent editing
|
||||
return widget;
|
||||
}
|
||||
|
||||
addPropertyAsOutput(propertyName: string, type: string) {
|
||||
|
||||
@@ -9,3 +9,4 @@ export { default as ComfyRadioNode } from "./ComfyRadioNode"
|
||||
export { default as ComfyNumberNode } from "./ComfyNumberNode"
|
||||
export { default as ComfyTextNode } from "./ComfyTextNode"
|
||||
export { default as ComfyMultiRegionNode } from "./ComfyMultiRegionNode"
|
||||
export { default as ComfyMarkdownNode } from "./ComfyMarkdownNode"
|
||||
|
||||
@@ -52,12 +52,19 @@
|
||||
};
|
||||
|
||||
let hasImage = false;
|
||||
|
||||
$: hasImage = $nodeValue && $nodeValue.length > 0;
|
||||
$: if (!hasImage) {
|
||||
editMask = false;
|
||||
}
|
||||
|
||||
let mask: ComfyImageLocation | null;
|
||||
$: if (hasImage && canMask) {
|
||||
mask = $nodeValue[0].children?.find(i => i.tags.includes("mask"))?.comfyUIFile;
|
||||
}
|
||||
else {
|
||||
mask = null;
|
||||
}
|
||||
|
||||
const MASK_FILENAME: string = "ComfyBoxMask.png"
|
||||
|
||||
async function onMaskReleased(e: CustomEvent<MaskCanvasData>) {
|
||||
@@ -122,6 +129,7 @@
|
||||
// TODO other child image types preserved here?
|
||||
image.children = [];
|
||||
}
|
||||
mask = null;
|
||||
if (maskCanvasComp) {
|
||||
maskCanvasComp.clearStrokes();
|
||||
}
|
||||
@@ -232,6 +240,7 @@
|
||||
<ImageUpload value={_value}
|
||||
bind:imgWidth={$imgWidth}
|
||||
bind:imgHeight={$imgHeight}
|
||||
{mask}
|
||||
fileCount={"single"}
|
||||
elem_classes={[]}
|
||||
style={""}
|
||||
|
||||
233
src/lib/widgets/MarkdownWidget.svelte
Normal file
233
src/lib/widgets/MarkdownWidget.svelte
Normal file
@@ -0,0 +1,233 @@
|
||||
<script lang="ts">
|
||||
import { type WidgetLayout } from "$lib/stores/layoutStates";
|
||||
import { get, type Writable, writable } from "svelte/store";
|
||||
import { Block } from "@gradio/atoms";
|
||||
import type { ComfyMarkdownNode } from "$lib/nodes/widgets";
|
||||
import SvelteMarkdown from "@dogagenc/svelte-markdown"
|
||||
import NullMarkdownRenderer from "./markdown/NullMarkdownRenderer.svelte"
|
||||
import { SvelteComponentDev } from "svelte/internal";
|
||||
|
||||
export let widget: WidgetLayout | null = null;
|
||||
export let isMobile: boolean = false;
|
||||
|
||||
let node: ComfyMarkdownNode | null = null;
|
||||
let nodeValue: Writable<string> = writable("");
|
||||
let attrsChanged: Writable<number> = writable(0);
|
||||
|
||||
let renderers: Record<string, typeof SvelteComponentDev> = {
|
||||
"html": NullMarkdownRenderer
|
||||
}
|
||||
|
||||
$: widget && setNodeValue(widget);
|
||||
|
||||
function setNodeValue(widget: WidgetLayout) {
|
||||
if (widget) {
|
||||
node = widget.node as ComfyMarkdownNode
|
||||
nodeValue = node.value;
|
||||
attrsChanged = widget.attrsChanged;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="wrapper prose">
|
||||
{#key $attrsChanged}
|
||||
{#if widget !== null && node !== null}
|
||||
<Block>
|
||||
<SvelteMarkdown source={$nodeValue} {renderers} />
|
||||
</Block>
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.wrapper {
|
||||
padding: 2px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
:global(> button) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:global(> .block) {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.prose {
|
||||
font-weight: var(--prose-text-weight);
|
||||
font-size: var(--text-md);
|
||||
}
|
||||
|
||||
.prose * {
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
|
||||
.prose p {
|
||||
margin-bottom: var(--spacing-sm);
|
||||
line-height: var(--line-lg);
|
||||
}
|
||||
|
||||
/* headings
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
|
||||
|
||||
.prose h1,
|
||||
.prose h2,
|
||||
.prose h3,
|
||||
.prose h4,
|
||||
.prose h5 {
|
||||
margin: var(--spacing-xxl) 0 var(--spacing-lg);
|
||||
font-weight: var(--prose-header-text-weight);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.prose > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.prose h1 {
|
||||
margin-top: 0;
|
||||
font-size: var(--text-xxl);
|
||||
}
|
||||
|
||||
.prose h2 {
|
||||
font-size: var(--text-xl);
|
||||
}
|
||||
|
||||
.prose h3 {
|
||||
font-size: var(--text-lg);
|
||||
}
|
||||
|
||||
.prose h4 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.prose h5 {
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
/* lists
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
|
||||
.prose ul {
|
||||
list-style: circle inside;
|
||||
}
|
||||
.prose ol {
|
||||
list-style: decimal inside;
|
||||
}
|
||||
|
||||
.prose ul > p,
|
||||
.prose li > p {
|
||||
display: inline-block;
|
||||
}
|
||||
.prose ol,
|
||||
.prose ul {
|
||||
margin-top: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
.prose ul ul,
|
||||
.prose ul ol,
|
||||
.prose ol ol,
|
||||
.prose ol ul {
|
||||
margin: 0.5em 0 0.5em 3em;
|
||||
font-size: 90%;
|
||||
}
|
||||
.prose li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
/* code
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
|
||||
.prose code {
|
||||
border: 1px solid var(--border-color-primary);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--background-fill-secondary);
|
||||
padding: 1px 3px;
|
||||
font-size: 85%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.prose pre > code {
|
||||
display: block;
|
||||
padding: 0.5em 0.7em;
|
||||
/* font-size: 100%; */
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* tables
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
|
||||
.prose th,
|
||||
.prose td {
|
||||
border-bottom: 1px solid #e1e1e1;
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
}
|
||||
.prose th:first-child,
|
||||
.prose td:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
.prose th:last-child,
|
||||
.prose td:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
/* spacing
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
|
||||
.prose button,
|
||||
.prose .button {
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
.prose input,
|
||||
.prose textarea,
|
||||
.prose select,
|
||||
.prose fieldset {
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
.prose pre,
|
||||
.prose blockquote,
|
||||
.prose dl,
|
||||
.prose figure,
|
||||
.prose table,
|
||||
.prose p,
|
||||
.prose ul,
|
||||
.prose ol,
|
||||
.prose form {
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
/* links
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
|
||||
.prose a {
|
||||
color: var(--link-text-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.prose a:visited {
|
||||
color: var(--link-text-color-visited);
|
||||
}
|
||||
|
||||
.prose a:hover {
|
||||
color: var(--link-text-color-hover);
|
||||
}
|
||||
.prose a:active {
|
||||
color: var(--link-text-color-active);
|
||||
}
|
||||
|
||||
/* misc
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
|
||||
|
||||
.prose hr {
|
||||
margin-top: 3em;
|
||||
margin-bottom: 3.5em;
|
||||
border-width: 0;
|
||||
border-top: 1px solid #e1e1e1;
|
||||
}
|
||||
|
||||
.prose blockquote {
|
||||
margin: var(--size-6) 0 !important;
|
||||
border-left: 5px solid var(--border-color-primary);
|
||||
padding-left: var(--size-2);
|
||||
}
|
||||
|
||||
.prose :last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
</style>
|
||||
7
src/lib/widgets/markdown/NullMarkdownRenderer.svelte
Normal file
7
src/lib/widgets/markdown/NullMarkdownRenderer.svelte
Normal file
@@ -0,0 +1,7 @@
|
||||
<script>
|
||||
export let href = "";
|
||||
export let title = undefined;
|
||||
export let text = "";
|
||||
</script>
|
||||
|
||||
<div/>
|
||||
Reference in New Issue
Block a user