Skip to content

Commit baddbf8

Browse files
author
Tim Willebrands
committed
initial vibez
0 parents  commit baddbf8

10 files changed

Lines changed: 532 additions & 0 deletions

File tree

.github/workflows/publish.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Publish to npm
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
tags:
7+
- 'v*.*.*'
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
build-and-publish:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Setup Bun
20+
uses: oven-sh/setup-bun@v2
21+
with:
22+
bun-version: '1.x'
23+
24+
- name: Install
25+
run: bun install --no-progress
26+
27+
- name: Build
28+
run: bun run build
29+
30+
- name: Publish (Bun)
31+
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
32+
env:
33+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
34+
run: |
35+
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
36+
bun publish --access public
37+
38+

.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# dependencies (bun install)
2+
node_modules
3+
4+
# output
5+
out
6+
dist
7+
*.tgz
8+
9+
# code coverage
10+
coverage
11+
*.lcov
12+
13+
# logs
14+
logs
15+
_.log
16+
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17+
18+
# dotenv environment variable files
19+
.env
20+
.env.development.local
21+
.env.test.local
22+
.env.production.local
23+
.env.local
24+
25+
# caches
26+
.eslintcache
27+
.cache
28+
*.tsbuildinfo
29+
30+
# IntelliJ based IDEs
31+
.idea
32+
33+
# Finder (MacOS) folder config
34+
.DS_Store

LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Tim
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+
23+

