<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael</title>
    <description>The latest articles on DEV Community by Michael (@michael_b581c5c68faa9de1d).</description>
    <link>https://dev.to/michael_b581c5c68faa9de1d</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4101296%2Ff7bb8c28-58c0-47cd-adab-67a79e9b840d.png</url>
      <title>DEV Community: Michael</title>
      <link>https://dev.to/michael_b581c5c68faa9de1d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michael_b581c5c68faa9de1d"/>
    <language>en</language>
    <item>
      <title>domonic 1.8.4 released!</title>
      <dc:creator>Michael</dc:creator>
      <pubDate>Tue, 22 Sep 2026 10:18:14 +0000</pubDate>
      <link>https://dev.to/michael_b581c5c68faa9de1d/domonic-184-released-4pjp</link>
      <guid>https://dev.to/michael_b581c5c68faa9de1d/domonic-184-released-4pjp</guid>
      <description>&lt;p&gt;If you're using Python and working with the DOM or HTML you may want to take a look!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://domonic.readthedocs.io/" rel="noopener noreferrer"&gt;https://domonic.readthedocs.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;;&lt;/p&gt;

</description>
      <category>python</category>
      <category>html</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Perusal: the internet without all the 💩</title>
      <dc:creator>Michael</dc:creator>
      <pubDate>Sat, 19 Sep 2026 10:44:08 +0000</pubDate>
      <link>https://dev.to/michael_b581c5c68faa9de1d/perusal-the-internet-without-all-the-410n</link>
      <guid>https://dev.to/michael_b581c5c68faa9de1d/perusal-the-internet-without-all-the-410n</guid>
      <description>&lt;h1&gt;
  
  
  Perusal: the internet without all the 💩
&lt;/h1&gt;

&lt;p&gt;I've just released &lt;strong&gt;Perusal&lt;/strong&gt;, a small Python tool for reading the web without most of the web getting in the way.&lt;/p&gt;

&lt;p&gt;Modern web pages contain an extraordinary amount of stuff that isn't actually the content:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;navigation&lt;/li&gt;
&lt;li&gt;cookie banners&lt;/li&gt;
&lt;li&gt;footers&lt;/li&gt;
&lt;li&gt;related articles&lt;/li&gt;
&lt;li&gt;scripts&lt;/li&gt;
&lt;li&gt;styles&lt;/li&gt;
&lt;li&gt;tracking&lt;/li&gt;
&lt;li&gt;repeated links&lt;/li&gt;
&lt;li&gt;UI chrome&lt;/li&gt;
&lt;li&gt;adverts&lt;/li&gt;
&lt;li&gt;framework scaffolding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes you don't want a web page.&lt;/p&gt;

&lt;p&gt;You just want &lt;strong&gt;what the page says&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's what Perusal does.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;perusal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perusal https://en.wikipedia.org/wiki/Domino
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And instead of a giant HTML document, you get clean Markdown on stdout.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML in, useful text out
&lt;/h2&gt;

&lt;p&gt;Under the hood the basic pipeline is deliberately simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fetch
  ↓
DOM
  ↓
Readability
  ↓
Turndown
  ↓
clean Markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perusal uses the Python ports of Readability and Turndown I've been working on in the &lt;code&gt;domonic&lt;/code&gt; ecosystem.&lt;/p&gt;

&lt;p&gt;That means it isn't just stripping tags.&lt;/p&gt;

&lt;p&gt;It first tries to identify the meaningful article content, throws away most of the surrounding page furniture, and then converts what remains into Markdown.&lt;/p&gt;

&lt;p&gt;You can use a URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perusal https://example.com/article
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A local HTML file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perusal ./saved-page.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or pipe HTML straight into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://example.com | perusal -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  See how much rubbish disappeared
&lt;/h2&gt;

&lt;p&gt;There's also a report command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perusal report https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This compares the original HTML with the cleaned Markdown and gives you an indication of the size reduction and approximate token saving.&lt;/p&gt;

&lt;p&gt;That becomes particularly interesting when the consumer isn't a human.&lt;/p&gt;

&lt;p&gt;It's an LLM.&lt;/p&gt;

