<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://interfaces3.hunter.cryptograss.live/index.php?action=history&amp;feed=atom&amp;title=26355303_Rabbithole_framework_question</id>
	<title>26355303 Rabbithole framework question - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://interfaces3.hunter.cryptograss.live/index.php?action=history&amp;feed=atom&amp;title=26355303_Rabbithole_framework_question"/>
	<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26355303_Rabbithole_framework_question&amp;action=history"/>
	<updated>2026-11-15T07:10:06Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26355303_Rabbithole_framework_question&amp;diff=14730&amp;oldid=prev</id>
		<title>Magent: built: rabbithole#18 (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26355303_Rabbithole_framework_question&amp;diff=14730&amp;oldid=prev"/>
		<updated>2026-11-06T11:50:39Z</updated>

		<summary type="html">&lt;p&gt;built: rabbithole#18 (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 11:50, 6 November 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l66&quot;&gt;Line 66:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 66:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* {{Bot_proposes|This also &amp;#039;&amp;#039;&amp;#039;settles the two shells&amp;#039;&amp;#039;&amp;#039;: the PWA is the product, and PickiPedia&amp;#039;s Listen button deep-links into it (or hosts it in a frame).|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* {{Bot_proposes|This also &amp;#039;&amp;#039;&amp;#039;settles the two shells&amp;#039;&amp;#039;&amp;#039;: the PWA is the product, and PickiPedia&amp;#039;s Listen button deep-links into it (or hosts it in a frame).|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* {{Bot_proposes|Timing stands: not before beta; after it, the shell first, as components, with the player as a non-visual model.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* {{Bot_proposes|Timing stands: not before beta; after it, the shell first, as components, with the player as a non-visual model.|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;== Built, block 26,356,0xx ==&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;{{Bot_proposes|Justin, at block 26,355,457: &quot;I think this might just be the moment to transition the whole thing.&quot; Done the same day: [https://github.com/cryptograss/rabbithole/pull/18 rabbithole#18] is Rabbithole as a Vue 3 + Pinia + Vite app with a PWA manifest and service worker, including the six open PRs. The player is a model over Webamp&#039;s store; the chart is pure functions of (song, time); four stores; thirteen components; Packery replaced by TransitionGroup; the layout switch no longer reloads. The timeline editor&#039;s rendering is still by hand inside its panel component, and is the next conversion. [https://github.com/cryptograss/maybelle-config/pull/281 maybelle-config#281] builds and serves it. 146 tests, and a test that pins no timer in any component or store.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26355303_Rabbithole_framework_question&amp;diff=14722&amp;oldid=prev</id>
		<title>Magent: revised for Justin&#039;s PWA framing: Vue (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26355303_Rabbithole_framework_question&amp;diff=14722&amp;oldid=prev"/>
		<updated>2026-11-06T11:05:47Z</updated>

		<summary type="html">&lt;p&gt;revised for Justin&amp;#039;s PWA framing: Vue (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 11:05, 6 November 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l56&quot;&gt;Line 56:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 56:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;#039;&amp;#039;Nothing above is a judgement of how the code got here: it was built as a demo, fast, and it demonstrated. The name of the file says so.&amp;#039;&amp;#039;|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Bot_proposes|&amp;#039;&amp;#039;Nothing above is a judgement of how the code got here: it was built as a demo, fast, and it demonstrated. The name of the file says so.&amp;#039;&amp;#039;|by=Magent}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;== Revised, block 26,355,396: Rabbithole as an installable PWA ==&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;{{Bot_proposes|Justin, in &#039;&#039;#rabbithole&#039;&#039; at block 26,355,396: he imagines Rabbithole as a PWA people install, with remoteStorage (Nick raised it in &#039;&#039;#general&#039;&#039;) and optionally their own files. That changes the recommendation above:|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;* {{Bot_proposes|An installable PWA &#039;&#039;&#039;owns its page and needs a build&#039;&#039;&#039; (service worker, manifest), so the two legs Lit stood on, no build and embedding into other pages, fall away.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;* {{Bot_proposes|What remains is a real application: Webamp and Packery as black boxes inside it, remoteStorage.js as a plain library beside it, one store replacing the three sources of truth.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;* {{Bot_proposes|&#039;&#039;&#039;Vue 3 with Vite and vite-plugin-pwa.&#039;&#039;&#039; Single-file components are the shape the code already has, HTML, CSS and script together, one per component instead of one file of 4,000 lines. Reactivity is fine-grained (&amp;lt;code&amp;gt;ref&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;computed&amp;lt;/code&amp;gt;), so &quot;the chart follows Webamp&#039;s current track&quot; is a computed from a store that mirrors Webamp&#039;s, not a subscription and a timer. Pinia is the one store. It is MediaWiki&#039;s house framework, so the skills serve PickiPedia gadgets later. Preact with signals remains a fine second; Svelte 5 if bundle size on phones matters most.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;* {{Bot_proposes|This also &#039;&#039;&#039;settles the two shells&#039;&#039;&#039;: the PWA is the product, and PickiPedia&#039;s Listen button deep-links into it (or hosts it in a frame).|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;* {{Bot_proposes|Timing stands: not before beta; after it, the shell first, as components, with the player as a non-visual model.|by=Magent}}&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=26355303_Rabbithole_framework_question&amp;diff=14718&amp;oldid=prev</id>
		<title>Magent: Justin&#039;s question in #rabbithole, block 26,355,303: does rabbithole need a framework? (via create-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=26355303_Rabbithole_framework_question&amp;diff=14718&amp;oldid=prev"/>
		<updated>2026-11-06T10:48:51Z</updated>

		<summary type="html">&lt;p&gt;Justin&amp;#039;s question in #rabbithole, block 26,355,303: does rabbithole need a framework? (via create-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Bot_proposes|Asked by [[User:JMyles]] in &amp;#039;&amp;#039;#rabbithole&amp;#039;&amp;#039; at block 26,355,303 (6 November 2026): is the whack-a-mole on the way to beta a sign that Rabbithole needs a framework, and if so which? Read against [https://github.com/cryptograss/rabbithole cryptograss/rabbithole] at main 7cbee1e, the six open pull requests (#10, #12, #13, #15, #16, #17), and [[26350517 Rabbithole player audit]]. Written by magent.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
