cross-social avatar

hive-markdown-editor

cross-social

Published: 27 Apr 2026 โ€บ Updated: 27 Apr 2026hive-markdown-editor

hive-markdown-editor

Hive Markdown Post Editor
๐Ÿ“

Hive Markdown Post Editor

Ready
Refresh
<div class="flex flex-col lg:flex-row h-[calc(100vh-73px)]">

    <!-- Preview Pane -->
    <div class="lg:w-1/2 border-r border-zinc-800 bg-white text-zinc-900 flex flex-col">
        <div class="px-6 py-3 border-b bg-zinc-50 flex justify-between items-center">
            <h2 class="font-semibold text-zinc-700">Preview</h2>
            <button onclick="refreshPreview()" class="text-xs px-4 py-1 border border-zinc-300 rounded-xl hover:bg-zinc-100">โ†ป Refresh</button>
        </div>
        <div id="preview" class="preview p-8 overflow-auto flex-1 prose prose-zinc max-w-none"></div>
    </div>

    <!-- Editor Pane -->
    <div class="lg:w-1/2 flex flex-col bg-zinc-900">
        <div class="flex border-b border-zinc-700">
            <button onclick="switchTab(0)" id="tab0" class="tab-active flex-1 py-4 font-medium">Body</button>
            <button onclick="switchTab(1)" id="tab1" class="flex-1 py-4 font-medium">Metadata</button>
        </div>

        <div id="tabContent0" class="flex-1 p-6 space-y-6 overflow-auto">
            <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                <div>
                    <label class="block text-xs text-zinc-400 mb-1">Author</label>
                    <input id="author" class="w-full bg-zinc-950 border border-zinc-700 rounded-xl px-4 py-3 focus:outline-none focus:border-emerald-400">
                </div>
                <div>
                    <label class="block text-xs text-zinc-400 mb-1">Permlink</label>
                    <input id="permlink" class="w-full bg-zinc-950 border border-zinc-700 rounded-xl px-4 py-3 focus:outline-none focus:border-emerald-400">
                </div>
            </div>

            <div>
                <label class="block text-xs text-zinc-400 mb-1">Title</label>
                <input id="title" class="w-full bg-zinc-950 border border-zinc-700 rounded-xl px-4 py-3 focus:outline-none focus:border-emerald-400">
            </div>

            <div>
                <label class="block text-xs text-zinc-400 mb-1">Body (Markdown)</label>
                <textarea id="body" class="w-full bg-zinc-950 border border-zinc-700 rounded-3xl px-5 py-4 h-96 font-mono text-sm focus:outline-none focus:border-emerald-400"></textarea>
            </div>
        </div>

        <div id="tabContent1" class="flex-1 p-6 overflow-auto hidden">
            <label class="block text-xs text-zinc-400 mb-1">json_metadata</label>
            <textarea id="jsonMetadata" class="w-full bg-zinc-950 border border-zinc-700 rounded-3xl px-5 py-4 h-96 font-mono text-sm focus:outline-none focus:border-emerald-400"></textarea>
            <button onclick="validateAndFormatJson()" class="mt-4 px-5 py-2 bg-zinc-800 hover:bg-zinc-700 rounded-2xl text-sm">Format & Validate JSON</button>
        </div>

        <div class="p-6 border-t border-zinc-700 bg-zinc-950 flex flex-wrap gap-3">
            <button onclick="saveWithKeychain()" class="flex-1 bg-emerald-500 hover:bg-emerald-600 text-black py-4 rounded-3xl font-medium transition">๐Ÿš€ Save to Hive (Keychain)</button>
            <button onclick="copyFullOperation()" class="px-8 bg-zinc-800 hover:bg-zinc-700 rounded-3xl text-sm font-medium">๐Ÿ“‹ Copy Operation</button>
        </div>
    </div>
</div>
// ====================== CONFIG ====================== const HIVE_NODE = "https://api.hive.blog";

// ====================== RECEIVE FROM SHIM ======================
let currentPost = null;

window.addEventListener("message", (event) => {
if (event.data && event.data.type === "hive-shim-context" && event.data.post) {
currentPost = event.data.post;
populateEditor(currentPost);
}
});