README.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
## streaming-markdown-component
2+
3+
Minimal custom element `<streaming-md>` for progressively rendering streamed Markdown in the browser. Internally powered by the excellent Streaming Markdown library.
4+
5+
### Thanks and Credits
6+
7+
Huge thanks to the Streaming Markdown project for the streaming parser and default renderer that make this possible. Please check out and support the project: [Streaming Markdown docs](https://thetarnav.github.io/streaming-markdown/).
8+
9+
### Install
10+
11+
```bash
12+
npm install streaming-markdown-component
13+
# or
14+
pnpm add streaming-markdown-component
15+
# or
16+
yarn add streaming-markdown-component
17+
```
18+
19+
### Usage
20+
21+
```html
22+
<script type="module">
23+
import 'streaming-markdown-component/dist/index.js'
24+
</script>
25+
26+
<streaming-md id="md"></streaming-md>
27+
28+
<script type="module">
29+
const el = document.getElementById('md')
30+
el.appendChunk('# Hello, streamed world!\n\n')
31+
el.appendChunk('- One\n- Two\n- Three\n')
32+
el.finish()
33+
</script>
34+
```
35+
36+
Works great with htmx SSE:
37+
38+
```html
39+
<article
40+
hx-ext="sse"
41+
sse-connect="/party/ROOM/messages/MSG"
42+
sse-swap="message"
43+
hx-swap="beforeend"
44+
hx-target="find streaming-md"
45+
sse-close="finished">
46+
<streaming-md></streaming-md>
47+
<!-- htmx appends data: chunks here; the element consumes text nodes -->
48+
<!-- server should send: event: message / data: ... and event: finished when done -->
49+
<!-- Reference: https://thetarnav.github.io/streaming-markdown/ -->
50+
</article>
51+
```
52+
53+
### API
54+
55+
- `appendChunk(text: string)`: Append raw markdown chunk.
56+
- `finish()`: Flush and mark complete.
57+
- `reset()`: Clear content and restart.
58+
59+
### Demo
60+
61+
Open `demo.html` locally in a browser. It imports `dist/index.js` and streams predefined Markdown chunks into `<streaming-md>`.
62+
63+
#### GitHub Pages
64+
65+
- The repo includes `docs/index.html`. CI builds the component and copies `dist/` into `docs/dist/`.
66+
- Pages workflow `.github/workflows/pages.yml` deploys the `docs/` directory on pushes to `main` (and on manual dispatch).
67+
- After enabling GitHub Pages (GitHub → Settings → Pages → Source: GitHub Actions), your demo will be available at:
68+
- `https://<yourname>.github.io/<repo>/`
69+
70+
### Development
71+
72+
```bash
73+
bun install
74+
bun run build
75+
# outputs ESM at dist/index.js
76+
```
77+
78+
### Publish
79+
80+
This package ships ESM for the browser and uses Bun for publishing via GitHub Actions.
81+
82+
- CI: On tag push like `v0.1.0` (or manual dispatch), the workflow builds with Bun and runs `bun publish` to npm. Set `NPM_TOKEN` in repo secrets.
83+
- Local: you can also publish from your machine with Bun:
84+
85+
```bash
86+
bun run build
87+
bun publish --access public
88+
```
89+
90+
### License
91+
92+
MIT

bun.lock

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo.html

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>Streaming Markdown Demo</title>
7+
<style>
8+
:root { color-scheme: light dark; }
9+
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Arial, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; margin: 2rem; line-height: 1.5; }
10+
main { max-width: 800px; margin: 0 auto; }
11+
.controls { display: flex; gap: 0.5rem; margin: 1rem 0; align-items: center; }
12+
button { padding: 0.5rem 0.75rem; font: inherit; cursor: pointer; }
13+
streaming-md { display: block; border: 1px solid #8884; border-radius: 8px; padding: 1rem; height: 360px; overflow: auto; background: Canvas; color: CanvasText; }
14+
streaming-md::part(content) { font-size: 0.95rem; }
15+
pre { background: color-mix(in oklab, Canvas 90%, CanvasText); padding: 0.75rem; border-radius: 6px; overflow: auto; }
16+
code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
17+
</style>
18+
</head>
19+
<body>
20+
<main>
21+
<h1>Streaming Markdown Demo</h1>
22+
<p>This demo streams predefined Markdown chunks into the custom <code>&lt;streaming-md&gt;</code> component.</p>
23+
<div class="controls">
24+
<button id="start">Start stream</button>
25+
<button id="reset">Reset</button>
26+
</div>
27+
<streaming-md id="md"></streaming-md>
28+
</main>
29+
30+
<script type="module">
31+
import './dist/index.js';
32+
33+
const md = `
34+
*[DIAL-UP MODEM SOUNDS INTENSIFY]*
35+
36+
37+
# \U0001f389 PROOMPTING: The Future Is Now, Thanks To Science!
38+
39+
**SOI SOI SOI SOI SOI SOI SOI**
40+
41+
Greetings, fellow carbon-based life forms! Microsoft Sam here, and boy oh boy, do I have something SPECTACULAR to show you today! Remember when I used to read your emails back in Windows XP? Well, those days are OVER because now I'm here to tell you about PROOMPTING - the most REVOLUTIONARY chat application since... well... since ME!
42+
43+
## What The Heck Is This Thing?
44+
45+
Picture this: It's 2001. You've got your Windows XP machine humming along, Limewire is downloading your "totally legal" MP3s, and your 56k modem is screaming into the void. But WAIT! What if I told you that you could chat with an ARTIFICIAL INTELLIGENCE right from your desktop? That's right, folks - we've taken the best parts of Windows XP and SUPERCHARGED them with \u2728AI\u2728!
46+
47+
\`\`\`
48+
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
49+
\u2502 \U0001f5a5\ufe0f AUTHENTIC XP DESKTOP EXPERIENCE \u2502
50+
\u2502 \U0001f916 REAL AI THAT SOMEWHAT WORKS \u2502
51+
\u2502 \U0001f4ac CHAT ROOMS (LIKE MSN BUT BETTER) \u2502
52+
\u2502 \u26a1 FASTER THAN DIALUP (THANKFULLY) \u2502
53+
\u2502 \U0001f504 REAL-TIME MESSAGE STREAMING \u2502
54+
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
55+
\`\`\`
56+
57+
Link (wow): [Windows XP](https://www.microsoft.com/en-us/windowsxp/)
58+
59+
60+
*[WINDOWS XP SHUTDOWN SOUND]*
61+
`
62+
63+
function delay(ms) { return new Promise(r => setTimeout(r, ms)); }
64+
65+
async function start() {
66+
const el = document.getElementById('md');
67+
el.reset();
68+
const chunks = md.split(' ').map(c => c + ' ');
69+
for (const c of chunks) {
70+
el.appendChunk(c);
71+
await delay(50);
72+
}
73+
el.finish();
74+
}
75+
76+
document.getElementById('start').addEventListener('click', start);
77+
document.getElementById('reset').addEventListener('click', () => document.getElementById('md').reset());
78+
79+
// Auto-start
80+
start();
81+
</script>
82+
</body>
83+
</html>
84+
85+

docs/index.html

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>Streaming Markdown Component Demo</title>
7+
<style>
8+
:root { color-scheme: light dark; }
9+
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Arial; margin: 2rem; line-height: 1.5; }
10+
main { max-width: 800px; margin: 0 auto; }
11+
.controls { display: flex; gap: 0.5rem; margin: 1rem 0; align-items: center; }
12+
button { padding: 0.5rem 0.75rem; font: inherit; cursor: pointer; }
13+
streaming-md { display: block; border: 1px solid #8884; border-radius: 8px; padding: 1rem; height: 360px; overflow: auto; background: Canvas; color: CanvasText; }
14+
streaming-md::part(content) { font-size: 0.95rem; }
15+
pre { background: color-mix(in oklab, Canvas 90%, CanvasText); padding: 0.75rem; border-radius: 6px; overflow: auto; }
16+
code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
17+
</style>
18+
</head>
19+
<body>
20+
<main>
21+
<h1>Streaming Markdown Component Demo</h1>
22+
<p>This page is hosted via GitHub Pages. It loads the built component and streams some text.</p>
23+
<div class="controls">
24+
<button id="start">Start stream</button>
25+
<button id="reset">Reset</button>
26+
</div>
27+
<streaming-md id="md"></streaming-md>
28+
</main>
29+
30+
<script type="module">
31+
import './dist/index.js';
32+
33+
const md = `# Hello Pages!\n\n` +
34+
`This demo is served from GitHub Pages.\n\n` +
35+
`- Uses <streaming-md>\n` +
36+
`- Powered by streaming-markdown\n\n` +
37+
`\` +
38+
`\`\`\`js\nconsole.log("Pages demo")\n\`\`\`\n`;
39+
40+
function delay(ms) { return new Promise(r => setTimeout(r, ms)); }
41+
async function start() {
42+
const el = document.getElementById('md');
43+
el.reset();
44+
const chunks = md.split(' ').map(c => c + ' ');
45+
for (const c of chunks) {
46+
el.appendChunk(c);
47+
await delay(50);
48+
}
49+
el.finish();
50+
}
51+
document.getElementById('start').addEventListener('click', start);
52+
document.getElementById('reset').addEventListener('click', () => document.getElementById('md').reset());
53+
start();
54+
</script>
55+
</body>
56+
</html>
57+
58+

0 commit comments

Comments
 (0)