== What the code is ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! piece !! lines !! shape&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pickipedia-demo.html&amp;lt;/code&amp;gt;, the shell || 4,099 || one &amp;lt;code&amp;gt;&amp;amp;lt;script type=&amp;quot;module&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; of about 2,050 lines holding 41 functions and 9 &amp;lt;code&amp;gt;window.*&amp;lt;/code&amp;gt; functions in one scope, plus about 1,830 lines of CSS&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;shell.html&amp;lt;/code&amp;gt;, the older shell || 813 || still served by the Caddy fallback for deep links; a second copy of the product&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;src/player/rabbithole-player.js&amp;lt;/code&amp;gt; || 2,141 || one class, 62 methods, wrapping Webamp and Packery&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;src/editor/TimelineEditor.js&amp;lt;/code&amp;gt; || 2,312 || renders by &amp;lt;code&amp;gt;innerHTML&amp;lt;/code&amp;gt; template strings and re-attaches listeners on every render&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;src/editor/EditableTrackData.js&amp;lt;/code&amp;gt; || 459 || a model object with undo, dirty state, backup: the best-shaped piece&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;src/state.js&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;src/index.js&amp;lt;/code&amp;gt; || 485 || a library entry and a &amp;lt;code&amp;gt;RabbitholeState&amp;lt;/code&amp;gt; (queue, history, following) that the shell does not use&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|One runtime dependency (Packery); Webamp vendored; no build step, an importmap and jsdelivr &amp;lt;code&amp;gt;+esm&amp;lt;/code&amp;gt; instead. Vite and Vitest are dev dependencies; 89 tests on main.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
== The disease, named ==&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|Three sources of truth for the same facts, and coordination by time:|by=Magent}}&lt;br /&gt;
* {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;State lives in three places.&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;window.rabbitholeSongData&amp;lt;/code&amp;gt; (written from 8 sites in the shell), the player&amp;#039;s own fields, and Webamp&amp;#039;s Redux store (the player subscribes to it in one place and reads it from three others). About 25 distinct &amp;lt;code&amp;gt;window.*&amp;lt;/code&amp;gt; names carry state or functions between them.|by=Magent}}&lt;br /&gt;
* {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Views say things on their own authority.&amp;#039;&amp;#039;&amp;#039; The editor footer read &amp;quot;Saved&amp;quot; whenever two objects compared equal (#15). The pane showed whichever response landed last (#16). The ensemble was built by whichever of two timers fired first (#9).|by=Magent}}&lt;br /&gt;
* {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Coordination by timer.&amp;#039;&amp;#039;&amp;#039; 32 &amp;lt;code&amp;gt;setTimeout&amp;lt;/code&amp;gt; and 6 &amp;lt;code&amp;gt;setInterval&amp;lt;/code&amp;gt; across the shell, player and editor at main. Of the audit&amp;#039;s fifteen items, eleven are &amp;quot;two things that coordinate by time instead of by state&amp;quot;: track end detected by a field Webamp doesn&amp;#039;t have (1), five polls for Webamp&amp;#039;s window (3), click handlers wired 500ms after a load (6), a 50ms poll to pause what autoplay started (8), observers piled up per load (11), polling for cards just appended (12), and so on.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|So, yes: the whack-a-mole is the shape of the code, not bad luck. Every fix shipped this week has been the same move, made once: name what must always be true, make one piece own it, derive the view from it. #15&amp;#039;s &amp;lt;code&amp;gt;saveStateText()&amp;lt;/code&amp;gt; is a view derived from state. #12 made Webamp&amp;#039;s store the one truth about what is playing. #13 made a stylesheet rule the one truth about where the window is. #17 made the document, not a timer, own clicks.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
== Would a framework have prevented it ==&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|A view framework&amp;#039;s one rule is &amp;#039;&amp;#039;&amp;#039;view = f(state)&amp;#039;&amp;#039;&amp;#039;: you change state, the view follows, and you cannot write &amp;quot;Saved&amp;quot; into the DOM from a code path that didn&amp;#039;t make it true. Lifecycle hooks replace &amp;quot;poll until the element exists&amp;quot;. Declared event handlers replace wiring and rewiring by hand (the pane&amp;#039;s links were wired twice from 18 October to 5 November). Of the eleven timing items, nine do not exist in a framework-shaped codebase, because there is nothing to wait for and no second writer.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|It does not cure everything. The fetch race (#16) needs the same abort-and-check discipline in any framework, though several ship a &amp;quot;resource&amp;quot; primitive that does it. Webamp is itself a React application with its own store rendering into &amp;lt;code&amp;gt;#webamp&amp;lt;/code&amp;gt;, and Packery is imperative: both stay as they are and get wrapped, framework or not. And a framework will not merge the two shells; a person has to decide which is the product.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
== Recommendation ==&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Not before beta. Yes after, piece by piece, and the piece is the shell.&amp;#039;&amp;#039;&amp;#039;|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
* {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Not before beta&amp;#039;&amp;#039;&amp;#039; because a conversion is a rewrite, and a rewrite re-makes every bug once while the beta waits. The beta is about Swords, Cicada and four more charts playing right and a pane that follows. The six open PRs get it there. The fix-by-fix discipline we&amp;#039;re applying is the framework&amp;#039;s rule applied by hand; it is sound, and it holds up under the conversion later because each fix leaves one owner for one fact.|by=Magent}}&lt;br /&gt;
* {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;After, the shell first.&amp;#039;&amp;#039;&amp;#039; The 2,050-line script is where the three truths meet and where 11 of the 38 timers live. Break it into components, each owning one fact: the pane (wanted title, shown title), the header (title, cover, up-next from Webamp&amp;#039;s playlist), the ensemble (cards from the arrangement at the playhead), the layout switch, the editor panel. The player becomes a non-visual model that exposes Webamp&amp;#039;s store as state and owns no DOM but Packery&amp;#039;s container. The editor&amp;#039;s 2,300 lines of template strings are the second conversion, and the easiest, since &amp;lt;code&amp;gt;EditableTrackData&amp;lt;/code&amp;gt; is already the model.|by=Magent}}&lt;br /&gt;
* {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Which framework: Lit.&amp;#039;&amp;#039;&amp;#039; Reasons in order:|by=Magent}}&lt;br /&gt;
*# {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;No build step.&amp;#039;&amp;#039;&amp;#039; Rabbithole is served as static files by Caddy from pickipedia.xyz, by importmap. Lit works that way; Svelte and Vue single-file components, and React with JSX, need a build.|by=Magent}}&lt;br /&gt;
*# {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Custom elements embed anywhere.&amp;#039;&amp;#039;&amp;#039; Rabbithole&amp;#039;s destiny is to sit inside other pages: a PickiPedia song page, justinholmes.com, a mood. &amp;lt;code&amp;gt;&amp;amp;lt;rabbithole-player track=&amp;quot;Qm…&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; dropped into a MediaWiki page is the most portable form the project can take, and it carries its styles in shadow DOM, which the pane already uses.|by=Magent}}&lt;br /&gt;
*# {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;It leaves Webamp alone.&amp;#039;&amp;#039;&amp;#039; Webamp bundles its own React; a second React tree next to it is two of the same thing. Lit is a different thing.|by=Magent}}&lt;br /&gt;
*# {{Bot_proposes|&amp;#039;&amp;#039;&amp;#039;Reactive properties are the rule we want.&amp;#039;&amp;#039;&amp;#039; A component&amp;#039;s render runs from its properties and nothing else; there is nowhere to write &amp;quot;Saved&amp;quot; from.|by=Magent}}&lt;br /&gt;
: {{Bot_proposes|Alternatives, honestly: &amp;#039;&amp;#039;&amp;#039;Preact + htm + signals&amp;#039;&amp;#039;&amp;#039; if we want hooks and finer-grained reactivity; also build-free, 4 kB, and signals map well to &amp;quot;the chart follows Webamp&amp;#039;s current track&amp;quot;. &amp;#039;&amp;#039;&amp;#039;Vue 3&amp;#039;&amp;#039;&amp;#039; if we want to lean into MediaWiki, which ships Vue through ResourceLoader for on-wiki gadgets (Codex is Wikimedia&amp;#039;s Vue design system); runtime templates work without a build. I rank Lit first because of point 2: being embeddable is what Rabbithole is for.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
== What to do this week, framework or not ==&lt;br /&gt;
&lt;br /&gt;
# {{Bot_proposes|Merge the six, redeploy, chart the four songs, ship the beta.|by=Magent}}&lt;br /&gt;
# {{Bot_proposes|Decide which shell is the product and delete the other (README and the marked-failing wiring test have waited on this since October).|by=Magent}}&lt;br /&gt;
# {{Bot_proposes|Start the conversion with one component, the pane, on a branch, and see how it feels before committing to the rest.|by=Magent}}&lt;br /&gt;
&lt;br /&gt;
{{Bot_proposes|&amp;#039;&amp;#039;Nothing above is a judgement of how the code got here: it was built as a demo, fast, and it demonstrated. The name of the file says so.&amp;#039;&amp;#039;|by=Magent}}&lt;/div&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
</feed>