From d79602478058c57f318e90b3e3cc3f28ca7b4697 Mon Sep 17 00:00:00 2001 From: SUNIL PARISI Date: Mon, 22 Jun 2026 19:21:00 +0000 Subject: [PATCH] Refactor duplicated code patterns into shared utilities - Extract repeated inline SVG styles into css/styles.css - Create js/diagram-utils.js: shared factories for SVG markers, actors, messages, edge paths, and flow nodes (eliminates 4+ duplicated arrowhead defs, 6 duplicated actor blocks, and repeated edgePath boilerplate) - Create js/toc-generator.js: auto-generates TOC from headings instead of manually duplicating every heading title in sidebar - Refactor Welcome file.md to use shared utilities via data-driven rendering instead of copy-pasted inline SVG Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- Welcome file.md | 230 ++++++++++++++++++++++++++------- css/styles.css | 151 ++++++++++++++++++++++ js/diagram-utils.js | 308 ++++++++++++++++++++++++++++++++++++++++++++ js/toc-generator.js | 100 ++++++++++++++ 4 files changed, 745 insertions(+), 44 deletions(-) create mode 100644 css/styles.css create mode 100644 js/diagram-utils.js create mode 100644 js/toc-generator.js diff --git a/Welcome file.md b/Welcome file.md index fdfe23c..effa03e 100644 --- a/Welcome file.md +++ b/Welcome file.md @@ -6,53 +6,21 @@ Welcome file + + +

Welcome to StackEdit!

-

Hi! I’m your first Markdown file in StackEdit. If you want to learn about StackEdit, you can read me. If you want to play with Markdown, you can edit me. If you have finished with me, you can just create new files by opening the file explorer on the left corner of the navigation bar.
+

Hi! I'm your first Markdown file in StackEdit. If you want to learn about StackEdit, you can read me. If you want to play with Markdown, you can edit me. If you have finished with me, you can just create new files by opening the file explorer on the left corner of the navigation bar.
Google

Files

Hello
@@ -97,7 +65,7 @@ StackEdit stores your files in your browser, which means all your files are auto

Manage file synchronization

Since one file can be synced with multiple locations, you can list and manage synchronized locations by clicking File synchronization in the Synchronize sub-menu. This allows you to list and remove synchronized locations that are linked to your file.

Publication

-

Publishing in StackEdit makes it simple for you to publish online your files. Once you’re happy with a file, you can publish it to different hosting platforms like Blogger, Dropbox, Gist, GitHub, Google Drive, WordPress and Zendesk. With Handlebars templates, you have full control over what you export.

+

Publishing in StackEdit makes it simple for you to publish online your files. Once you're happy with a file, you can publish it to different hosting platforms like Blogger, Dropbox, Gist, GitHub, Google Drive, WordPress and Zendesk. With Handlebars templates, you have full control over what you export.

Before starting to publish, you must link an account in the Publish sub-menu.

@@ -120,7 +88,7 @@ StackEdit stores your files in your browser, which means all your files are auto

ProTip: You can disable any Markdown extension in the File properties dialog.

SmartyPants

-

SmartyPants converts ASCII punctuation characters into “smart” typographic punctuation HTML entities. For example:

+

SmartyPants converts ASCII punctuation characters into "smart" typographic punctuation HTML entities. For example:

@@ -134,12 +102,12 @@ StackEdit stores your files in your browser, which means all your files are auto - + - + @@ -152,18 +120,192 @@ StackEdit stores your files in your browser, which means all your files are auto

The Gamma function satisfying Γ(n)=(n1)!nN\Gamma(n) = (n-1)!\quad\forall n\in\mathbb N is via the Euler integral

Γ(z)=0tz1etdt. \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. -

+

You can find more information about LaTeX mathematical expressions here.

UML diagrams

You can render UML diagrams using Mermaid. For example, this will produce a sequence diagram:

-
AliceBobJohnHello Bob, how are you?How about you John?I am good thanks!I am good thanks!Bob thinks a longlong time, so longthat the text doesnot fit on a row.Checking with John...Yes... John, how are you?AliceBobJohn
+ +

And this will produce a flow chart:

