From 48ac1249c8228c32430b8f0f9231702583ae0fb7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 3 Aug 2026 01:24:54 -0400 Subject: [PATCH] feat: slideout editor new new new! --- bliss-cli/live-editor/inspector.js | 3 + bliss-cli/live-editor/install.js | 79 ++++++ bliss-cli/live-editor/route-editor.eta | 28 +++ bliss-cli/live-editor/route-editor.js | 8 + bliss-cli/live-editor/slideout.eta | 76 ++++++ bliss-cli/live-editor/template-editor.eta | 25 ++ bliss-cli/live-editor/template-editor.js | 8 + db.js | 47 ++++ index.js | 27 +- public/js/bliss_inspector.js | 293 ++++++++++++++-------- 10 files changed, 482 insertions(+), 112 deletions(-) create mode 100644 bliss-cli/live-editor/inspector.js create mode 100644 bliss-cli/live-editor/install.js create mode 100644 bliss-cli/live-editor/route-editor.eta create mode 100644 bliss-cli/live-editor/route-editor.js create mode 100644 bliss-cli/live-editor/slideout.eta create mode 100644 bliss-cli/live-editor/template-editor.eta create mode 100644 bliss-cli/live-editor/template-editor.js diff --git a/bliss-cli/live-editor/inspector.js b/bliss-cli/live-editor/inspector.js new file mode 100644 index 0000000..b4f228d --- /dev/null +++ b/bliss-cli/live-editor/inspector.js @@ -0,0 +1,3 @@ +function handler(req, res) { + res.render("inspector/slideout", {}); +} diff --git a/bliss-cli/live-editor/install.js b/bliss-cli/live-editor/install.js new file mode 100644 index 0000000..d7eebcd --- /dev/null +++ b/bliss-cli/live-editor/install.js @@ -0,0 +1,79 @@ +#!/usr/bin/env node + +// Install/update the live editor using Bliss's public CLI workflow. This file +// intentionally does not open the Bliss database directly. +const { spawnSync } = require("node:child_process"); +const path = require("node:path"); + +const repo = path.resolve(__dirname, "../.."); +const cli = path.join(repo, "bliss-cli/bliss"); + +function bliss(...args) { + const result = spawnSync(process.execPath, [cli, ...args], { + cwd: repo, + env: process.env, + encoding: "utf8", + }); + if (result.status !== 0) throw new Error(result.stderr || result.stdout); + return result.stdout.trim(); +} + +function json(...args) { + return JSON.parse(bliss(...args)); +} + +const structures = json("structures"); +let structure = structures.find((item) => item.name === "__bliss_live_editor"); +if (!structure) { + const created = json("create-structure", "__bliss_live_editor"); + structure = { id: created.id }; +} + +const structureId = String(structure.id); +bliss("update-settings", structureId, "--route-prefix", "/_bliss"); +let state = json("structure", structureId); +if (!state.dbs.some((db) => String(db.id) === "0" && db.alias === "bliss")) { + bliss("attach-db", structureId, "0", "bliss"); +} + +const templates = [ + ["inspector/slideout", "slideout.eta"], + ["inspector/route_editor", "route-editor.eta"], + ["inspector/template_editor", "template-editor.eta"], +]; +for (const [name, filename] of templates) { + let template = state.templates.find((item) => item.name === name); + if (!template) { + template = json("create-template", structureId, name); + } + bliss( + "update-template", + structureId, + String(template.id), + "--content-file", + path.join(__dirname, filename), + ); +} + +const routes = [ + ["/inspector", "inspector.js"], + ["/editor/route/:id", "route-editor.js"], + ["/editor/template/:id", "template-editor.js"], +]; +for (const [routePath, filename] of routes) { + let route = state.routes.find( + (item) => item.verb === "GET" && item.path === routePath, + ); + if (!route) { + route = json("create-route", structureId, "GET", routePath); + } + bliss( + "update-route", + structureId, + String(route.id), + "--handler-file", + path.join(__dirname, filename), + ); +} + +console.log(`live editor installed as Structure ${structureId}`); diff --git a/bliss-cli/live-editor/route-editor.eta b/bliss-cli/live-editor/route-editor.eta new file mode 100644 index 0000000..212859e --- /dev/null +++ b/bliss-cli/live-editor/route-editor.eta @@ -0,0 +1,28 @@ +
+
<%= it.sourceRoute.verb %> <%= it.sourceRoute.path %>
+ <% if (it.sourceRoute.verb !== "GET") { %> +
Rerunning this <%= it.sourceRoute.verb %> request can repeat writes or external side effects. Bliss will not rerun it automatically.
+ <% } %> +
<%= it.sourceRoute.handler %>
+ +
+ diff --git a/bliss-cli/live-editor/route-editor.js b/bliss-cli/live-editor/route-editor.js new file mode 100644 index 0000000..7b1a13d --- /dev/null +++ b/bliss-cli/live-editor/route-editor.js @@ -0,0 +1,8 @@ +function handler(req, res) { + const sql = require("db")("bliss").sql; + const sourceRoute = sql + .prepare("SELECT * FROM routes WHERE id = ?") + .get(req.params.id); + if (!sourceRoute) return res.status(404).send("Route not found"); + res.render("inspector/route_editor", { sourceRoute }); +} diff --git a/bliss-cli/live-editor/slideout.eta b/bliss-cli/live-editor/slideout.eta new file mode 100644 index 0000000..4acf7b3 --- /dev/null +++ b/bliss-cli/live-editor/slideout.eta @@ -0,0 +1,76 @@ + + diff --git a/bliss-cli/live-editor/template-editor.eta b/bliss-cli/live-editor/template-editor.eta new file mode 100644 index 0000000..f2aa49d --- /dev/null +++ b/bliss-cli/live-editor/template-editor.eta @@ -0,0 +1,25 @@ +
+
<%= it.sourceTemplate.name %>
+
<%= it.sourceTemplate.content %>
+ +
+ diff --git a/bliss-cli/live-editor/template-editor.js b/bliss-cli/live-editor/template-editor.js new file mode 100644 index 0000000..e97f348 --- /dev/null +++ b/bliss-cli/live-editor/template-editor.js @@ -0,0 +1,8 @@ +function handler(req, res) { + const sql = require("db")("bliss").sql; + const sourceTemplate = sql + .prepare("SELECT * FROM templates WHERE id = ?") + .get(req.params.id); + if (!sourceTemplate) return res.status(404).send("Template not found"); + res.render("inspector/template_editor", { sourceTemplate }); +} diff --git a/db.js b/db.js index 83f7ce0..0bcf33e 100644 --- a/db.js +++ b/db.js @@ -3,6 +3,7 @@ const path = require("path"); const betterSqlite3 = require("better-sqlite3"); const { LRUCache } = require("lru-cache"); const { Eta } = require("eta"); +const cheerio = require("cheerio"); const db = betterSqlite3("./dbs/0.sqlite"); @@ -91,6 +92,37 @@ function getAllRoutes() { const templateCache = new LRUCache({ max: 100 }); +function annotateTemplateHtml(html, template) { + function annotate($, element) { + const current = $(element).attr("data-bliss-templates"); + let templates = []; + if (current) { + try { + templates = JSON.parse(current); + } catch (_) {} + } + if (!templates.some((item) => String(item.id) === String(template.id))) { + templates.push({ id: String(template.id), name: template.name }); + } + $(element).attr("data-bliss-templates", JSON.stringify(templates)); + if (!$(element).attr("data-bliss-template-id")) { + $(element).attr({ + "data-bliss-template-id": String(template.id), + "data-bliss-template-name": template.name, + }); + } + } + const lower = html.trimStart().toLowerCase(); + if (lower.startsWith(" +