&lt;p&gt;If I'm passing a web page into an AI system, I generally don't want to pay for it to read navigation menus, tracking markup and 400 links from the footer.&lt;/p&gt;

&lt;p&gt;I want the useful bit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give it a reading goal
&lt;/h2&gt;

&lt;p&gt;Perusal can also rank sections of a page against a query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perusal https://example.com/docs &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"installation instructions"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--max-chars&lt;/span&gt; 4000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query isn't a web search.&lt;/p&gt;

&lt;p&gt;It's more like telling the reader:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;this is what I'm trying to find on this page&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perusal ranks the headings and content chunks against that goal and then applies the character budget.&lt;/p&gt;

&lt;p&gt;So instead of blindly chopping the first 4,000 characters from a document, it can preferentially return the relevant sections while keeping them in their original page order.&lt;/p&gt;

&lt;p&gt;That makes it useful as a very small preprocessing layer for RAG, agents and other AI tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read several pages at once
&lt;/h2&gt;

&lt;p&gt;You can also throw several URLs at it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perusal &lt;span class="se"&gt;\&lt;/span&gt;
    https://example.com/page-one &lt;span class="se"&gt;\&lt;/span&gt;
    https://example.com/page-two &lt;span class="se"&gt;\&lt;/span&gt;
    https://example.com/page-three
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perusal fetches the pages and produces one Markdown digest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Digest of 3 pages&lt;/span&gt;

&lt;span class="gu"&gt;## Page One&lt;/span&gt;

...

&lt;span class="gu"&gt;## Page Two&lt;/span&gt;

...

&lt;span class="gu"&gt;## Page Three&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For simple research tasks I've found this much nicer than juggling multiple blobs of HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about JavaScript sites?
&lt;/h2&gt;

&lt;p&gt;By default Perusal reads the HTML returned by the server.&lt;/p&gt;

&lt;p&gt;That's intentional. It's fast and covers a huge amount of the web.&lt;/p&gt;

&lt;p&gt;But obviously some sites return little more than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"everything.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For those there's an optional JavaScript mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'perusal[js]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perusal https://example.com/app &lt;span class="nt"&gt;--js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses &lt;strong&gt;myjs&lt;/strong&gt;, my Python JavaScript interpreter, to execute the page's scripts against a live &lt;code&gt;domonic&lt;/code&gt; DOM before handing the result to Readability.&lt;/p&gt;

&lt;p&gt;No Node process and no full browser involved.&lt;/p&gt;

&lt;p&gt;It's obviously heavier than simply fetching HTML, which is why it's opt-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  It accidentally became a browser too
&lt;/h2&gt;

&lt;p&gt;Once I had a clean page reader I thought:&lt;/p&gt;

&lt;p&gt;why shouldn't humans be able to use the same thing?&lt;/p&gt;

&lt;p&gt;So Perusal also has an optional reader browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'perusal[browser]'&lt;/span&gt;
perusal browse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or open a page directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perusal browse https://en.wikipedia.org/wiki/Domino
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a deliberately small tabbed reader with things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tabs and history&lt;/li&gt;
&lt;li&gt;bookmarks&lt;/li&gt;
&lt;li&gt;incognito tabs&lt;/li&gt;
&lt;li&gt;adjustable typography&lt;/li&gt;
&lt;li&gt;save page as Markdown&lt;/li&gt;
&lt;li&gt;page source&lt;/li&gt;
&lt;li&gt;resource inspection&lt;/li&gt;
&lt;li&gt;DNS / WHOIS information&lt;/li&gt;
&lt;li&gt;certificate-transparency subdomain discovery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The DevTools are intentionally fairly old-school.&lt;/p&gt;

&lt;p&gt;They're there to answer things like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What did this page actually return?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What resources does it reference?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What domain am I talking to?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;rather than pretending to be a replacement for Chrome DevTools.&lt;/p&gt;

&lt;h2&gt;
  
  
  And an MCP server
&lt;/h2&gt;

&lt;p&gt;The other obvious consumer of clean web content is an agent.&lt;/p&gt;