-
Link text
Square Rect
Circle
Round Rect
Rhombus
+ +
+ + + + diff --git a/css/styles.css b/css/styles.css new file mode 100644 index 0000000..0d2cdd0 --- /dev/null +++ b/css/styles.css @@ -0,0 +1,151 @@ +/* Shared styles extracted from inline duplications */ + +/* Layout */ +.stackedit__left { + position: fixed; + left: 0; + top: 0; + width: 250px; + height: 100%; + overflow-y: auto; + padding: 1em; + box-sizing: border-box; +} + +.stackedit__right { + margin-left: 250px; + padding: 1em 2em; +} + +/* Table of Contents */ +.stackedit__toc ul { + list-style: none; + padding-left: 1em; +} + +.stackedit__toc > ul { + padding-left: 0; +} + +.stackedit__toc a { + text-decoration: none; + color: #333; +} + +.stackedit__toc a:hover { + color: #0366d6; +} + +/* Tables */ +table { + border-collapse: collapse; + margin: 1em 0; +} + +table th, +table td { + border: 1px solid #ddd; + padding: 0.5em 1em; + text-align: left; +} + +table th { + background-color: #f6f8fa; +} + +/* SVG Diagram shared styles - eliminates repeated inline style attributes */ +.mermaid svg { + display: block; + margin: 1em auto; +} + +.mermaid .actor-line { + stroke-width: 0.5px; + stroke: #999; +} + +.mermaid .actor rect { + fill: #eaeaea; + stroke: #666; + rx: 3; + ry: 3; +} + +.mermaid .actor text { + text-anchor: middle; + dominant-baseline: central; + alignment-baseline: central; +} + +.mermaid .messageText { + text-anchor: middle; +} + +.mermaid .messageLine0, +.mermaid .messageLine1 { + stroke-width: 1.5; + stroke: #333; + fill: none; +} + +.mermaid .messageLine1 { + stroke-dasharray: 3, 3; +} + +.mermaid .loopLine { + stroke-width: 2; + stroke: #526e76; + fill: none; +} + +.mermaid .loopText { + fill: #526e76; + font-size: 12.5px; +} + +.mermaid .labelText { + fill: #131300; +} + +/* Flow chart shared styles */ +.mermaid .edgePath .path { + fill: none; + stroke-width: 1; +} + +.mermaid .node rect, +.mermaid .node polygon { + stroke-width: 1; +} + +.mermaid .label { + color: #333; +} + +/* KaTeX shared styles - eliminates repeated inline katex spans */ +.katex--inline { + display: inline; +} + +.katex--display { + display: block; + text-align: center; + margin: 1em 0; +} + +.katex .strut { + display: inline-block; + height: 0.75em; +} + +.katex .strut.bottom { + vertical-align: -0.25em; +} + +.katex .mspace.quad { + width: 1em; +} + +.katex .mspace.thinspace { + width: 0.16667em; +} diff --git a/js/diagram-utils.js b/js/diagram-utils.js new file mode 100644 index 0000000..569969c --- /dev/null +++ b/js/diagram-utils.js @@ -0,0 +1,308 @@ +/** + * Shared diagram utilities. + * + * Eliminates duplicated SVG marker definitions, actor blocks, and edge-path + * boilerplate that was previously copy-pasted across every inline diagram. + */ + +var DiagramUtils = (function () { + 'use strict'; + + var SVG_NS = 'http://www.w3.org/2000/svg'; + + // ─── Shared SVG Marker Definitions ────────────────────────────────── + // Previously each diagram redefined identical blocks. + + /** + * Create a reusable arrowhead marker element. + * @param {string} id - Unique marker ID + * @param {object} [opts] - Optional overrides for marker dimensions + * @returns {SVGElement} + */ + function createArrowheadMarker(id, opts) { + opts = opts || {}; + var marker = document.createElementNS(SVG_NS, 'marker'); + marker.setAttribute('id', id); + marker.setAttribute('refX', opts.refX || '5'); + marker.setAttribute('refY', opts.refY || '2'); + marker.setAttribute('markerWidth', opts.markerWidth || '6'); + marker.setAttribute('markerHeight', opts.markerHeight || '4'); + marker.setAttribute('orient', 'auto'); + + var path = document.createElementNS(SVG_NS, 'path'); + path.setAttribute('d', opts.path || 'M 0,0 V 4 L6,2 Z'); + marker.appendChild(path); + + return marker; + } + + /** + * Create a crosshead marker element (used in sequence diagrams). + * @param {string} id - Unique marker ID + * @returns {SVGElement} + */ + function createCrossheadMarker(id) { + var marker = document.createElementNS(SVG_NS, 'marker'); + marker.setAttribute('id', id); + marker.setAttribute('markerWidth', '15'); + marker.setAttribute('markerHeight', '8'); + marker.setAttribute('orient', 'auto'); + marker.setAttribute('refX', '16'); + marker.setAttribute('refY', '4'); + + var arrow = document.createElementNS(SVG_NS, 'path'); + arrow.setAttribute('fill', 'black'); + arrow.setAttribute('stroke', '#000000'); + arrow.setAttribute('stroke-width', '1px'); + arrow.setAttribute('d', 'M 9,2 V 6 L16,4 Z'); + arrow.setAttribute('style', 'stroke-dasharray: 0, 0;'); + marker.appendChild(arrow); + + var cross = document.createElementNS(SVG_NS, 'path'); + cross.setAttribute('fill', 'none'); + cross.setAttribute('stroke', '#000000'); + cross.setAttribute('stroke-width', '1px'); + cross.setAttribute('d', 'M 0,1 L 6,7 M 6,1 L 0,7'); + cross.setAttribute('style', 'stroke-dasharray: 0, 0;'); + marker.appendChild(cross); + + return marker; + } + + /** + * Inject shared marker definitions into an SVG element once, + * rather than duplicating per edge path. + * @param {SVGElement} svg - Target SVG element + * @param {string[]} markerIds - Array of marker IDs to create + * @param {string} [type] - 'arrowhead' (default) or 'flowchart' + */ + function injectSharedDefs(svg, markerIds, type) { + var defs = document.createElementNS(SVG_NS, 'defs'); + + markerIds.forEach(function (id) { + var marker; + if (type === 'flowchart') { + marker = createArrowheadMarker(id, { + refX: '9', + refY: '5', + markerWidth: '8', + markerHeight: '6', + path: 'M 0 0 L 10 5 L 0 10 z' + }); + marker.setAttribute('viewBox', '0 0 10 10'); + marker.setAttribute('markerUnits', 'strokeWidth'); + } else { + marker = createArrowheadMarker(id); + } + defs.appendChild(marker); + }); + + svg.insertBefore(defs, svg.firstChild); + } + + // ─── Sequence Diagram Actor Factory ───────────────────────────────── + // Previously each actor was 3 nearly-identical element groups. + + /** + * Create an actor group (line + rect + text label) for a sequence diagram. + * @param {object} config + * @param {string} config.id - Actor line element ID + * @param {number} config.x - Center X position + * @param {number} config.yStart - Line start Y + * @param {number} config.yEnd - Line end Y + * @param {string} config.name - Display label + * @param {number} [config.rectWidth=150] - Box width + * @param {number} [config.rectHeight=65] - Box height + * @returns {SVGElement} + */ + function createActor(config) { + var g = document.createElementNS(SVG_NS, 'g'); + var rectWidth = config.rectWidth || 150; + var rectHeight = config.rectHeight || 65; + var rectX = config.x - rectWidth / 2; + + // Actor line + var line = document.createElementNS(SVG_NS, 'line'); + line.setAttribute('id', config.id); + line.setAttribute('x1', config.x); + line.setAttribute('y1', config.yStart); + line.setAttribute('x2', config.x); + line.setAttribute('y2', config.yEnd); + line.setAttribute('class', 'actor-line'); + g.appendChild(line); + + // Actor box + var rect = document.createElementNS(SVG_NS, 'rect'); + rect.setAttribute('x', rectX); + rect.setAttribute('y', '0'); + rect.setAttribute('fill', '#eaeaea'); + rect.setAttribute('stroke', '#666'); + rect.setAttribute('width', rectWidth); + rect.setAttribute('height', rectHeight); + rect.setAttribute('rx', '3'); + rect.setAttribute('ry', '3'); + rect.setAttribute('class', 'actor'); + g.appendChild(rect); + + // Actor label + var text = document.createElementNS(SVG_NS, 'text'); + text.setAttribute('x', config.x); + text.setAttribute('y', rectHeight / 2); + text.setAttribute('dominant-baseline', 'central'); + text.setAttribute('alignment-baseline', 'central'); + text.setAttribute('class', 'actor'); + text.setAttribute('style', 'text-anchor: middle;'); + + var tspan = document.createElementNS(SVG_NS, 'tspan'); + tspan.setAttribute('x', config.x); + tspan.setAttribute('dy', '0'); + tspan.textContent = config.name; + text.appendChild(tspan); + + g.appendChild(text); + return g; + } + + // ─── Message Line Factory ────────────────────────────────────────── + // Previously each message duplicated line + text elements with the same attrs. + + /** + * Create a message (line + label) between two actors. + * @param {object} config + * @param {number} config.x1 - Start X + * @param {number} config.x2 - End X + * @param {number} config.y - Y position + * @param {string} config.text - Message label + * @param {string} [config.lineClass='messageLine0'] - CSS class + * @param {string} [config.markerEnd] - Marker reference URL + * @returns {SVGElement} + */ + function createMessage(config) { + var g = document.createElementNS(SVG_NS, 'g'); + + var text = document.createElementNS(SVG_NS, 'text'); + text.setAttribute('x', (config.x1 + config.x2) / 2); + text.setAttribute('y', config.y - 7); + text.setAttribute('class', 'messageText'); + text.setAttribute('style', 'text-anchor: middle;'); + text.textContent = config.text; + g.appendChild(text); + + var line = document.createElementNS(SVG_NS, 'line'); + line.setAttribute('x1', config.x1); + line.setAttribute('y1', config.y); + line.setAttribute('x2', config.x2); + line.setAttribute('y2', config.y); + line.setAttribute('class', config.lineClass || 'messageLine0'); + if (config.markerEnd) { + line.setAttribute('marker-end', config.markerEnd); + } + g.appendChild(line); + + return g; + } + + // ─── Flow Chart Edge Factory ──────────────────────────────────────── + // Previously each edge path duplicated + . + + /** + * Create an edge path for a flow chart (replaces duplicated edgePath groups). + * @param {object} config + * @param {string} config.d - SVG path data + * @param {string} config.markerId - ID of the arrowhead marker to reference + * @returns {SVGElement} + */ + function createEdgePath(config) { + var g = document.createElementNS(SVG_NS, 'g'); + g.setAttribute('class', 'edgePath'); + g.setAttribute('style', 'opacity: 1;'); + + var path = document.createElementNS(SVG_NS, 'path'); + path.setAttribute('class', 'path'); + path.setAttribute('d', config.d); + path.setAttribute('marker-end', 'url(#' + config.markerId + ')'); + path.setAttribute('style', 'fill:none'); + g.appendChild(path); + + return g; + } + + // ─── Flow Chart Node Factory ──────────────────────────────────────── + // Previously each node duplicated rect/polygon + text with similar styles. + + /** + * Create a flow chart node. + * @param {object} config + * @param {string} config.id - Node ID + * @param {string} config.label - Display text + * @param {string} [config.shape='rect'] - 'rect', 'diamond', or 'round' + * @param {number} config.x - Center X + * @param {number} config.y - Center Y + * @param {number} [config.width] - Node width + * @param {number} [config.height] - Node height + * @returns {SVGElement} + */ + function createFlowNode(config) { + var g = document.createElementNS(SVG_NS, 'g'); + g.setAttribute('class', 'node'); + g.setAttribute('id', config.id); + g.setAttribute('style', 'opacity: 1;'); + g.setAttribute('transform', + 'translate(' + config.x + ',' + config.y + ')'); + + var shape; + var width = config.width || 80; + var height = config.height || 40; + + if (config.shape === 'diamond') { + shape = document.createElementNS(SVG_NS, 'polygon'); + var hw = width / 2; + var hh = height / 2; + shape.setAttribute('points', + '0,-' + hh + ' ' + hw + ',0 0,' + hh + ' -' + hw + ',0'); + shape.setAttribute('class', 'label-container'); + shape.setAttribute('style', 'fill: #ececff; stroke: #9370db;'); + } else if (config.shape === 'round') { + shape = document.createElementNS(SVG_NS, 'rect'); + shape.setAttribute('x', -width / 2); + shape.setAttribute('y', -height / 2); + shape.setAttribute('width', width); + shape.setAttribute('height', height); + shape.setAttribute('rx', '5'); + shape.setAttribute('ry', '5'); + shape.setAttribute('class', 'label-container'); + shape.setAttribute('style', 'fill: #ececff; stroke: #9370db;'); + } else { + shape = document.createElementNS(SVG_NS, 'rect'); + shape.setAttribute('x', -width / 2); + shape.setAttribute('y', -height / 2); + shape.setAttribute('width', width); + shape.setAttribute('height', height); + shape.setAttribute('class', 'label-container'); + shape.setAttribute('style', 'fill: #ececff; stroke: #9370db;'); + } + g.appendChild(shape); + + var text = document.createElementNS(SVG_NS, 'text'); + text.setAttribute('style', 'text-anchor: middle;'); + var tspan = document.createElementNS(SVG_NS, 'tspan'); + tspan.setAttribute('class', 'nodeLabel'); + tspan.textContent = config.label; + text.appendChild(tspan); + g.appendChild(text); + + return g; + } + + // ─── Public API ───────────────────────────────────────────────────── + + return { + createArrowheadMarker: createArrowheadMarker, + createCrossheadMarker: createCrossheadMarker, + injectSharedDefs: injectSharedDefs, + createActor: createActor, + createMessage: createMessage, + createEdgePath: createEdgePath, + createFlowNode: createFlowNode + }; +})(); diff --git a/js/toc-generator.js b/js/toc-generator.js new file mode 100644 index 0000000..d38eb6b --- /dev/null +++ b/js/toc-generator.js @@ -0,0 +1,100 @@ +/** + * Table of Contents generator utility. + * + * Eliminates the duplicated
  • Title
  • pattern + * that was repeated for every heading in the navigation sidebar. + * Instead, the TOC is generated from a declarative data structure. + */ + +var TocGenerator = (function () { + 'use strict'; + + /** + * @typedef {object} TocEntry + * @property {string} id - The heading anchor ID + * @property {string} title - The display text + * @property {TocEntry[]} [children] - Sub-entries (nested list) + */ + + /** + * Build a nested
      element from a TOC data array. + * @param {TocEntry[]} entries - Array of TOC entries + * @returns {HTMLUListElement} + */ + function buildList(entries) { + var ul = document.createElement('ul'); + + entries.forEach(function (entry) { + var li = document.createElement('li'); + var a = document.createElement('a'); + a.href = '#' + entry.id; + a.textContent = entry.title; + li.appendChild(a); + + if (entry.children && entry.children.length > 0) { + li.appendChild(buildList(entry.children)); + } + + ul.appendChild(li); + }); + + return ul; + } + + /** + * Generate and inject the TOC into a container element. + * @param {string|HTMLElement} container - Selector or element for the TOC + * @param {TocEntry[]} entries - TOC structure + */ + function generate(container, entries) { + var el = typeof container === 'string' + ? document.querySelector(container) + : container; + + if (!el) return; + el.innerHTML = ''; + el.appendChild(buildList(entries)); + } + + /** + * Auto-generate TOC from headings within a content container. + * Eliminates manual duplication of heading titles in the sidebar. + * @param {string} contentSelector - CSS selector for the content area + * @param {string} tocSelector - CSS selector for the TOC container + */ + function generateFromHeadings(contentSelector, tocSelector) { + var content = document.querySelector(contentSelector); + var tocContainer = document.querySelector(tocSelector); + if (!content || !tocContainer) return; + + var headings = content.querySelectorAll('h1, h2, h3'); + var entries = []; + var stack = [{ children: entries, level: 0 }]; + + for (var i = 0; i < headings.length; i++) { + var h = headings[i]; + var level = parseInt(h.tagName.charAt(1), 10); + var entry = { + id: h.id, + title: h.textContent, + children: [] + }; + + while (stack.length > 1 && stack[stack.length - 1].level >= level) { + stack.pop(); + } + + stack[stack.length - 1].children.push(entry); + stack.push({ children: entry.children, level: level }); + } + + tocContainer.innerHTML = ''; + tocContainer.appendChild(buildList(entries)); + } + + return { + buildList: buildList, + generate: generate, + generateFromHeadings: generateFromHeadings + }; +})();
    Single backticks 'Isn't this fun?'‘Isn’t this fun?’'Isn't this fun?'
    Quotes "Isn't this fun?"“Isn’t this fun?”"Isn't this fun?"
    Dashes