diff --git a/src/embed.ts b/src/embed.ts index e5ff8c6..4a4fd0c 100644 --- a/src/embed.ts +++ b/src/embed.ts @@ -1,29 +1,29 @@ -import { s } from "hastscript"; -import { Element } from "hast"; +import type { Element } from "hast" +import { s } from "hastscript" -import { fromMarkdown } from "mdast-util-from-markdown"; -import { toHast } from "mdast-util-to-hast"; +import { fromMarkdown } from "mdast-util-from-markdown" +import { toHast } from "mdast-util-to-hast" -import { GenericNode } from "@trbn/jsoncanvas"; +import type { GenericNode } from "@trbn/jsoncanvas" // import { applyDefaults, Options } from "./options"; -import { getCanvasFromEmbed } from "./plugin"; +import { getCanvasFromEmbed } from "./plugin" -const imagesLoaded = [] as Array; +const imagesLoaded = [] as Array export function checkImagesLoaded(callback: Function) { - let allLoaded = imagesLoaded.every((el) => el.complete); - console.group("Images loading", imagesLoaded, allLoaded); - if (imagesLoaded.length < 1) return callback(); + const allLoaded = imagesLoaded.every((el) => el.complete) + console.group("Images loading", imagesLoaded, allLoaded) + if (imagesLoaded.length < 1) return callback() // return callback(); - if (allLoaded) callback(); - else checkImagesLoaded(callback); + if (allLoaded) callback() + else checkImagesLoaded(callback) } // This renders out the images export async function drawEmbedded( svg: Element, grp: Element, - node: GenericNode | any + node: GenericNode | any, ) { if (node.type === "file" && svg) { if (node.file.match(/\.(jpg|jpeg|png|gif)$/i)) { @@ -33,9 +33,9 @@ export async function drawEmbedded( width: node.width - 10, height: node.height - 10, "xlink:href": node.file, - }); + }) - grp.children.push(image); + grp.children.push(image) } } } @@ -44,14 +44,14 @@ export async function drawEmbedded( export async function drawMarkdownEmbed( svg: Element, grp: Element, - node: GenericNode | any + node: GenericNode | any, ) { if (node.type === "file" && svg) { if (node.file.match(/\.(md|mdx)$/i)) { - const mdFile = await getCanvasFromEmbed(node.file); + const mdFile = await getCanvasFromEmbed(node.file) - const mdast = fromMarkdown(mdFile); - const hast = toHast(mdast); + const mdast = fromMarkdown(mdFile) + const hast = toHast(mdast) // Ref: https://stackoverflow.com/questions/45518545/svg-foreignobject-not-showing-on-any-browser-why const embed = s("foreignObject", { @@ -59,10 +59,10 @@ export async function drawMarkdownEmbed( y: 5 + node.y + svg.properties!.renHeight / 2, width: node.width - 10, height: node.height - 10, - }); - embed.children.push(hast as Element); // If this breaks, this is probably the spot it breaks + }) + embed.children.push(hast as Element) // If this breaks, this is probably the spot it breaks - grp.children.push(embed); + grp.children.push(embed) } } } diff --git a/src/index.ts b/src/index.ts index 6b3c677..3d87ecf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ -import { rehypeJsonCanvas } from "./plugin"; +import { rehypeJsonCanvas } from "./plugin" -export type { Options } from "./options"; +export type { Options } from "./options" -export { rehypeJsonCanvas }; -export default rehypeJsonCanvas; +export { rehypeJsonCanvas } +export default rehypeJsonCanvas // CommonJS default export hack /* eslint-env commonjs */ diff --git a/src/jsoncanvas.ts b/src/jsoncanvas.ts index 7daa987..fd2ce70 100644 --- a/src/jsoncanvas.ts +++ b/src/jsoncanvas.ts @@ -1,80 +1,80 @@ -import { s } from "hastscript"; -import { Element } from "hast"; +import type { Element } from "hast" +import { s } from "hastscript" -import { JSONCanvas, Edge, GenericNode } from "@trbn/jsoncanvas"; +import type { Edge, GenericNode, JSONCanvas } from "@trbn/jsoncanvas" -import { applyDefaults, Options } from "./options"; +import { type Options, applyDefaults } from "./options" -import { drawEmbedded, drawMarkdownEmbed, checkImagesLoaded } from "./embed"; +import { checkImagesLoaded, drawEmbedded, drawMarkdownEmbed } from "./embed" function calculateMinimumCanvasSize(canvas: JSONCanvas) { - let minX = Infinity, - minY = Infinity, - maxX = -Infinity, - maxY = -Infinity; + let minX = Number.POSITIVE_INFINITY, + minY = Number.POSITIVE_INFINITY, + maxX = Number.NEGATIVE_INFINITY, + maxY = Number.NEGATIVE_INFINITY canvas.getNodes().forEach((node) => { - minX = Math.min(minX, node.x); - minY = Math.min(minY, node.y); - maxX = Math.max(maxX, node.x + node.width); - maxY = Math.max(maxY, node.y + node.height); - }); + minX = Math.min(minX, node.x) + minY = Math.min(minY, node.y) + maxX = Math.max(maxX, node.x + node.width) + maxY = Math.max(maxY, node.y + node.height) + }) - const canvasWidth = maxX - minX; - const canvasHeight = maxY - minY; + const canvasWidth = maxX - minX + const canvasHeight = maxY - minY - return { canvasWidth, canvasHeight, offsetX: -minX, offsetY: -minY }; + return { canvasWidth, canvasHeight, offsetX: -minX, offsetY: -minY } } export function validate(jsonCanvasData: JSONCanvas) { // Use the typescript lib to vlaidate? - console.log("Validate!", jsonCanvasData); - return true; + console.log("Validate!", jsonCanvasData) + return true } export function render( jsc: JSONCanvas, - config?: Partial + config?: Partial, ): Element | null { - const options = applyDefaults(config); + const options = applyDefaults(config) const { canvasWidth, canvasHeight, offsetX, offsetY } = - calculateMinimumCanvasSize(jsc); + calculateMinimumCanvasSize(jsc) // Init Canvas objects - const svg = initRender(canvasWidth + offsetX, canvasHeight + offsetY); + const svg = initRender(canvasWidth + offsetX, canvasHeight + offsetY) - if (svg === null) return null; + if (svg === null) return null // Draw nodes jsc.getNodes().forEach((node) => { - drawNode(svg, node, options); - }); + drawNode(svg, node, options) + }) // Draw Edges jsc.getEdges().forEach((edge) => { - const fromNode = jsc.getNodes().find((node) => node.id === edge.fromNode); - const toNode = jsc.getNodes().find((node) => node.id === edge.toNode); + const fromNode = jsc.getNodes().find((node) => node.id === edge.fromNode) + const toNode = jsc.getNodes().find((node) => node.id === edge.toNode) if (toNode !== undefined && fromNode !== undefined) - drawEdge(svg, toNode, fromNode, edge, options); - }); + drawEdge(svg, toNode, fromNode, edge, options) + }) - return checkImagesLoaded(() => renderToBuffer(svg)); + return checkImagesLoaded(() => renderToBuffer(svg)) } function renderToBuffer(svg: Element, config?: Partial): Element { - const options = applyDefaults(config); - console.log("Rendering", svg, options); - return svg; + const options = applyDefaults(config) + console.log("Rendering", svg, options) + return svg } function initRender( width: number, height: number, - config?: Partial + config?: Partial, ): Element { - const options = applyDefaults(config); - console.log(options); + const options = applyDefaults(config) + console.log(options) const BASE_SVG_PROPS = { version: "1.1", xmlns: "http://www.w3.org/2000/svg", @@ -85,7 +85,7 @@ function initRender( "fill-rule": "evenodd", fill: "currentColor", stroke: "currentColor", - }; + } const props = { ...BASE_SVG_PROPS, @@ -95,48 +95,48 @@ function initRender( renHeight: height as number, viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "none", - }; + } - const svg = s("svg", props); - svg.properties; - return svg; + const svg = s("svg", props) + svg.properties + return svg } async function drawNode( svg: Element, node: GenericNode | any, - config?: Partial + config?: Partial, ) { - const options = applyDefaults(config); + const options = applyDefaults(config) - let fillStyle = "rgba(255, 255, 255, .5)"; - let strokeStyle = "rgba(0,0,0,1)"; + let fillStyle = "rgba(255, 255, 255, .5)" + let strokeStyle = "rgba(0,0,0,1)" if (node.color === "1") { - fillStyle = "rgba(255, 0, 0, .5)"; - strokeStyle = "rgba(255,0,0,1)"; + fillStyle = "rgba(255, 0, 0, .5)" + strokeStyle = "rgba(255,0,0,1)" } else if (node.color === "2") { - fillStyle = "rgba(255, 100, 0, .5)"; - strokeStyle = "rgba(255,100,0,1)"; + fillStyle = "rgba(255, 100, 0, .5)" + strokeStyle = "rgba(255,100,0,1)" } else if (node.color === "3") { - fillStyle = "rgba(255, 255, 0, .5)"; - strokeStyle = "rgba(255,255,0,1)"; + fillStyle = "rgba(255, 255, 0, .5)" + strokeStyle = "rgba(255,255,0,1)" } else if (node.color === "4") { - fillStyle = "rgba(0, 255, 100, .5)"; - strokeStyle = "rgba(0,100,0,1)"; + fillStyle = "rgba(0, 255, 100, .5)" + strokeStyle = "rgba(0,100,0,1)" } else if (node.color === "5") { - fillStyle = "rgba(0, 255, 255, .5)"; - strokeStyle = "rgba(0,255,255,1)"; + fillStyle = "rgba(0, 255, 255, .5)" + strokeStyle = "rgba(0,255,255,1)" } else if (node.color === "6") { - fillStyle = "rgba(100, 10, 100, .5)"; - strokeStyle = "rgba(100,10,100,1)"; + fillStyle = "rgba(100, 10, 100, .5)" + strokeStyle = "rgba(100,10,100,1)" } - const group = s("g"); + const group = s("g") const rect = s("rect", { - x: node.x + svg.properties!.renWidth / 2, - y: node.y + svg.properties!.renHeight / 2, + x: node.x + svg.properties.renWidth / 2, + y: node.y + svg.properties.renHeight / 2, width: node.width, height: node.height, rx: 5, @@ -144,31 +144,49 @@ async function drawNode( stroke: strokeStyle, fill: fillStyle, "stroke-width": options.lineStrokeWidth, - }); + }) - group.children.push(rect); + group.children.push(rect) - drawEmbedded(svg, group, node); - drawMarkdownEmbed(svg, group, node); + drawEmbedded(svg, group, node) + drawMarkdownEmbed(svg, group, node) // ctx.fillStyle = "rgba(0, 0, 0, 1)"; if (node.label) { - s("text", { - x: node.x + 5 + svg.properties!.renWidth / 2, - y: node.y + 10 + svg.properties!.renHeight / 2, - children: node.label, - }); + const t = s( + "text", + { + x: node.x + 5 + svg.properties.renWidth / 2, + y: node.y - 10 + svg.properties.renHeight / 2, + "font-family": "monospace", + "font-size": 20, + "stroke-width": 1, + }, + node.label, + ) + group.children.push(t) } if (node.type === "text" && node.text) { - s("text", { - x: node.x + 5 + svg.properties!.renWidth / 2, - y: node.y + 40 + svg.properties!.renHeight / 2, - children: node.label, - }); + const t = s( + "text", + { + x: node.x + 5 + svg.properties.renWidth / 2, + y: + node.y + + 5 + + node.height / 2 + + svg.properties.renHeight / 2, + "font-family": "monospace", + "font-size": 20, + "stroke-width": 1, + }, + node.text, + ) + group.children.push(t) } - svg.children.push(group); + svg.children.push(group) } function drawEdge( @@ -176,65 +194,65 @@ function drawEdge( toNode: GenericNode, fromNode: GenericNode, edge: Edge | any, - config?: Partial + config?: Partial, ) { - const options = applyDefaults(config); - if (svg === null || svg == undefined) return; + const options = applyDefaults(config) + if (svg === null || svg == undefined) return - const cWidth = svg.properties.renWidth || (1 as number); - const cHeight = svg.properties.renHeight || (1 as number); + const cWidth = svg.properties.renWidth || (1 as number) + const cHeight = svg.properties.renHeight || (1 as number) if (fromNode && toNode) { let startX = fromNode.x + - (edge.fromSide == "top" || edge.fromSide == "bottom" + (edge.fromSide === "top" || edge.fromSide === "bottom" ? fromNode.width / 2 : fromNode.width) + - cWidth / 2; - let startY = fromNode.y + fromNode.height / 2 + cHeight / 2; + cWidth / 2 + let startY = fromNode.y + fromNode.height / 2 + cHeight / 2 let endX = toNode.x + - (edge.toSide == "top" || edge.toSide == "bottom" + (edge.toSide === "top" || edge.toSide === "bottom" ? toNode.width / 2 : toNode.width) + - cWidth / 2; - let endY = toNode.y + toNode.height / 2 + cHeight / 2; + cWidth / 2 + let endY = toNode.y + toNode.height / 2 + cHeight / 2 if (edge.fromSide === "left") { - startX = fromNode.x + cWidth / 2; + startX = fromNode.x + cWidth / 2 } else if (edge.fromSide === "top") { - startY = fromNode.y + cHeight / 2; + startY = fromNode.y + cHeight / 2 } else if (edge.fromSide === "bottom") { - startY = fromNode.y + fromNode.height + cHeight / 2; + startY = fromNode.y + fromNode.height + cHeight / 2 } if (edge.toSide === "right") { - endX = toNode.x + toNode.width + cWidth / 2; + endX = toNode.x + toNode.width + cWidth / 2 } else if (edge.toSide === "top") { - endY = toNode.y + cHeight / 2; + endY = toNode.y + cHeight / 2 } else if (edge.toSide === "bottom") { - endY = toNode.y + toNode.height + cHeight / 2; + endY = toNode.y + toNode.height + cHeight / 2 } else if (edge.toSide === "left") { - endX = toNode.x + cWidth / 2; + endX = toNode.x + cWidth / 2 } // Change the control point logic based on fromSide/toSide const cp1 = { x: startX, y: endY, - }; + } const cp2 = { x: endX, y: startY, - }; + } const line = s("path", { d: `M ${startX} ${startY} C ${cp1.x} ${cp1.y}, ${cp2.x} ${cp2.y}, ${endX} ${endY}`, stroke: "black", "stroke-width": options.lineStrokeWidth, fill: "none", - }); - svg.children.push(line); + }) + svg.children.push(line) } } diff --git a/src/options.ts b/src/options.ts index 4b57679..5312546 100644 --- a/src/options.ts +++ b/src/options.ts @@ -7,42 +7,42 @@ export interface Options { * * Defaults to true */ - openEmbededInNewTab: boolean; + openEmbededInNewTab: boolean /** * Define an asset path where the .canvas files exists. This will add the asset path before the filename. Otherwise uses cwd.process() path + filename * * Defaults to null */ - assetPath: string | null; + assetPath: string | null /** * Render mode. Determines the canvas output mode * * Defaults to canvas */ - renderMode: "svg" | "image" | "canvas"; + renderMode: "svg" | "image" | "canvas" /** * Canvas Buffer * * Defaults to 30 */ - canvasBuffer: number; + canvasBuffer: number /** * Canvas node stroke width * * Defaults to 3 */ - nodeStrokeWidth: number; + nodeStrokeWidth: number /** * Canvas line stroke width * * Defaults to 5 */ - lineStrokeWidth: number; + lineStrokeWidth: number } /** @@ -61,5 +61,5 @@ export function applyDefaults(config: Partial = {}): Options { config.nodeStrokeWidth === undefined ? 3 : config.nodeStrokeWidth, lineStrokeWidth: config.lineStrokeWidth === undefined ? 5 : config.lineStrokeWidth, - }; + } } diff --git a/src/plugin.ts b/src/plugin.ts index 76e730a..aaf25ab 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,14 +1,14 @@ -import path from "path"; -import fs from "fs"; -import type { Plugin } from "unified"; -import type { Element, Root } from "hast"; +import fs from "fs" +import path from "path" +import JSONCanvas from "@trbn/jsoncanvas" +import type { Element, Root } from "hast" +import { h } from "hastscript" +import type { Plugin } from "unified" // import { fromHtmlIsomorphic } from "hast-util-from-html-isomorphic"; -import { visit } from "unist-util-visit"; -import { validate, render } from "./jsoncanvas"; -import JSONCanvas from "@trbn/jsoncanvas"; -import { h } from "hastscript"; +import { visit } from "unist-util-visit" +import { render, validate } from "./jsoncanvas" -import { applyDefaults, Options } from "./options"; +import { type Options, applyDefaults } from "./options" /* Let's think this through. @@ -32,36 +32,36 @@ Things decide: export const rehypeJsonCanvas: Plugin<[], Root> = () => { return async (tree) => { - const nodesToReplace = [] as Array; + const nodesToReplace = [] as Array // Iterate over the markdown file as tree visit(tree, "element", (node, index) => { - console.log(node, index); + console.log(node, index) // only match image embeds if (node.tagName !== "img" || index === undefined) { - return; + return } - console.log("Adding", node); - nodesToReplace.push(node); + console.log("Adding", node) + nodesToReplace.push(node) // index = index += 1; - }); + }) for (const node of nodesToReplace) { - const canvasPath = node.properties.src as string; - let canvasMarkdown = await getCanvasFromEmbed(canvasPath); + const canvasPath = node.properties.src as string + const canvasMarkdown = await getCanvasFromEmbed(canvasPath) - const jsonCanvasFromString = JSONCanvas.fromString(canvasMarkdown); + const jsonCanvasFromString = JSONCanvas.fromString(canvasMarkdown) - let canvas; + let canvas if (validate(jsonCanvasFromString)) { - canvas = render(jsonCanvasFromString, {}); + canvas = render(jsonCanvasFromString, {}) } else { - canvas = h("div", "
Not a properly formatted JsonCanvas
"); + canvas = h("div", "
Not a properly formatted JsonCanvas
") } - console.log(canvas); + console.log(canvas) // const canvasHast = fromHtmlIsomorphic( // ``, @@ -71,42 +71,42 @@ export const rehypeJsonCanvas: Plugin<[], Root> = () => { // ); node.properties = { ...node.properties, - }; - node.tagName = "div"; - node.children = []; - node.children.push(canvas!); //canvasHast.children as ElementContent[]; + } + node.tagName = "div" + node.children = [] + node.children.push(canvas!) //canvasHast.children as ElementContent[]; } - }; -}; + } +} export async function getCanvasFromEmbed( markdownPath: string, - config?: Partial + config?: Partial, ): Promise { - const options = applyDefaults(config); - let canvasMarkdown = "Loading"; - const webcheck = markdownPath.trim().toLowerCase(); + const options = applyDefaults(config) + let canvasMarkdown = "Loading" + const webcheck = markdownPath.trim().toLowerCase() if (webcheck.startsWith("https://") || typeof window !== "undefined") { await fetch(markdownPath) .then((res) => res.text()) - .then((text) => (canvasMarkdown = text)); + .then((text) => (canvasMarkdown = text)) } else { // To accomodate ssr const ssrPath = options.assetPath ? path.join(process.cwd(), options.assetPath, markdownPath) - : path.join(process.cwd(), markdownPath); - console.log("File Path", ssrPath); + : path.join(process.cwd(), markdownPath) + console.log("File Path", ssrPath) try { canvasMarkdown = fs.readFileSync(ssrPath, { encoding: "utf8", flag: "r", - }); + }) } catch (err) { - console.log("No Canvas File Found. Try using the assetPath option!", err); + console.log("No Canvas File Found. Try using the assetPath option!", err) } } - if (canvasMarkdown === null) return ""; + if (canvasMarkdown === null) return "" - return canvasMarkdown; + return canvasMarkdown }