&lt;p&gt;So Perusal can expose the exact same reader through MCP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'perusal[mcp]'&lt;/span&gt;
perusal mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It intentionally exposes just one read-only tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web_read(url, query?, max_chars?, timeout?)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;I didn't particularly want another MCP server with seventeen overlapping tools for searching, crawling, scraping, clicking and browsing.&lt;/p&gt;

&lt;p&gt;Perusal does one thing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;read this web page for me.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That makes it quite easy to give an AI client controlled access to cleaned web content without handing it an entire browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  One pipeline, three interfaces
&lt;/h2&gt;

&lt;p&gt;So Perusal has effectively ended up with three ways of using the same idea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;perusal &amp;lt;url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for scripts and terminals,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;perusal browse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for humans,&lt;/p&gt;

&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;perusal mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for agents.&lt;/p&gt;

&lt;p&gt;I'm still treating it as alpha software and there are plenty of rough edges, but I think the underlying idea is useful:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;the web page and the information on the web page are not the same thing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot of the time we can throw away an enormous amount of the former while keeping the latter.&lt;/p&gt;

&lt;p&gt;If you want to try it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;perusal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PyPI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/perusal/" rel="noopener noreferrer"&gt;https://pypi.org/project/perusal/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback, bug reports and weird pages that break it are very welcome.&lt;/p&gt;

&lt;h1&gt;
  
  
  python #opensource #ai #webdev
&lt;/h1&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>html</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Python Browser needs your tokens</title>
      <dc:creator>Michael</dc:creator>
      <pubDate>Tue, 15 Sep 2026 06:49:34 +0000</pubDate>
      <link>https://dev.to/michael_b581c5c68faa9de1d/python-browser-needs-your-tokens-5b3f</link>
      <guid>https://dev.to/michael_b581c5c68faa9de1d/python-browser-needs-your-tokens-5b3f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Friends, Wo-mans, pythonistas… lend me your tokens.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m building &lt;strong&gt;Chromonic&lt;/strong&gt;, an experimental Python browser/rendering engine, mostly by throwing Claude at increasingly unreasonable problems:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/byteface/chromonic/" rel="noopener noreferrer"&gt;https://github.com/byteface/chromonic/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It uses Domonic as its DOM/CSSOM and now has a Chrome-vs-Chromonic layout conformance harness, which means I can feed it real web-platform tests and objectively measure how wrong my browser is.&lt;/p&gt;

&lt;p&gt;Naturally, I would now like to outsource that wrongness to your Claudes.&lt;/p&gt;

&lt;p&gt;If anyone has some spare tokens and fancies joining the campaign:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd tests/layout&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Clone WPT:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git clone --depth=1 https://github.com/web-platform-tests/wpt.git&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then point Claude at a folder under &lt;strong&gt;WPT/CSS2&lt;/strong&gt; and basically tell it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run the tests. Find why Chromonic disagrees with Chrome. Fix it. Repeat until victory or bankruptcy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the problem is actually in Domonic upstream, add it to &lt;code&gt;PLAN.md&lt;/code&gt; and patch around it locally for now.&lt;/p&gt;

&lt;p&gt;Pick whatever CSS2 area takes your fancy. Different agents attacking different folders would be especially interesting.&lt;/p&gt;

&lt;p&gt;PRs, battle reports, pass-rate improvements and tales of token expenditure all welcome.&lt;/p&gt;

&lt;p&gt;The browser is unfinished, the standards are enormous, and I have made the questionable decision to see whether a small army of Claudes can brute-force CSS conformance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who’s in?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Chromonic (Python Browser)</title>
      <dc:creator>Michael</dc:creator>
      <pubDate>Mon, 14 Sep 2026 11:52:24 +0000</pubDate>
      <link>https://dev.to/michael_b581c5c68faa9de1d/chromonic-python-browser-48n4</link>
      <guid>https://dev.to/michael_b581c5c68faa9de1d/chromonic-python-browser-48n4</guid>
      <description>&lt;p&gt;I'm going to be building a browser with Python. I already have a DOM and so just need to work on a layout engine. Watch progress here...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/byteface/chromonic/" rel="noopener noreferrer"&gt;https://github.com/byteface/chromonic/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>html</category>
      <category>programming</category>
    </item>
    <item>
      <title>myjs</title>
      <dc:creator>Michael</dc:creator>
      <pubDate>Sat, 05 Sep 2026 16:55:17 +0000</pubDate>
      <link>https://dev.to/michael_b581c5c68faa9de1d/myjs-54b9</link>
      <guid>https://dev.to/michael_b581c5c68faa9de1d/myjs-54b9</guid>
      <description>&lt;p&gt;A JavaScript interpreter for Python. Run .js files, render HTML pages headlessly, script the DOM, call native C through ffi, and reach the entire Python ecosystem from JavaScript — no Node, no browser, no build step.&lt;/p&gt;