// ====================== FALLBACK FETCH ======================
async function fallbackFetch() {
const path = window.location.pathname.replace(/^/shim//, '/');
const match = path.match(//@([/]+)/([/?#]+)/);
if (!match) return;

const author = match[1];
const permlink = match[2];

document.getElementById("author").value = author;
document.getElementById("permlink").value = permlink;

try {
    const res = await fetch(HIVE_NODE, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
            jsonrpc: "2.0",
            method: "condenser_api.get_content",
            params: [author, permlink],
            id: 1
        })
    });
    const data = await res.json();
    const post = data.result;
    if (post && post.body) {
        populateEditor(post);
    }
} catch (e) {
    console.error(e);
}

}

function populateEditor(post) {
if (!post) return;

document.getElementById("author").value = post.author || "";
document.getElementById("permlink").value = post.permlink || "";
document.getElementById("title").value = post.title || "";
document.getElementById("body").value = post.body || "";

let meta = {};
try { meta = JSON.parse(post.json_metadata || "{}"); } catch(e) {}
document.getElementById("jsonMetadata").value = JSON.stringify(meta, null, 2);

renderPreview(post.body);
logStatus("Post loaded โœ“", "success");

}

// ====================== UTILITIES ======================
function logStatus(msg, type = "info") {
const el = document.getElementById("status");
el.textContent = msg;
if (type === "success") el.className = "px-4 py-1.5 rounded-2xl text-sm font-medium bg-emerald-400 text-black";
else if (type === "error") el.className = "px-4 py-1.5 rounded-2xl text-sm font-medium bg-red-500 text-white";
setTimeout(() => el.className = "px-4 py-1.5 rounded-2xl text-sm font-medium bg-zinc-800 text-emerald-400", 4000);
}

function renderPreview(body) {
const el = document.getElementById("preview");
try {
el.innerHTML = marked.parse(body || "", { gfm: true, breaks: true, tables: true });
} catch (e) {
el.innerHTML = <p class="text-red-500">Preview error</p>;
}
}

function refreshPreview() {
renderPreview(document.getElementById("body").value);
}

function refreshAll() {
location.reload();
}

function switchTab(n) {
document.getElementById("tabContent0").classList.toggle("hidden", n !== 0);
document.getElementById("tabContent1").classList.toggle("hidden", n !== 1);
document.getElementById("tab0").classList.toggle("tab-active", n === 0);
document.getElementById("tab1").classList.toggle("tab-active", n === 1);
}

function validateAndFormatJson() {
const ta = document.getElementById("jsonMetadata");
try {
ta.value = JSON.stringify(JSON.parse(ta.value), null, 2);
logStatus("JSON formatted", "success");
} catch (e) {
logStatus("Invalid JSON", "error");
}
}

async function saveWithKeychain() {
if (!window.hive_keychain) return alert("Hive Keychain not detected");

const author = document.getElementById("author").value.trim();
const permlink = document.getElementById("permlink").value.trim();
const title = document.getElementById("title").value.trim();
const body = document.getElementById("body").value.trim();
const jsonMetadata = document.getElementById("jsonMetadata").value.trim();

if (!author || !permlink || !body) {
    logStatus("Author, permlink and body are required", "error");
    return;
}

const operation = ["comment", {
    parent_author: "",
    parent_permlink: "hive",
    author: author,
    permlink: permlink,
    title: title,
    body: body,
    json_metadata: jsonMetadata
}];

window.hive_keychain.requestBroadcast(author, [operation], "Posting", (res) => {
    if (res.success) logStatus("โœ… Saved to Hive!", "success");
    else logStatus("โŒ Save failed", "error");
});

}

function copyFullOperation() {
const op = {
parent_author: "",
parent_permlink: "hive",
author: document.getElementById("author").value.trim(),
permlink: document.getElementById("permlink").value.trim(),
title: document.getElementById("title").value.trim(),
body: document.getElementById("body").value.trim(),
json_metadata: document.getElementById("jsonMetadata").value.trim()
};
navigator.clipboard.writeText(JSON.stringify(op, null, 2)).then(() => {
logStatus("โœ… Operation copied to clipboard", "success");
});
}

// ====================== INIT ======================
window.addEventListener('load', () => {
tailwind.config = { content: [] };
// Try to get data from shim first, fallback to direct fetch if needed
setTimeout(fallbackFetch, 800);

// Live preview
document.getElementById("body").addEventListener("input", () => renderPreview(document.getElementById("body").value));

});

Leave hive-markdown-editor to:

Written by

Read more #shim posts


Best Posts From cross-social

We have not curated any of cross-social's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.

More Posts From cross-social