+ Starmer literally has a lower approval rating than Maduro
+
+
+
+ Citazione
+
+
+
+
+
+
+
+
+
+
+
+ Basil the Great
+
+ @BasilTheGreat
+
+ ·
+
+
+
+
+
+
+
+
+
+ ASMONGOLD SAYS AMERICANS SHOULD CAPTURE KEIR STARMER This
+ comes after Keir Starmer says he may ban X in the UK "We
+ should just go and arrest him, he has a lower approval
+ rating than Maduro and we arrested him"
+
+ In software, the code documents the app. In AI, the traces
+ do.
+ TL;DR In traditional software, you read the code to understand
+ what the app does - the decision logic lives in your codebase In
+ AI agents, the code is just scaffolding - the actual
+ decision-making...
+
+ Or use a state machine from the beginning
+
+ This is the next best thing, though!
+
+
+ Citazione
+
+
+
+
+
+
+
+
+
+
+
+
+ mert | helius
+
+
+
+
+
+ @mert
+
+ ·
+
+
+
+
+
+
+
+
+ a quick opus 4.5 vibecoding tip ask claude to make state
+ machine diagrams of existing components this causes it to
+ map out all paths (which, it will default to being lazy
+ otherwise) but also helps you verify if it's doing things
+ correctly at a systems ...5 more characters
+
+ Reason #42 for open platforms: to shut down every politician’s
+ incessant demands to all gatekeepers to censor all of their
+ political opponents.
+
+
+ Citazione
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MacRumors.com
+
+ @MacRumors
+
+ ·
+
+
+
+
+
+
+
+
+ U.S. Senators Ask Apple and Google to Remove X and Grok
+ Apps Over Sexualized Image Generation
+
+ https://
+ macrumors.com/2026/01/09/sen
+ ators-apple-google-remove-grok/
+ …
+
+
+ All major AIs have documented instances of going off the rails;
+ all major AI companies make their best efforts to combat this;
+ none are perfect. Politicians demanding gatekeepers selectively
+ crush the one that's their political opponent's company is basic
+ crony capitalism.
+
+ I seriously doubt any of these companies makes "their best
+ efforts" to do anything other than ship AI versions as rapidly as
+ possible. Multiple have already admitted in court to mass IP
+ theft. Their concern for the law is at best an afterthought when
+ it isn't ignored entirely.
+
Press space bar to start a drag. When dragging you can use the arrow keys to move the item around and escape to
+ cancel. Some screen readers may require you to be in focus mode or to use your pass through key
+ Starmer literally has a lower approval rating than Maduro
+
+
+
+
+
+
+ Citazione
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Basil the Great
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @BasilTheGreat
+
+
+
+
+ ·
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ASMONGOLD SAYS AMERICANS SHOULD CAPTURE KEIR STARMER This
+ comes after Keir Starmer says he may ban X in the UK "We
+ should just go and arrest him, he has a lower approval
+ rating than Maduro and we arrested him"
+
+ In software, the code documents the app. In AI, the traces
+ do.
+
+
+ TL;DR In traditional software, you read the code to understand
+ what the app does - the decision logic lives in your codebase In
+ AI agents, the code is just scaffolding - the actual
+ decision-making...
+
+ Or use a state machine from the beginning
+
+ This is the next best thing, though!
+
+
+
+
+
+ Citazione
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mert | helius
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @mert
+
+
+
+
+ ·
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ a quick opus 4.5 vibecoding tip ask claude to make state
+ machine diagrams of existing components this causes it to
+ map out all paths (which, it will default to being lazy
+ otherwise) but also helps you verify if it's doing things
+ correctly at a systems ...5 more characters
+
+ Reason #42 for open platforms: to shut down every politician’s
+ incessant demands to all gatekeepers to censor all of their
+ political opponents.
+
+
+
+
+
+ Citazione
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MacRumors.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @MacRumors
+
+
+
+
+ ·
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ U.S. Senators Ask Apple and Google to Remove X and Grok
+ Apps Over Sexualized Image Generation
+
+ https://
+ macrumors.com/2026/01/09/sen
+ ators-apple-google-remove-grok/
+ …
+
+
+ All major AIs have documented instances of going off the rails;
+ all major AI companies make their best efforts to combat this;
+ none are perfect. Politicians demanding gatekeepers selectively
+ crush the one that's their political opponent's company is basic
+ crony capitalism.
+
+ I seriously doubt any of these companies makes "their best
+ efforts" to do anything other than ship AI versions as rapidly as
+ possible. Multiple have already admitted in court to mass IP
+ theft. Their concern for the law is at best an afterthought when
+ it isn't ignored entirely.
+
Press space bar to start a drag. When dragging you can use the arrow keys to move the item around and escape to
+ cancel. Some screen readers may require you to be in focus mode or to use your pass through key
+ Starmer literally has a lower approval rating than Maduro
+
+
+
+
+
+ Citazione
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Basil the Great
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @BasilTheGreat
+
+
+
+
+ ·
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ASMONGOLD SAYS AMERICANS SHOULD CAPTURE KEIR STARMER This
+ comes after Keir Starmer says he may ban X in the UK "We
+ should just go and arrest him, he has a lower approval
+ rating than Maduro and we arrested him"
+
+ In software, the code documents the app. In AI, the traces
+ do.
+
+
+ TL;DR In traditional software, you read the code to understand
+ what the app does - the decision logic lives in your codebase In
+ AI agents, the code is just scaffolding - the actual
+ decision-making...
+
+ Or use a state machine from the beginning
+ This is the next best thing, though!
+
+
+
+
+
+ Citazione
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mert | helius
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @mert
+
+
+
+
+ ·
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ a quick opus 4.5 vibecoding tip ask claude to make state
+ machine diagrams of existing components this causes it to
+ map out all paths (which, it will default to being lazy
+ otherwise) but also helps you verify if it's doing things
+ correctly at a systems level
+
+ Reason #42 for open platforms: to shut down every politician’s
+ incessant demands to all gatekeepers to censor all of their
+ political opponents.
+
+
+
+
+
+ Citazione
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MacRumors.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @MacRumors
+
+
+
+
+ ·
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ U.S. Senators Ask Apple and Google to Remove X and Grok
+ Apps Over Sexualized Image Generation https://macrumors.com/2026/01/09/senators-apple-google-remove-grok/…
+
+ All major AIs have documented instances of going off the rails;
+ all major AI companies make their best efforts to combat this;
+ none are perfect. Politicians demanding gatekeepers selectively
+ crush the one that's their political opponent's company is basic
+ crony capitalism.
+
+ I seriously doubt any of these companies makes "their best
+ efforts" to do anything other than ship AI versions as rapidly as
+ possible. Multiple have already admitted in court to mass IP
+ theft. Their concern for the law is at best an afterthought when
+ it isn't ignored entirely.
+
+ Press space bar to start a drag. When dragging you can use the arrow keys to move the item around and escape to
+ cancel. Some screen readers may require you to be in focus mode or to use your pass through key
+
+
+
+
+
+
+
+
+
diff --git a/playwriter/src/htmlrewrite.test.ts b/playwriter/src/htmlrewrite.test.ts
index 64d54f8..c4bc458 100644
--- a/playwriter/src/htmlrewrite.test.ts
+++ b/playwriter/src/htmlrewrite.test.ts
@@ -1,4 +1,5 @@
import { expect, test } from 'vitest'
+import { readFileSync } from 'fs'
import { formatHtmlForPrompt } from './htmlrewrite.js'
test('formatHtmlForPrompt', async () => {
@@ -16351,48 +16352,39 @@ test('formatHtmlForPrompt', async () => {
)
})
-test('collapses 3+ consecutive empty elements of same type', async () => {
+test('removes empty elements', async () => {
const html = `
-
-
content
-
`
const result = await formatHtmlForPrompt({ html })
- // Should collapse 4 spans into 1, but keep 2 divs (not >= 3)
+ // All empty elements should be removed
expect(result).toMatchInlineSnapshot(`
- "
-
-
content
-
-
-
+ "
content
"
`)
})
-test('does not collapse empty elements with attributes', async () => {
+test('keeps empty elements that have attributes', async () => {
const html = `
-
+
`
const result = await formatHtmlForPrompt({ html })
- // All 3 should remain because they have attributes
+ // Empty elements with attributes should remain, empty without attrs removed
expect(result).toMatchInlineSnapshot(`
"
+ `
+ const result = await formatHtmlForPrompt({ html })
+
+ // Should unwrap to a single div containing the p
+ expect(result).toMatchInlineSnapshot(`
+ "
content
+ "
+ `)
+})
diff --git a/playwriter/src/htmlrewrite.ts b/playwriter/src/htmlrewrite.ts
index d2d1a12..5ea6955 100644
--- a/playwriter/src/htmlrewrite.ts
+++ b/playwriter/src/htmlrewrite.ts
@@ -26,6 +26,7 @@ export async function formatHtmlForPrompt({
]
const attributesToKeep = [
+ // Standard descriptive attributes
'label',
'title',
'alt',
@@ -37,10 +38,24 @@ export async function formatHtmlForPrompt({
'type',
'role',
'target',
- 'vimium-label',
+ // Descriptive aria attributes (text content)
+ 'aria-label',
+ 'aria-placeholder',
+ 'aria-valuetext',
+ 'aria-roledescription',
+ // Useful aria state attributes
+ 'aria-hidden',
+ 'aria-expanded',
+ 'aria-checked',
+ 'aria-selected',
+ 'aria-disabled',
+ 'aria-pressed',
+ 'aria-required',
+ 'aria-current',
// Test IDs (data-testid, data-test, data-cy are covered by data-* prefix)
'testid',
'test-id',
+ 'vimium-label',
// Conditionally added: 'style', 'class'
]
@@ -85,7 +100,6 @@ export async function formatHtmlForPrompt({
const newAttrs: typeof node.attrs = {}
for (const [attr, value] of Object.entries(node.attrs)) {
const shouldKeep =
- attr.startsWith('aria-') ||
attr.startsWith('data-') ||
attributesToKeep.includes(attr)
@@ -121,8 +135,63 @@ export async function formatHtmlForPrompt({
}
}
- // Plugin to collapse 3+ consecutive empty elements of the same type
- const collapseEmptyElementsPlugin = () => {
+ // Plugin to unwrap unnecessary nested wrapper elements
+ // e.g.,
text
->
text
+ const unwrapNestedWrappersPlugin = () => {
+ return (tree) => {
+ const isWhitespaceOnly = (node) => {
+ return typeof node === 'string' && node.trim().length === 0
+ }
+
+ const hasNoAttrs = (node) => {
+ return !node.attrs || Object.keys(node.attrs).length === 0
+ }
+
+ const unwrapNode = (node) => {
+ if (typeof node === 'string') return node
+ if (!node.tag) return node
+
+ // First, recursively process children
+ if (node.content && Array.isArray(node.content)) {
+ node.content = node.content.map(unwrapNode)
+ }
+
+ // Check if this node is an unnecessary wrapper:
+ // - has no attributes
+ // - has exactly one non-whitespace child that is an element
+ if (hasNoAttrs(node) && node.content && Array.isArray(node.content)) {
+ const nonWhitespaceChildren = node.content.filter(c => !isWhitespaceOnly(c))
+
+ if (nonWhitespaceChildren.length === 1) {
+ const onlyChild = nonWhitespaceChildren[0]
+ // If the only child is also an element (not text), unwrap
+ if (typeof onlyChild !== 'string' && onlyChild.tag) {
+ // Replace this node with its child
+ return onlyChild
+ }
+ }
+ }
+
+ return node
+ }
+
+ // Apply multiple passes until stable (handles deeply nested wrappers)
+ let result = tree.map(unwrapNode)
+ let prevJson = ''
+ let currJson = JSON.stringify(result)
+ while (prevJson !== currJson) {
+ prevJson = currJson
+ result = result.map(unwrapNode)
+ currJson = JSON.stringify(result)
+ }
+
+ return result
+ }
+ }
+
+ // Plugin to remove empty elements (no attrs, no content)
+ // Runs repeatedly until no more empty elements exist
+ const removeEmptyElementsPlugin = () => {
return (tree) => {
const isEmptyElement = (node) => {
if (typeof node === 'string') return false
@@ -134,63 +203,39 @@ export async function formatHtmlForPrompt({
return !hasAttrs && !hasContent
}
- const isWhitespaceOnly = (node) => {
- return typeof node === 'string' && node.trim().length === 0
- }
-
- const collapseConsecutive = (content) => {
+ const removeEmpty = (content) => {
if (!content || !Array.isArray(content)) return content
- const result: typeof content = []
- let i = 0
- while (i < content.length) {
- const current = content[i]
-
- // Process children first (recursive)
- if (typeof current !== 'string' && current.content) {
- current.content = collapseConsecutive(current.content)
- }
-
- // Check for consecutive empty elements (skipping whitespace-only strings)
- if (isEmptyElement(current)) {
- const emptyElements = [current]
- let j = i + 1
-
- while (j < content.length) {
- if (isWhitespaceOnly(content[j])) {
- j++
- continue
- }
- if (isEmptyElement(content[j]) && content[j].tag === current.tag) {
- emptyElements.push(content[j])
- j++
- continue
- }
- break
+ return content
+ .map(node => {
+ if (typeof node === 'string') return node
+ if (node.content) {
+ node.content = removeEmpty(node.content)
}
-
- if (emptyElements.length >= 3) {
- // Collapse: keep only one element
- result.push(current)
- i = j
- continue
- }
- }
-
- result.push(current)
- i++
- }
- return result
+ return node
+ })
+ .filter(node => !isEmptyElement(node))
}
- return collapseConsecutive(tree)
+ // Apply multiple passes until stable
+ let result = removeEmpty(tree)
+ let prevJson = ''
+ let currJson = JSON.stringify(result)
+ while (prevJson !== currJson) {
+ prevJson = currJson
+ result = removeEmpty(result)
+ currJson = JSON.stringify(result)
+ }
+
+ return result
}
}
// Process HTML
const processor = posthtml()
.use(removeTagsAndAttrsPlugin())
- .use(collapseEmptyElementsPlugin())
+ .use(removeEmptyElementsPlugin())
+ .use(unwrapNestedWrappersPlugin())
.use(beautify({
rules: {
indent: 1, // 1-space indent