Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.17.0 · main

Markdown

tier 1 — the frame

Two independent pieces: .markdown-body styles rendered Markdown wherever it appears, and .md-editor is a toolbar + textarea + live preview driven by sednaUi.md.

.markdown-body

Put it on the container holding rendered HTML. It normalises headings, lists, code, quotes and links onto the tokens, and strips the first and last margin so it sits flush in a card body.

Escalation policy

Raise to P2 when either condition holds:

  • More than 20 users are affected
  • A VIP flag is set on the caller
Never raise priority and reroute in the same decision — the receiving group loses the audit trail.
if (affected > 20 || caller.IsVip)
priority = Priority.P2;

See the routing matrix for target groups.

<div class="card">
    <div class="card-body">
        <div class="markdown-body">
            <h2>Escalation policy</h2>
            <p>Raise to <strong>P2</strong> when <em>either</em> condition holds:</p>
            <ul>
                <li>More than 20 users are affected</li>
                <li>A <code>VIP</code> flag is set on the caller</li>
            </ul>
            <blockquote>Never raise priority and reroute in the same decision — the receiving group loses the audit trail.</blockquote>
            <pre><code>if (affected &gt; 20 || caller.IsVip)
priority = Priority.P2;</code></pre>
            <p>See the <a href="#">routing matrix</a> for target groups.</p>
        </div>
    </div>
</div>

Tables and nested lists

A table in rendered Markdown gets .table's own borders and header treatment without the class, and nested lists indent one step per level. The HTML here comes from somewhere else, so a long <code> breaks inside a word and a wide table scrolls rather than widening the page.

Release 0.5.0

Adds the live-state family. Nothing was renamed, so an upgrade needs no app changes.

ClassLayerWhat it is for
.health-badgesedna.paintA connection's state, as a dot and a word
.outputsedna.paintLines of machine output, with follow-tail
.stalesedna.paintHow old the figure beside it is

Before bumping:

  1. Grep your own stylesheets for the names above.
  2. Check each one you already style — both rule sets will apply.
    • Yours wins, because your stylesheet is unlayered.
    • The library's declarations you have not set still land.
  3. Rename yours, or delete it if the library's does the job.
<div class="markdown-body">
    <h2>Release 0.5.0</h2>
    <p>Adds the live-state family. Nothing was renamed, so an upgrade needs no app changes.</p>
    <table>
        <thead>
            <tr><th>Class</th><th>Layer</th><th>What it is for</th></tr>
        </thead>
        <tbody>
            <tr><td><code>.health-badge</code></td><td>sedna.paint</td><td>A connection's state, as a dot and a word</td></tr>
            <tr><td><code>.output</code></td><td>sedna.paint</td><td>Lines of machine output, with follow-tail</td></tr>
            <tr><td><code>.stale</code></td><td>sedna.paint</td><td>How old the figure beside it is</td></tr>
        </tbody>
    </table>
    <p>Before bumping:</p>
    <ol>
        <li>Grep your own stylesheets for the names above.</li>
        <li>Check each one you already style &mdash; both rule sets will apply.
            <ul>
                <li>Yours wins, because your stylesheet is unlayered.</li>
                <li>The library's declarations you have not set still land.</li>
            </ul>
        </li>
        <li>Rename yours, or delete it if the library's does the job.</li>
    </ol>
</div>

.md-editor

Live below — type in it, use the toolbar, then switch to Preview. The wiring is data-md-view on the root, data-md-input on the textarea, data-md-preview on the preview pane, data-md-cmd on toolbar buttons and data-md-tab on each radio of the .segmented Write/Preview switch; the markup sits inert until sednaUi.md.init() runs — from C#, ISednaUi.InitMarkdownAsync() in OnAfterRenderAsync, which wires every editor in the document and is idempotent per editor. In Blazor, bind the textarea with @bind:event="oninput" and let it own the value; toolbar edits dispatch a bubbling input event so the binding picks them up.