&lt;p&gt;pip install myjs&lt;/p&gt;

&lt;h1&gt;
  
  
  or, for an isolated CLI:
&lt;/h1&gt;

&lt;p&gt;pipx install myjs&lt;br&gt;
myjs                          # REPL (history, TAB completion, top-level await)&lt;br&gt;
myjs script.js                # run a file&lt;br&gt;
myjs script.js a b            # ...with ["a","b"] as globalThis.argv&lt;br&gt;
myjs -e "6 * 7"               # evaluate a snippet&lt;br&gt;
myjs page.html                # render a page headlessly -&amp;gt; HTML on stdout&lt;br&gt;
myjs page.html -e "document.title"   # ...then read the rendered DOM&lt;br&gt;
myjs examples                 # list the bundled demos; &lt;code&gt;myjs examples hn --run&lt;/code&gt;&lt;br&gt;
myjs examples cockpit --run   # a live TUI: dashboard, killable workers, raw keypresses&lt;br&gt;
import myjs&lt;/p&gt;

&lt;p&gt;myjs.eval("1 + 2 * 3")               # -&amp;gt; 7&lt;br&gt;
myjs.run("script.js")&lt;/p&gt;

&lt;p&gt;s = myjs.Session()                   # an isolated global scope&lt;br&gt;
s.eval("const x = 21;"); s.eval("x * 2")   # -&amp;gt; 42&lt;/p&gt;
&lt;h1&gt;
  
  
  a .py file calling a .js file directly -- no bridge, no serialization
&lt;/h1&gt;

&lt;p&gt;mod = myjs.import_js("math_utils.js")   # export function double(x) { return x*2; }&lt;br&gt;
mod.double(21)                          # -&amp;gt; 42, a real Python call into real JS&lt;br&gt;
What a script can reach&lt;br&gt;
DOM document, window, and ~190 constructors (URL, Headers, Event, DOMRect, TextEncoder, …). document.createElement(...).appendChild(...) builds a real tree.&lt;br&gt;
Event loop  Promise (all / allSettled / race / any / finally), async / await, setTimeout / setInterval, queueMicrotask. Microtasks before timers.&lt;br&gt;
ES modules  import / export from .js files; a bare specifier resolves to a Python module (import np from "numpy").&lt;br&gt;
fetch   asynchronous, thread-backed — await Promise.all([fetch(a), fetch(b)]) is genuinely concurrent. WebSocket is a real RFC 6455 client.&lt;br&gt;
fs / path / sh / http   read &amp;amp; write files (fs.readBytes(path, n) for a raw header), run shell commands, serve HTTP (http.serve(port, handler)).&lt;br&gt;
struct  pre-imported — struct.unpack_from(fmt, fs.readBytes(path, n)) reads a BMP/PNG/ELF header with no library.&lt;br&gt;
os / process    Node-flavoured — os.platform(), os.cpus(), process.argv, process.env, process.exit().&lt;br&gt;
ffi call any C library through ctypes — no node-gyp, no compiler.&lt;br&gt;
py  callable — py("2 ** 10") evaluates Python; py.import("pandas"), py.exec(...). The whole package index.&lt;br&gt;
console %s / %d / %c format specifiers, console.table, console.group, console.count / time / assert.&lt;br&gt;
native dialogs  prompt() / confirm() / chooseFile() / chooseFolder() — the real macOS UI, driven from JS.&lt;br&gt;
clipboard   readText() / writeText() — the real system clipboard, shared with every other app.&lt;br&gt;
plus    say / notify / open, hash, atob / btoa, localStorage&lt;br&gt;
const libc = ffi.loadLibrary("c");&lt;br&gt;
libc.abs.argtypes = [ffi.types.int];&lt;br&gt;
libc.abs.restype  = ffi.types.int;&lt;br&gt;
libc.abs(-42);                              // 42&lt;/p&gt;

