1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import { reloadOnSaveFetchMiddleware } from "https://esm.town/v/stevekrouse/reloadOnSave?v=12";
const body = `<html>
<head>
<script
type="module"
src="https://raw.esm.sh/code-mirror-web-component@0.0.20/dist/code-mirror.js"
></script>
<style>
.cm-editor {
height: 300px;
border: 1px solid #ddd;
}
.cm-scroller {
overflow: auto;
}
</style>
</head>
<body style="margin: 0px">
<code-mirror
class="cm-editor"
id="editor"
language="sql"
code="SELECT * FROM sqlite;"
></code-mirror>
</body>
<button id="myButton">Click me</button>
<script type="module">
import { identify } from "https://esm.sh/sql-query-identifier@2.7.0";
const editor = document.getElementById("editor");
function selectStatementFromPosition(statements, pos) {
for (const statement of statements) {
if (statement.end + 1 >= pos) return statement;
}
return undefined;
}
function getCode() {
const code = editor.code;
let finalStatements = [];
const statements = identify(code, {
dialect: "sqlite",
strict: false,
});
const position = editor.view.state.selection.main.head;
const statement = selectStatementFromPosition(statements, position);
if (statement) {
finalStatements = [statement.text];
}
console.log(finalStatements);
}
// Use an event listener instead of an inline event handler
document.getElementById("myButton").addEventListener("click", getCode);
</script>
</html>
`;
export default reloadOnSaveFetchMiddleware(async function(req: Request): Promise<Response> {
return new Response(body, {
headers: {
"Content-Type": "text/html",
},
});
});

CodeMirror Web Component

Available Attributes

  • language
  • readonly
  • code
  • theme

API

You can access the code using the code property:

Create valdocument.getElementById("editor").code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const body = `<html>
<head>
<script
type="module"
src="https://raw.esm.sh/code-mirror-web-component@0.0.20/dist/code-mirror.js"
></script>
<script type="module">
document.getElementById("editor").addEventListener("code-change", (e) => {
console.log(e.detail.code);
});
</script>
</head>
<body style="margin: 0px; height: 100vh;">
<code-mirror id="editor" theme="dracula" language="js"></code-mirror>
</body>
</html>
`;
export default function() {
return new Response(body, {
headers: {
"Content-Type": "text/html",
},
});
}

@postpostscript/readmeManager: Edit Val Readmes With Persistent Drafts

edit this readme

image.png

image.png

Todo:

  • Upload images
  • Autosave/save without reloading page
  • Ctrl+S
  • Multiple draft versions
  • Switch to dark codemirror theme which has markdown styling
  • Allow for checking checkboxes in preview
    • View with just the preview
  • Ability to favorite vals on the Home page
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/hono/jsx */
import { fetchPaginatedData } from "https://esm.town/v/nbbaier/fetchPaginatedData?v=49";
import { api } from "https://esm.town/v/pomdtr/api?v=12";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo?v=26";
import { authMiddlewareCookie, type HonoEnv } from "https://esm.town/v/postpostscript/authMiddleware";
import { html } from "https://esm.town/v/postpostscript/html";
import { updateValReadme } from "https://esm.town/v/postpostscript/updateValReadme";
import { API_URL } from "https://esm.town/v/std/API_URL?v=5";
import { blob } from "https://esm.town/v/std/blob?v=11";
import { type Context, Hono } from "npm:hono";
const app = new Hono<HonoEnv>();
app.use(
"*",
authMiddlewareCookie({
optional: true,
}),
);
export const DRAFT_PREFIX = "readmeManager:draft";
const styles = `
img {
max-width: 100%;
}
`;
app.all("/:author/:name", async (c) => {
const { pathname } = new URL(c.req.url);
const author = c.req.param("author");
const name = c.req.param("name");
let { id, readme } = await api(`/v1/alias/${author}/${name}`);
const blobID = `${DRAFT_PREFIX}:${id}`;
let draft;
if (c.get("auth")) {
if (c.req.method === "POST") {
const formData = await c.req.formData();
const submitType = formData.get("submit");
if (formData.get("readme")) {
if (submitType === "publish") {
await updateValReadme(id, formData.get("readme"));
return c.redirect(`/${author}/${name}`);
} else {
await blob.setJSON(blobID, formData.get("readme"));
return c.redirect(`/${author}/${name}?draft=1`);
}
}
} else {
draft = await blob.getJSON(blobID);
}
if (c.req.query("draft") && draft) {
readme = draft;
}
}
return c.html(
<html data-bs-theme="dark">
<head>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
crossorigin="anonymous"
/>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.4.0/github-markdown.min.css"
rel="stylesheet"
/>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/code-mirror-web-component@0.0.16/dist/code-mirror.js"
>
</script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script
type="module"
dangerouslySetInnerHTML={{
__html: html`
import debounce from 'https://esm.sh/debounce';
async function render(text) {
window.result.innerHTML = await marked.parse(text);
document.getElementById('readme-input').value = text
}
document.addEventListener("DOMContentLoaded", () => {
const $editor = document.createElement('code-mirror')
$editor.setAttribute('id', 'editor')
$editor.setAttribute('theme', 'dracula')
$editor.setAttribute('language', 'markdown')
$editor.setAttribute('code', decodeURIComponent("${encodeURIComponent(readme ?? "")}"))
$editor.setAttribute('class', 'h-100')
$editor.addEventListener("code-change", (e) => {
render(e.detail.code)
})
render($editor.getAttribute('code'));
document.getElementById('editor-col').appendChild($editor)
})
1
Next