<div class="md-editor" data-md-view="write" id="demo-md">
    <div class="md-toolbar">
        <button class="md-tool" type="button" data-md-cmd="bold" aria-label="Bold" data-tip="Wrap the selection in ** **."><i class="ri-bold"></i></button>
        <button class="md-tool" type="button" data-md-cmd="italic" aria-label="Italic"><i class="ri-italic"></i></button>
        <button class="md-tool" type="button" data-md-cmd="code" aria-label="Code"><i class="ri-code-line"></i></button>
        <button class="md-tool" type="button" data-md-cmd="h2" aria-label="Heading"><i class="ri-h-2"></i></button>
        <button class="md-tool" type="button" data-md-cmd="ul" aria-label="Bullet list"><i class="ri-list-unordered"></i></button>
        <button class="md-tool" type="button" data-md-cmd="ol" aria-label="Numbered list"><i class="ri-list-ordered"></i></button>
        <button class="md-tool" type="button" data-md-cmd="quote" aria-label="Quote"><i class="ri-quote-text"></i></button>
        <button class="md-tool" type="button" data-md-cmd="link" aria-label="Link"><i class="ri-link"></i></button>
        <div class="segmented" role="group" aria-label="Editor view">
            <label class="segmented-option">
                <input type="radio" data-md-tab="write" checked /> Write</label>
            <label class="segmented-option">
                <input type="radio" data-md-tab="preview" /> Preview</label>
        </div>
    </div>
    <textarea class="form-input md-input" data-md-input rows="8" aria-label="Markdown source">## Escalation policy

Raise to **P2** when _either_ condition holds:

- More than 20 users are affected
- A `VIP` flag is set on the caller

> Never raise priority and reroute in the same decision.
</textarea>
    <div class="markdown-body md-preview" data-md-preview></div>
</div>

As a form field

Live below. The editor in place: a .form-label pointing at the textarea's own id, a .form-hint reached by aria-describedby, and the .form-actions row. Label the textarea, not the .md-editor wrapper — the textarea is the control — and leave out the data-md-cmd buttons the field does not need.

Kept on the order and shown in its history. Markdown is rendered.

<div class="form-field" style="max-width:560px">
    <label class="form-label" for="md-reason">Reason for the decision</label>
    <div class="md-editor" data-md-view="write" id="reason-md">
        <div class="md-toolbar">
            <button class="md-tool" type="button" data-md-cmd="bold" aria-label="Bold"><i class="ri-bold"></i></button>
            <button class="md-tool" type="button" data-md-cmd="code" aria-label="Code"><i class="ri-code-line"></i></button>
            <button class="md-tool" type="button" data-md-cmd="ul" aria-label="Bullet list"><i class="ri-list-unordered"></i></button>
            <button class="md-tool" type="button" data-md-cmd="link" aria-label="Link"><i class="ri-link"></i></button>
            <div class="segmented" role="group" aria-label="Editor view">
                <label class="segmented-option">
                    <input type="radio" data-md-tab="write" checked /> Write</label>
                <label class="segmented-option">
                    <input type="radio" data-md-tab="preview" /> Preview</label>
            </div>
        </div>
        <textarea class="form-input md-input" id="md-reason" data-md-input rows="5"
                  aria-describedby="md-reason-hint">Dispatching the three reserved lines now.

- Lines 4 and 5 are out of stock until Monday
- The customer accepted a split delivery
</textarea>
        <div class="markdown-body md-preview" data-md-preview></div>
    </div>
    <p class="form-hint" id="md-reason-hint">Kept on the order and shown in its history. Markdown is rendered.</p>
    <div class="form-actions">
        <button class="btn" type="button">Discard</button>
        <button class="btn btn-primary" type="button">Save the reason</button>
    </div>
</div>
Sanitise server-side as well sednaUi.md.render() escapes all HTML first and only re-introduces a fixed set of constructs, with link hrefs restricted to http:, https:, mailto: and root-relative paths. It is not a spec-complete parser: for untrusted input rendered to other users, sanitise server-side as well.