&lt;p&gt;const rows = py.import("sqlite3").connect(":memory:").execute("SELECT 1+1").fetchall();&lt;br&gt;
console.table([...py.list(py.list(rows)[0])]);&lt;br&gt;
Headless HTML — render, scrape, automate&lt;br&gt;
page = myjs.Page.load("&lt;a href="https://example.com/%22" rel="noopener noreferrer"&gt;https://example.com/"&lt;/a&gt;)   # a URL (fetches HTML + CSS + JS) or a local file&lt;br&gt;
page.eval("getComputedStyle(document.body).color")   # the fetched CSS, applied&lt;br&gt;
page.fill("#search", "widgets")&lt;br&gt;
page.submit("#form")&lt;br&gt;
page.wait_for(".result")                # pump the event loop until it appears&lt;br&gt;
page.text(".result")                    # read it back&lt;/p&gt;

&lt;p&gt;myjs.render("app.html", strip_scripts=True)   # SSR: one call -&amp;gt; rendered HTML&lt;br&gt;
Page.load takes a local path or an http(s):// URL. For a URL it fetches the page, every  (folded into the document so getComputedStyle sees the rules), and every  — all resolved relative to the page URL. &amp;lt;script type="module"&amp;gt; gets module semantics; DOMContentLoaded / load fire. It&amp;amp;#39;s Puppeteer without a browser — a pip install, not a 150 MB Chromium download.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Command line&amp;lt;br&amp;gt;
myjs [FILE] [ARGS...]           run a .js file (ARGS -&amp;amp;gt; globalThis.argv),&amp;lt;br&amp;gt;
                                or render a .html file headlessly&amp;lt;br&amp;gt;
  -e, --eval JS                 evaluate JS; with a .html file, against the page&amp;lt;br&amp;gt;
  -o, --output FILE             write rendered HTML to a file&amp;lt;br&amp;gt;
  --html                        force headless-render mode&amp;lt;br&amp;gt;
  --strip-scripts               drop &amp;lt;script&amp;gt; elements after they run&amp;lt;br&amp;gt;
  --gui                         show the resulting DOM in a native window (needs myjs[gui])&amp;lt;br&amp;gt;
  -i, --interactive             enter the REPL after running&amp;lt;br&amp;gt;
myjs examples [NAME] [--run]    list / view / run the bundled demos&amp;lt;br&amp;gt;
myjs                            REPL&amp;lt;br&amp;gt;
The REPL&amp;lt;br&amp;gt;
readline history (persisted to ~/.config/myjs/history), reverse search, and TAB completion of globals and obj.member chains. Multi-line input is auto-detected; top-level await works. Values print the way a real REPL&amp;amp;#39;s inspector would -- colored, wrapped past 72 columns, Map/Set/RegExp/ Date/Symbol/Promise shown natively rather than falling back to a generic dump. A live setInterval dashboard redraws in place instead of flashing the whole terminal. Commands: .load &amp;lt;file&amp;gt; (a .js, or a .html rendered into the session as page), .save, .editor, .clear, .help, .exit.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Language embedding&amp;lt;br&amp;gt;
s = myjs.Session(scope={&amp;amp;quot;answer&amp;amp;quot;: 42})&amp;lt;br&amp;gt;
s.eval(&amp;amp;quot;answer * 2&amp;amp;quot;)                     # -&amp;amp;gt; 84&amp;lt;br&amp;gt;
A JS exception that escapes becomes myjs.JSError with js_name, js_line, and js_trace (the call stack). Sessions are isolated; console.log output is captured on s.console_lines.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;The language layer passes a curated test262-style battery — closures, classes (extends / super / fields / getters-setters / private / static), destructuring, generators, labelled break, async / await, and the Array / String / Object / Number / Math / JSON / RegExp built-ins, real ES5-style prototype chains, and Symbol. Not covered: Proxy, with.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;How it&amp;amp;#39;s built&amp;lt;br&amp;gt;
myjs is the runtime layer on top of the domonic-libs acorn port — a faithful Python port of the acorn parser, a tree-walking evaluator, and a code generator (parse / interpret / generate, so JS can go source → AST → source) — running on domonic&amp;amp;#39;s DOM. It ships in lockstep with domonic-libs.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Full reference: docs/myjs.md. MIT licensed.&amp;lt;/p&amp;gt;
&lt;/p&gt;

</description>
      <category>python</category>
      <category>javascript</category>
      <category>html</category>
      <category>ai</category>
    </item>
    <item>
      <title>I built a C-capable, DOM-native JavaScript Runtime in Pure Python</title>
      <dc:creator>Michael</dc:creator>
      <pubDate>Fri, 04 Sep 2026 01:03:36 +0000</pubDate>
      <link>https://dev.to/michael_b581c5c68faa9de1d/i-built-a-c-capable-dom-native-javascript-runtime-in-pure-python-b9c</link>
      <guid>https://dev.to/michael_b581c5c68faa9de1d/i-built-a-c-capable-dom-native-javascript-runtime-in-pure-python-b9c</guid>
      <description>&lt;p&gt;I just released myjs on PyPI (pip install myjs or pipx install myjs).&lt;/p&gt;

&lt;p&gt;It’s a Python-native JavaScript runtime that bridges Python’s AST capabilities, native DOM primitives (domonic), and direct C-level Foreign Function Interface (FFI) bindings into a unified JavaScript execution scope.&lt;/p&gt;

&lt;p&gt;🌐 The Pitch&lt;br&gt;
Why build another JS interpreter?&lt;/p&gt;

&lt;p&gt;In traditional stacks, if you want JavaScript to talk to native C libraries or manipulate server-side DOM structures, you end up juggling node-gyp, C++ addon build chains, or heavy IPC bridges like gRPC/REST between Node.js and Python.&lt;/p&gt;

&lt;p&gt;myjs collapses all of that into a single, pure-Python process.&lt;/p&gt;

&lt;p&gt;Because myjs parses JS (via an Acorn AST engine ported to Python) and executes it directly inside Python’s host scope, JavaScript code gains direct access to:&lt;/p&gt;

&lt;p&gt;Python’s DOM Tree: Real-time DOM manipulation via domonic.&lt;/p&gt;

&lt;p&gt;C Libraries &amp;amp; Shared Memory: Direct low-level access to system .so, .dylib, and .dll binaries via Python’s ctypes/cffi.&lt;/p&gt;

&lt;p&gt;Python Standard Library: Clean mapping between JS scopes and host OS capabilities.&lt;/p&gt;

&lt;p&gt;🚀 Quick Demo: C Memory, FFI, &amp;amp; DOM in JS&lt;br&gt;
Here is what running a script in myjs looks like:&lt;/p&gt;

&lt;p&gt;JavaScript&lt;br&gt;
// myjs_demo.js&lt;/p&gt;

&lt;p&gt;// 1. Call system C functions directly from JS without compilation!&lt;br&gt;
const libc = ffi.loadLibrary('libc.dylib'); // Or 'c' / 'msvcrt'&lt;/p&gt;

&lt;p&gt;const absVal = libc.abs(-42);&lt;br&gt;
const sqrtVal = libc.sqrt(2);&lt;/p&gt;

&lt;p&gt;window.console.log('C abs(-42) =', absVal);&lt;br&gt;
window.console.log('C sqrt(2)  =', sqrtVal);&lt;/p&gt;

&lt;p&gt;// 2. Manipulate a native Python DOM tree via Hyperscript h()&lt;br&gt;
function h(tag, props, ...kids) {&lt;br&gt;
  const el = document.createElement(tag);&lt;br&gt;
  for (const k in props) {&lt;br&gt;
    if (k === 'text') el.textContent = props[k];&lt;br&gt;
    else el.setAttribute(k, props[k]);&lt;br&gt;
  }&lt;br&gt;
  kids.forEach(c =&amp;gt; el.appendChild(&lt;br&gt;
    typeof c === 'string' ? document.createTextNode(c) : c&lt;br&gt;
  ));&lt;br&gt;
  return el;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;document.body.appendChild(&lt;br&gt;
  h('main', { class: 'card' },&lt;br&gt;
    h('h1', { text: 'Executed by myjs' }),&lt;br&gt;
    h('p', { text: 'Real Python DOM tree rendered from JS!' })&lt;br&gt;
  )&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;window.console.log('DOM Tree:', document.body.outerHTML);&lt;br&gt;
Run it via pipx:&lt;/p&gt;

&lt;p&gt;Bash&lt;br&gt;
$ pipx install myjs&lt;br&gt;
$ myjs myjs_demo.js&lt;br&gt;
Output:&lt;/p&gt;

&lt;p&gt;Plaintext&lt;br&gt;
C abs(-42)   = 42&lt;br&gt;
C sqrt(2)    = 1.4142135623730951&lt;br&gt;
DOM Tree     = &lt;/p&gt;
&lt;h1&gt;Executed by myjs&lt;/h1&gt;
&lt;p&gt;Real Python DOM tree rendered from JS!&lt;/p&gt;
&lt;br&gt;
host         = Darwin arm64 | python 3.13.12&lt;br&gt;
💡 Key Features&lt;br&gt;
Zero C-Compilers Required: Call C dynamic libraries, allocate raw C memory buffers, and pass function pointers straight from JS. Python’s runtime handles ABI resolution dynamically.

&lt;p&gt;Pure Python Portability: Embeddable anywhere Python runs—no heavy binary blobs (like V8) required.&lt;/p&gt;

&lt;p&gt;Direct DOM Integration: Mutate real domonic Python DOM nodes from within JS scripts.&lt;/p&gt;

&lt;p&gt;📦 Try It Out&lt;br&gt;
PyPI: &lt;a href="https://pypi.org/project/myjs/" rel="noopener noreferrer"&gt;https://pypi.org/project/myjs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Installation: pip install myjs or pipx install myjs&lt;/p&gt;

&lt;p&gt;I’d love to hear feedback, feature ideas, or wild use cases from anyone interested in language runtimes, Python/JS interop, or native FFI bindings!&lt;/p&gt;

</description>
      <category>python</category>
      <category>javascript</category>
      <category>html</category>
      <category>c</category>
    </item>
    <item>
      <title>convert javascript to python</title>
      <dc:creator>Michael</dc:creator>
      <pubDate>Thu, 03 Sep 2026 07:49:49 +0000</pubDate>
      <link>https://dev.to/michael_b581c5c68faa9de1d/convert-javascript-to-python-3jp4</link>
      <guid>https://dev.to/michael_b581c5c68faa9de1d/convert-javascript-to-python-3jp4</guid>
      <description>&lt;p&gt;It's now easier to convert javascript to python than every before due to domonic! A full DOM in Python. see here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/byteface/domonic-libs/" rel="noopener noreferrer"&gt;https://github.com/byteface/domonic-libs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just clone this repo and tell claude what library you want to port, to make it faithful and lean on domonic's dom.py and javascript.py and bingo an hour later and a few reprompts and out pops a compatible library.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>python</category>
      <category>html</category>
    </item>
    <item>
      <title>Beautiful Slop</title>
      <dc:creator>Michael</dc:creator>
      <pubDate>Sun, 30 Aug 2026 11:06:05 +0000</pubDate>
      <link>https://dev.to/michael_b581c5c68faa9de1d/beautiful-slop-4k1g</link>
      <guid>https://dev.to/michael_b581c5c68faa9de1d/beautiful-slop-4k1g</guid>
      <description>&lt;p&gt;I used AI to create a bs4 compatibility layer for domonic...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://domonic.readthedocs.io/packages/bs4/" rel="noopener noreferrer"&gt;https://domonic.readthedocs.io/packages/bs4/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's called Beautiful Slop, let me know what you think&lt;/p&gt;

</description>
      <category>python</category>
      <category>javascript</category>
      <category>web</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
