[{"data":1,"prerenderedAt":6964},["ShallowReactive",2],{"changelog":3},[4,49,55,65,76,83,90,98,108,142,151,157,165,188,206,217,224,238,243,249,256,264,272,279,291,296,306,311,317,324,329,335,342,353,358,364,369,375,380,385,391,400,405,410,426,432,439,449,465,494,520,532,538,547,552,559,567,573,579,585,590,595,600,605,611,616,621,626,634,644,668,673,682,687,695,701,710,715,720,725,736,759,764,780,792,802,811,832,838,845,853,863,869,875,884,889,894,902,907,920,926,941,969,981,987,994,999,1006,1011,1016,1021,1026,1032,1042,1047,1053,1063,1089,1095,1100,1110,1121,1128,1136,1144,1149,1155,1160,1168,1178,1183,1188,1197,1205,1210,1216,1222,1227,1233,1255,1267,1273,1287,1292,1298,1303,1309,1314,1321,1330,1337,1343,1348,1353,1359,1365,1372,1382,1388,1393,1404,1409,1415,1420,1425,1430,1435,1440,1445,1451,1459,1465,1471,1483,1503,1508,1513,1520,1525,1532,1543,1550,1558,1568,1580,1590,1595,1600,1605,1611,1621,1626,1639,1647,1653,1658,1676,1681,1687,1693,1698,1704,1709,1714,1719,1724,1729,1736,1741,1762,1767,1783,1788,1793,1799,1817,1827,1834,1841,1847,1867,1872,1880,1886,1893,1898,1903,1908,1913,1918,1923,1929,1934,1940,1945,1950,1955,1960,1965,1970,1975,1980,1986,1994,1999,2004,2009,2017,2030,2035,2044,2049,2054,2061,2067,2072,2077,2083,2091,2097,2102,2107,2112,2118,2124,2137,2143,2148,2153,2158,2163,2168,2177,2182,2188,2193,2199,2204,2209,2216,2225,2234,2240,2245,2250,2255,2261,2266,2271,2276,2281,2290,2297,2302,2311,2317,2327,2333,2339,2344,2354,2369,2379,2394,2405,2422,2433,2449,2455,2461,2468,2477,2500,2507,2512,2517,2523,2530,2536,2543,2560,2571,2586,2591,2611,2628,2644,2652,2657,2662,2670,2675,2684,2695,2704,2718,2730,2735,2746,2764,2772,2789,2799,2808,2818,2833,2846,2851,2860,2868,2880,2888,2898,2904,2910,2919,2936,2945,2951,2959,2966,2972,2987,2997,3003,3021,3033,3041,3048,3056,3062,3069,3074,3079,3093,3103,3109,3118,3124,3131,3144,3149,3157,3162,3169,3176,3186,3192,3197,3202,3217,3226,3235,3244,3261,3268,3273,3278,3283,3297,3306,3315,3322,3330,3338,3356,3361,3366,3373,3378,3383,3388,3397,3403,3410,3416,3422,3430,3437,3444,3452,3459,3469,3475,3481,3488,3495,3505,3511,3518,3536,3541,3546,3554,3560,3567,3573,3595,3600,3609,3624,3629,3637,3651,3659,3665,3671,3676,3682,3687,3696,3701,3715,3730,3747,3754,3759,3779,3791,3799,3808,3816,3821,3829,3837,3845,3852,3861,3869,3876,3887,3893,3899,3905,3913,3923,3936,3942,3948,3954,3962,3977,3988,3994,4001,4009,4019,4036,4041,4050,4062,4080,4088,4099,4104,4116,4121,4140,4151,4159,4164,4179,4184,4199,4212,4219,4224,4229,4236,4243,4250,4256,4263,4272,4285,4290,4300,4311,4319,4327,4337,4348,4354,4361,4374,4391,4406,4411,4416,4421,4432,4439,4458,4466,4478,4484,4496,4504,4515,4526,4531,4537,4542,4551,4556,4562,4569,4575,4582,4590,4596,4602,4608,4618,4624,4630,4637,4643,4649,4664,4671,4688,4695,4702,4708,4714,4720,4727,4734,4739,4750,4756,4762,4768,4777,4786,4793,4800,4807,4813,4819,4827,4846,4855,4860,4872,4880,4885,4895,4901,4906,4918,4925,4932,4940,4946,4952,4960,4974,4982,4989,4994,4999,5007,5013,5021,5027,5034,5044,5052,5060,5067,5072,5087,5094,5104,5113,5119,5126,5135,5143,5153,5163,5171,5177,5187,5194,5201,5210,5218,5226,5236,5251,5258,5264,5271,5278,5297,5308,5314,5322,5334,5343,5352,5367,5374,5382,5391,5398,5404,5412,5417,5424,5431,5441,5446,5453,5464,5470,5477,5483,5489,5497,5504,5512,5518,5526,5536,5542,5550,5557,5564,5571,5576,5582,5597,5606,5613,5619,5625,5632,5641,5648,5659,5666,5672,5678,5689,5695,5706,5713,5718,5723,5728,5734,5741,5747,5754,5760,5768,5776,5783,5795,5812,5822,5831,5836,5844,5857,5864,5872,5879,5888,5895,5902,5909,5917,5922,5928,5934,5940,5948,5957,5970,5975,5982,5990,5995,6001,6009,6020,6026,6035,6040,6052,6058,6072,6084,6093,6099,6105,6114,6120,6125,6131,6142,6151,6156,6161,6171,6178,6186,6193,6199,6205,6210,6216,6224,6230,6242,6247,6252,6257,6262,6274,6281,6288,6297,6313,6323,6328,6335,6342,6349,6354,6370,6375,6385,6394,6407,6417,6426,6448,6458,6466,6474,6480,6486,6491,6496,6503,6509,6520,6525,6537,6542,6552,6571,6578,6587,6596,6604,6610,6619,6631,6640,6649,6658,6667,6678,6697,6715,6722,6729,6741,6755,6761,6768,6773,6780,6790,6802,6815,6827,6833,6842,6850,6856,6862,6888,6911,6917,6922,6944,6949,6954,6959],{"hash":5,"date":6,"subject":7,"files":8,"plus":47,"minus":48,"truncated":28},"3f98030","2026-08-08","public keys get their own \u002Fkeys page — labeled pgp + ssh cards with fingerprints and copyable import commands; the ssh key is self-hosted at \u002Fssh.txt instead of github, and the pgp uid moved to pgp@laurensverspeek.nl",[9,12,16,18,22,26,30,33,35,37,41,44],{"path":10,"add":11,"del":11},"README.md",1,{"path":13,"add":14,"del":15},"app\u002Fcomponents\u002FKeyCommand.vue",77,0,{"path":17,"add":11,"del":15},"app\u002Fcomposables\u002FuseCommandPalette.ts",{"path":19,"add":20,"del":21},"app\u002Fdata\u002Fgithub.generated.json",104,91,{"path":23,"add":24,"del":25},"app\u002Fdata\u002Fpgp.ts",43,35,{"path":27,"add":28,"del":29},"app\u002Fpages\u002Fcontact.vue",5,59,{"path":31,"add":32,"del":15},"app\u002Fpages\u002Fkeys.vue",188,{"path":34,"add":11,"del":11},"app\u002Futils\u002Fterminal\u002FbrowseCommands.ts",{"path":36,"add":11,"del":15},"app\u002Futils\u002Fterminal\u002FmiscCommands.ts",{"path":38,"add":39,"del":40},"app\u002Futils\u002Fterminal\u002FnetCommands.ts",3,2,{"path":42,"add":43,"del":15},"app\u002Futils\u002Fterminal\u002FsiteFs.ts",7,{"path":45,"add":46,"del":43},"e2e\u002Fpages2.spec.ts",17,470,198,{"hash":50,"date":6,"subject":51,"files":52,"plus":11,"minus":11},"d5d78d2","update node in cicd",[53],{"path":54,"add":11,"del":11},".github\u002Fworkflows\u002Fdeploy_gh-pages.yml",{"hash":56,"date":6,"subject":57,"files":58,"plus":64,"minus":61},"10de157","explicit approval for install scripts",[59,62],{"path":60,"add":15,"del":61},"package-lock.json",45,{"path":63,"add":64,"del":15},"package.json",6,{"hash":66,"date":67,"subject":68,"files":69,"plus":75,"minus":28},"c6f7fa7","2026-07-16","share-target url strip survives hydration (nuxt finalizes with a replaceState of the original url after app:mounted — wait it out) and the snake header shortens on narrow boards, since the 44-column hint was the frame's real overflow",[70,73],{"path":71,"add":43,"del":72},"app\u002Fplugins\u002FshareTarget.client.ts",4,{"path":74,"add":28,"del":11},"app\u002Futils\u002Fgames\u002Fsnake.ts",12,{"hash":77,"date":67,"subject":78,"files":79,"plus":82,"minus":28},"34cb1c6","runtime-check fixes: the share-target url cleanup goes through router.replace (vue-router overrides a raw replaceState, so the one-shot params survived reloads), and the snake phone boards shrink further — a coarse column is really ~8.8px, so 22\u002F18 cells still clipped at 375\u002F320; 19\u002F16 fit with margin",[80,81],{"path":71,"add":39,"del":11},{"path":74,"add":28,"del":72},8,{"hash":84,"date":67,"subject":85,"files":86,"plus":64,"minus":64},"2dfae06","readme catch-up: wget\u002Fkillall\u002Fpair in the terminal tour, files multi-select, the installed-pwa share sheet + icon badge, this round's mobile fixes, fresh 375px screenshot",[87,88],{"path":10,"add":64,"del":64},{"path":89,"add":15,"del":15},"docs\u002Fscreenshots\u002Fmobile.png",{"hash":91,"date":67,"subject":92,"files":93,"plus":40,"minus":40},"95ebd95","un-export the pair session's module-local constants — knip flagged PAIR_PID and PAIR_CODE_RE as unused exports",[94,96],{"path":95,"add":11,"del":11},"app\u002Futils\u002Fterminal\u002FeffectProcs.ts",{"path":97,"add":11,"del":11},"realtime\u002Fserver\u002FpairSessions.mjs",{"hash":99,"date":67,"subject":100,"files":101,"plus":107,"minus":82},"2bc630b","museum + architecture catch-up: four new exhibits (wget keeps, the pair terminal, a citizen of your phone, the frame budget) and the docs learn this round's load-bearing truths — composited window position that every transform state must compose in, the lazy-mount focus trap both consoles now poll around, the one-boot postgenerate harness, the pinned mime maps, and the honest-503-for-any-non-404 rule",[102,104],{"path":103,"add":64,"del":40},"app\u002Fdata\u002Fmuseum.ts",{"path":105,"add":106,"del":64},"docs\u002Farchitecture.md",38,44,{"hash":109,"date":67,"subject":110,"files":111,"plus":141,"minus":11},"f830931","pair terminal: share your live shell over the relay — 'pair' mints a 4-char code, 'pair join \u003Ccode>' streams the host transcript in real time (30-line backlog for late joiners), 'pair allow' hands guests the keyboard so their lines run on the host's real shell queue; server-authoritative rooms (rebuilt line frames, whitelisted types, clipped text, flood gates, 8-guest cap), host shows in ps as pair-share.sh pid 2525 and kill ends the broadcast, solo copy when no relay; 16 new relay checks + 8 mirrorable unit tests",[112,114,116,119,121,123,126,128,130,133,135,138],{"path":113,"add":40,"del":15},"app\u002Fcomposables\u002FuseProcessTable.ts",{"path":115,"add":11,"del":15},"app\u002Fcomposables\u002FuseTerminal.ts",{"path":117,"add":118,"del":15},"app\u002Futils\u002Fgames\u002FpairWatch.ts",132,{"path":95,"add":120,"del":15},18,{"path":38,"add":122,"del":15},60,{"path":124,"add":125,"del":15},"app\u002Futils\u002Fterminal\u002FpairShare.ts",161,{"path":127,"add":72,"del":15},"app\u002Futils\u002Fterminal\u002Ftypes.ts",{"path":129,"add":28,"del":15},"realtime\u002Fcursors-server.mjs",{"path":131,"add":132,"del":11},"realtime\u002Fprotocol.d.ts",54,{"path":97,"add":134,"del":15},206,{"path":136,"add":137,"del":15},"scripts\u002Frelay-check.mjs",84,{"path":139,"add":140,"del":15},"tests\u002FpairShare.test.ts",55,782,{"hash":143,"date":67,"subject":144,"files":145,"plus":150,"minus":11},"2bd7c0c","the installed pwa is an os share target — manifest share_target routes shared title\u002Ftext\u002Furl to \u002Fdesktop, where a client plugin (after the vfs restore) pins the content as a real sticky note in ~\u002Fstickies, opens the notes app via the same pending-app hook desktop \u003Capp> uses, and strips the one-shot params from the url",[146,148],{"path":71,"add":147,"del":15},42,{"path":149,"add":82,"del":11},"nuxt.config.ts",50,{"hash":152,"date":67,"subject":153,"files":154,"plus":120,"minus":15},"ff7aaee","installed pwa wears the unread-mail count on its app icon — the badging api mirrors the same single-source read-state the lvos mail icon and terminal mail use, clears as you read, and only engages in standalone display mode so browser tabs pay nothing",[155],{"path":156,"add":120,"del":15},"app\u002Fplugins\u002FappBadge.client.ts",{"hash":158,"date":67,"subject":159,"files":160,"plus":163,"minus":164},"0280280","tile-windows e2e polls until the glide settles — window position is a transitioned transform since the compositor change, so tile\u002Fsnap animate to their slots and an immediate boundingbox read lands mid-flight",[161],{"path":162,"add":163,"del":164},"e2e\u002Flvos2.spec.ts",13,9,{"hash":166,"date":67,"subject":167,"files":168,"plus":186,"minus":187},"55fd4fa","files app learns multi-select — ctrl\u002Fcmd-click toggles, shift-click and shift-arrows range-select from the anchor, delete and the context menu act on the whole selection ('move 3 to bin', rename stays single), and dragging a selected row moves the whole nested-pruned selection; pure toggle\u002Frange\u002Fprune helpers extracted and tested",[169,172,176,178,180,183],{"path":170,"add":171,"del":75},"app\u002Fcomponents\u002Fdesktop\u002FDesktopFiles.vue",21,{"path":173,"add":174,"del":175},"app\u002Fcomposables\u002FuseFileExplorer.ts",97,28,{"path":177,"add":120,"del":15},"app\u002Futils\u002FmultiSelect.ts",{"path":179,"add":82,"del":15},"app\u002Futils\u002Fterminal\u002Ffilesystem.ts",{"path":181,"add":182,"del":11},"tests\u002Ffilesystem.test.ts",15,{"path":184,"add":185,"del":15},"tests\u002FmultiSelect.test.ts",36,195,41,{"hash":189,"date":67,"subject":190,"files":191,"plus":204,"minus":205},"021822e","postgenerate boots one chromium and one static server for all three capture steps — the og rasterizer, resume pdf and pwa screenshots each launched their own browser (two also their own server) back to back; scripts\u002Fpostgenerate.mjs runs them as steps with per-step non-fatal warns, npm run resume = --only=resume",[192,193,195,198,201],{"path":63,"add":40,"del":40},{"path":194,"add":43,"del":163},"scripts\u002Fgenerate-og-png.mjs",{"path":196,"add":28,"del":197},"scripts\u002Fgenerate-pwa-screenshots.mjs",25,{"path":199,"add":82,"del":200},"scripts\u002Fgenerate-resume.mjs",27,{"path":202,"add":203,"del":15},"scripts\u002Fpostgenerate.mjs",52,74,67,{"hash":207,"date":67,"subject":208,"files":209,"plus":216,"minus":39},"e08ca9d","static server and time-machine sw agree on mime types — the server map lacked webmanifest\u002Fwebm\u002Fmp4 (pwa manifest + project hover videos went out as octet-stream in e2e\u002Flocal runs) and the sw lacked the videos; a drift-guard test now pins the two maps together",[210,212,214],{"path":211,"add":39,"del":11},"public\u002Fsw-timemachine.js",{"path":213,"add":182,"del":40},"scripts\u002Fstatic-server.mjs",{"path":215,"add":24,"del":15},"tests\u002FstaticServerTypes.test.ts",61,{"hash":218,"date":67,"subject":219,"files":220,"plus":223,"minus":40},"cb29ac9","terminal scrollback lines are v-memo'd — settled lines are immutable, but line-by-line streams (tail -f, animated commands) re-diffed the whole list per appended line, o(n²) toward the 1500-line cap; a display:contents wrapper per line gives v-memo a real element without touching layout or selectors",[221],{"path":222,"add":223,"del":40},"app\u002Fcomponents\u002FTerminalConsole.vue",10,{"hash":225,"date":67,"subject":226,"files":227,"plus":46,"minus":72},"e995e9a","effects perf polish: matrix rain reads its glyph color once per resize instead of forcing a style flush every frame, the screensaver logo writes its transform imperatively instead of re-patching the component 60 times a second, and the life wallpaper \u002F sysmon localstorage walk \u002F globe sphere rebuild all skip ticks while the tab is hidden",[228,230,232,234,236],{"path":229,"add":28,"del":11},"app\u002Fcomponents\u002FMatrixRain.vue",{"path":231,"add":40,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopGlobe.vue",{"path":233,"add":40,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopLifeWallpaper.vue",{"path":235,"add":64,"del":40},"app\u002Fcomponents\u002Fdesktop\u002FDesktopScreensaver.vue",{"path":237,"add":40,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopSysMon.vue",{"hash":239,"date":67,"subject":240,"files":241,"plus":28,"minus":15},"f3cb82e","terminal quick-key labels stay inside their pills — flex-shrunk buttons let '⊞ split' and 'space' paint past the rounded border on phones; clip and ellipsize instead",[242],{"path":222,"add":28,"del":15},{"hash":244,"date":67,"subject":245,"files":246,"plus":43,"minus":40},"c3ffdce","mobile menu footer actions become real touch targets — the terminal\u002Fgithub\u002Flinkedin\u002Fkeybase\u002Femail row rendered as 23px-tall bare text; padded to the 44px standard with a tightened gap so 320px still fits them comfortably",[247],{"path":248,"add":43,"del":40},"app\u002Fcomponents\u002FAppNavbar.vue",{"hash":250,"date":67,"subject":251,"files":252,"plus":255,"minus":75},"d7d9980","hero crosshair rides the compositor — it measured the hero and moved via left\u002Ftop on every mousemove over the most-visited element on the site, forcing a reflow per move; the rect is cached (invalidated on scroll\u002Fresize) and the lines and readout track the pointer with transforms",[253],{"path":254,"add":255,"del":75},"app\u002Fcomponents\u002FHeroCrosshair.vue",20,{"hash":257,"date":67,"subject":258,"files":259,"plus":263,"minus":15},"5e75659","contact page phone fixes: the wizard titlebar ellipsizes to one line instead of wrapping to three at 320px (the exact fix the terminal titlebar got last round), and the tap-to-copy ssh\u002Fpgp key lines grow from an 18px-tall text sliver into real touch targets",[260,262],{"path":261,"add":64,"del":15},"app\u002Fcomponents\u002FContactWizard.vue",{"path":27,"add":28,"del":15},11,{"hash":265,"date":67,"subject":266,"files":267,"plus":197,"minus":15},"c40dfb1","taskbar and start-menu touch targets: the clock (only way into the calendar) measured 35x18 on a phone, volume and the tray toggle 22px wide, the start menu's system rows 34px — all floored to the coarse-pointer standard the search rows already meet, with wallpaper swatches kept square",[268,270],{"path":269,"add":163,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopStartMenu.vue",{"path":271,"add":75,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopTaskbar.vue",{"hash":273,"date":67,"subject":274,"files":275,"plus":278,"minus":82},"383d371","pixel-world minimap stops rescanning all 16k cells per frame — the board paints to an offscreen canvas keyed on its version, so a pan\u002Fzoom\u002Fcursor frame is one drawImage plus the viewport rectangle",[276],{"path":277,"add":278,"del":82},"app\u002Fcomponents\u002FWorldCanvas.vue",24,{"hash":280,"date":67,"subject":281,"files":282,"plus":290,"minus":197},"c21597e","windows move on the compositor: position rides --wx\u002F--wy custom props composed into transform instead of left\u002Ftop, so dragging a glass window no longer reflows and re-rasterizes its backdrop blur every pointermove; drag\u002Fresize writes are raf-coalesced, an is-manipulated class suppresses the genie transition mid-drag, and every transform state (genie, peek, open keyframe, reduced-motion) composes the position back in",[283,285,287],{"path":284,"add":263,"del":39},"app\u002Fcomponents\u002FWebDesktop.vue",{"path":286,"add":171,"del":72},"app\u002Fcomponents\u002Fdesktop\u002FDesktopWindow.vue",{"path":288,"add":289,"del":120},"app\u002Fcomposables\u002FuseWindowManager.ts",56,88,{"hash":292,"date":67,"subject":293,"files":294,"plus":150,"minus":15},"d249239","wget downloads a url into the real vfs — curl prints, wget saves: filename from the path or -O, curl's 10s timeout, and a 64k clip because the filesystem lives in localstorage; the file is immediately cat\u002Fvim\u002Fgrep-able",[295],{"path":38,"add":150,"del":15},{"hash":297,"date":67,"subject":298,"files":299,"plus":137,"minus":40},"f34e0c6","killall \u003Cname> stops every matching process by name over the same unified table kill uses — exact match first then prefix, case-insensitive, with the familiar not-running\u002Fnot-permitted refusals and tab completion from ps",[300,303,304],{"path":301,"add":302,"del":11},"app\u002Futils\u002Fterminal\u002FeffectCommands.ts",19,{"path":95,"add":278,"del":15},{"path":305,"add":187,"del":11},"tests\u002FeffectProcs.test.ts",{"hash":307,"date":67,"subject":308,"files":309,"plus":46,"minus":43},"3cb0ecc","snake board shrinks to fit phones — 26 cells render as 51 monospace columns (~357px), overflowing the 343px\u002F288px game frame at 375\u002F320 so the right wall and any food there were invisible mid-game; 22 cells under 480px, 18 under 350px, every surface (terminal, 404 shell, lvos app) picks it up",[310],{"path":74,"add":46,"del":43},{"hash":312,"date":67,"subject":313,"files":314,"plus":46,"minus":15},"ad83642","tmux splits are usable on phones: panes stack vertically below 640px (a 186px half-pane clipped the prompt and piled the quick keys into overlapping pills) and overflow becomes clip so the input's caret-scroll can't shove the pane sideways",[315],{"path":316,"add":46,"del":15},"app\u002Fcomponents\u002FTerminalPanes.vue",{"hash":318,"date":67,"subject":319,"files":320,"plus":164,"minus":39},"7c29bc3","shareable ?v= links resolve plain commit hashes — the plugin skipped the baked git log, so any sha that wasn't a deploy tip (most of what \u002Fchangelog and git log show) silently stayed in the present while git checkout travelled; also lowercase the 'first shipped in' compare so uppercase hashes don't print it spuriously",[321,323],{"path":322,"add":28,"del":11},"app\u002Fplugins\u002FtimeTravel.client.ts",{"path":36,"add":72,"del":40},{"hash":325,"date":67,"subject":326,"files":327,"plus":72,"minus":11},"26496d2","time machine: a snapshot fetch that answers 403\u002F429\u002F5xx (or an opaque proxy block) now renders the honest 503 archive-unreachable page — only a real cdn 404 means 'did not exist yet'; the r41 fix caught thrown fetches but any non-ok response still fell into the 404 bucket",[328],{"path":211,"add":72,"del":11},{"hash":330,"date":67,"subject":331,"files":332,"plus":75,"minus":15},"85b3933","command palette focuses its input on the lazy first mount — the immediate watch's nextTick fires during setup, before the async component renders, so first-open typing and escape were dead until a second open; poll the ref briefly on mount like the terminal overlay does",[333],{"path":334,"add":75,"del":15},"app\u002Fcomponents\u002FCommandPalette.vue",{"hash":336,"date":67,"subject":337,"files":338,"plus":182,"minus":72},"b79c815","museum + architecture catch-up: installing is real now (manifest link via VitePwaManifest, chip-dismissal keeps the terminal install working, rich install sheet), and the time-machine bar's mouse-only hover-peek + honest cdn-unreachable page are documented",[339,340,341],{"path":19,"add":40,"del":40},{"path":103,"add":11,"del":11},{"path":105,"add":75,"del":11},{"hash":343,"date":67,"subject":344,"files":345,"plus":352,"minus":11},"adca398","pwa rich install sheet: bake manifest screenshots (wide lvos desktop + narrow home, captured from the real build by a new postgenerate step) so android shows the preview bottom-sheet install ui instead of the bare mini prompt",[346,348,349,350],{"path":347,"add":39,"del":15},".gitignore",{"path":149,"add":43,"del":15},{"path":63,"add":11,"del":11},{"path":196,"add":351,"del":15},69,80,{"hash":354,"date":67,"subject":355,"files":356,"plus":72,"minus":11},"7d70883","e2e static server serves .wasm with its real mime type so the sqlite content engine streaming-compiles instead of falling back to arraybuffer instantiation every run",[357],{"path":213,"add":72,"del":11},{"hash":359,"date":67,"subject":360,"files":361,"plus":28,"minus":15},"f3999a9","terminal titlebar stays one line on phones — the user@domain title wrapped to three lines at 320px, now it ellipsizes",[362],{"path":363,"add":28,"del":15},"app\u002Fcomponents\u002FTerminalOverlay.vue",{"hash":365,"date":67,"subject":366,"files":367,"plus":28,"minus":15},"77a0588","palette rows get the 44px coarse-pointer treatment the start-menu search rows already have",[368],{"path":334,"add":28,"del":15},{"hash":370,"date":67,"subject":371,"files":372,"plus":28,"minus":15},"3b01e5e","lock screen: a wrong guess during the 900ms unlock grace no longer still unlocks (any new attempt revokes the pending timer), and the judged-guess hint keeps side padding at 320px",[373],{"path":374,"add":28,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopLockScreen.vue",{"hash":376,"date":67,"subject":377,"files":378,"plus":40,"minus":40},"8cff9a6","goto learns keyboard and status — both real prerendered pages the adventure and status bar point at, yet missing from GOTO_PAGES; also swap the stale 'goto uses' example for 'goto keyboard'",[379],{"path":34,"add":40,"del":40},{"hash":381,"date":67,"subject":382,"files":383,"plus":120,"minus":11},"d2ad528","time machine: tell the truth when jsdelivr is unreachable — a blocked\u002Foffline cdn rendered '404 — that page did not exist yet' for every page including home; network failure now gets its own 503 'snapshot archive unreachable' page with the return link, while a real absent-from-snapshot path keeps the 404",[384],{"path":211,"add":120,"del":11},{"hash":386,"date":67,"subject":387,"files":388,"plus":11,"minus":11},"70a29fe","blog: stop advertising the removed uses command — the rebuild post now cites blog as its second same-data example, so following the post's own instructions can't hit command-not-found",[389],{"path":390,"add":11,"del":11},"content\u002Fblog\u002Frebuilding-this-site.md",{"hash":392,"date":67,"subject":393,"files":394,"plus":182,"minus":39},"2e530e1","pin the footer build hash to --short=7 and match hashes both ways in findCommit — rev-parse --short honours core.abbrev=auto (8 chars on a ci clone), so the baked stamp could never match the 7-char git-log hashes and the stamp's git show printed 'bad revision'; the symmetric match also lets a full 40-char sha from github resolve",[395,397,398],{"path":396,"add":72,"del":11},"app\u002Futils\u002Fterminal\u002FgitLog.ts",{"path":149,"add":28,"del":40},{"path":399,"add":64,"del":15},"tests\u002FgitLog.test.ts",{"hash":401,"date":67,"subject":402,"files":403,"plus":72,"minus":40},"b6a15ab","mobile menu above the fixed chrome: the full-screen menu sat at z-index 29 under the tab bar and status bar (both 40), so its terminal\u002Fgithub\u002Flinkedin\u002Fkeybase\u002Femail actions were visible but untappable — taps landed in the tab bar underneath; also pad the bottom for the safe area",[404],{"path":248,"add":72,"del":40},{"hash":406,"date":67,"subject":407,"files":408,"plus":72,"minus":15},"4e4f0c7","time machine: make the bar's hover-peek mouse-only — touch browsers stick :hover on the last tap, so right after tapping the collapse button the bar:hover hold-open rule kept the bar pinned open; tap-to-expand via the label is untouched",[409],{"path":211,"add":72,"del":15},{"hash":411,"date":412,"subject":413,"files":414,"plus":425,"minus":223},"08ecf4b","2026-07-15","fix pwa install, print chrome and time-machine peek: render VitePwaManifest so the manifest link actually ships (the site was never installable, so beforeinstallprompt never fired), keep the deferred prompt after the chip is dismissed so the terminal install command still works, hide the mobile tab bar + floating chrome in print (a4 is narrower than 768px so it repeated on every resume\u002Fkeyboard pdf page), and let the peeked time-machine bar hold itself open on hover instead of flicker-fighting the handle it slides over",[415,417,419,421,423,424],{"path":416,"add":40,"del":15},"app\u002Fapp.vue",{"path":418,"add":28,"del":15},"app\u002Fassets\u002Fscss\u002Fglobal.scss",{"path":420,"add":40,"del":40},"app\u002Fcomponents\u002FPwaInstall.vue",{"path":422,"add":223,"del":72},"app\u002Fcomposables\u002FusePwaInstall.ts",{"path":19,"add":82,"del":72},{"path":211,"add":72,"del":15},31,{"hash":427,"date":412,"subject":428,"files":429,"plus":28,"minus":15},"e66f3ac","fix flaky destroy-mode test: wait for the terminal to unmount before firing — the overlay outlives the ship by ~400ms, so bullets were shredding the dying terminal lines, whose visibility:hidden left the dom with them",[430],{"path":431,"add":28,"del":15},"e2e\u002Fterminal2.spec.ts",{"hash":433,"date":412,"subject":434,"files":435,"plus":263,"minus":72},"62ff692","fix ci: pin the changelog's short hashes to --abbrev=7 — %h honours core.abbrev=auto, which scales with object count, so the same commit rendered 7 chars locally and 8 on a ci clone and broke \u002Fchangelog",[436,437],{"path":396,"add":43,"del":40},{"path":438,"add":72,"del":40},"e2e\u002Fpages.spec.ts",{"hash":440,"date":412,"subject":441,"files":442,"plus":106,"minus":182},"74a73a4","lvos: make hunter2 the one real password — the lock screen now only opens for the most famous password on IRC (same hint the site gate used), judges wrong guesses instead of waving them through, and keeps the bash.org punchline about everyone seeing your *******",[443,445,446],{"path":374,"add":444,"del":263},23,{"path":103,"add":11,"del":11},{"path":447,"add":448,"del":39},"e2e\u002Flvos.spec.ts",14,{"hash":450,"date":412,"subject":451,"files":452,"plus":72,"minus":464},"8b2ed38","open the site: drop the private-preview gate and its hunter2 password — no more curtain in app.vue, no gate key kept on factory reset, and the e2e suite no longer needs to pre-unlock it",[453,454,455,458,460,462],{"path":10,"add":11,"del":11},{"path":416,"add":15,"del":40},{"path":456,"add":15,"del":457},"app\u002Fcomponents\u002FSiteGate.vue",131,{"path":459,"add":40,"del":39},"app\u002Futils\u002FsafeStorage.ts",{"path":45,"add":15,"del":461},22,{"path":463,"add":11,"del":164},"playwright.config.ts",168,{"hash":466,"date":412,"subject":467,"files":468,"plus":492,"minus":493,"truncated":263},"1d1b11d","content: remove the \u002Fuses page, its data module and the terminal 'uses' command — drop it from the navbar, palette (incl. the per-gear-item entries), fs seeds, sitemap and og cards; the adventure workshop now fronts \u002Fkeyboard",[469,471,472,473,475,476,478,481,483,485,487,490],{"path":470,"add":15,"del":11},"CLAUDE.md",{"path":10,"add":11,"del":40},{"path":248,"add":15,"del":11},{"path":474,"add":11,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopBrowser.vue",{"path":17,"add":15,"del":461},{"path":477,"add":11,"del":11},"app\u002Fcomposables\u002FuseSeo.ts",{"path":479,"add":15,"del":480},"app\u002Fdata\u002Fuses.ts",64,{"path":482,"add":11,"del":40},"app\u002Ferror.vue",{"path":484,"add":11,"del":11},"app\u002Fpages\u002Fabout.vue",{"path":486,"add":11,"del":11},"app\u002Fpages\u002Fstats.vue",{"path":488,"add":15,"del":489},"app\u002Fpages\u002Fuses.vue",160,{"path":491,"add":11,"del":11},"app\u002Futils\u002Fgames\u002Fadventure\u002Frooms.ts",29,310,{"hash":495,"date":412,"subject":496,"files":497,"plus":518,"minus":519,"truncated":461},"178c7b1","content: add tappy + itsavirus roles to the timeline, broaden skills (react\u002Ftailwind, ethereum, new databases + infra groups), drop the \u002Fnow page and command, prune the blog to two posts (expanded rebuild story + new git merge-vs-rebase post)",[498,499,500,502,503,504,507,510,511,512,515,516],{"path":470,"add":11,"del":11},{"path":10,"add":40,"del":40},{"path":501,"add":11,"del":40},"app\u002Fcomponents\u002FAboutSkills.vue",{"path":474,"add":11,"del":11},{"path":17,"add":15,"del":11},{"path":505,"add":15,"del":506},"app\u002Fdata\u002Fnow.ts",40,{"path":508,"add":509,"del":39},"app\u002Fdata\u002Fprofile.ts",32,{"path":482,"add":11,"del":11},{"path":484,"add":11,"del":11},{"path":513,"add":15,"del":514},"app\u002Fpages\u002Fnow.vue",174,{"path":486,"add":11,"del":11},{"path":517,"add":11,"del":11},"app\u002Futils\u002Fgames\u002Fadventure\u002Fengine.ts",447,621,{"hash":521,"date":412,"subject":522,"files":523,"plus":530,"minus":531},"595ca6b","content updates",[524,526,527],{"path":525,"add":40,"del":40},"app\u002Fcomponents\u002FHomeWhatIDo.vue",{"path":508,"add":223,"del":223},{"path":528,"add":489,"del":529},"app\u002Fdata\u002Fprojects.ts",158,172,170,{"hash":533,"date":412,"subject":534,"files":535,"plus":182,"minus":72},"64f85e0","museum + docs: note the shareable \u002F?v= time-travel link on the exhibit (four ways in now), and document the shared usePwaInstall + the one-SW PWA\u002Foffline setup (wasm precache, importScripts, offline fallback) in architecture.md",[536,537],{"path":103,"add":11,"del":11},{"path":105,"add":448,"del":39},{"hash":539,"date":412,"subject":540,"files":541,"plus":546,"minus":187},"c2e625e","feat: terminal 'install' \u002F 'pwa' command that fires the browser install prompt — hoisted PwaInstall's deferred beforeinstallprompt into a shared usePwaInstall composable so the chip and the command drive the same prompt (one source of truth)",[542,543,544],{"path":420,"add":64,"del":187},{"path":422,"add":140,"del":15},{"path":36,"add":545,"del":15},26,87,{"hash":548,"date":412,"subject":549,"files":550,"plus":171,"minus":15},"129213e","feat: shareable time-travel links — visiting \u002F?v=\u003Cversion|date|sha> auto-travels to that past build on load via a client plugin that reuses useTimeMachine (resolveRef + travelTo), so you can share a link straight into the site as it was",[551],{"path":322,"add":171,"del":15},{"hash":553,"date":412,"subject":554,"files":555,"plus":82,"minus":11},"8d63f5e","pwa: add a maskable 512 icon (full-bleed >_ mark, glyph in the safe zone) so Android adaptive icons mask cleanly instead of letterboxing; and cap the runtime pages cache at 50 entries so it can't grow unbounded",[556,557],{"path":149,"add":82,"del":11},{"path":558,"add":15,"del":15},"public\u002Fpwa-512-maskable.png",{"hash":560,"date":412,"subject":561,"files":562,"plus":75,"minus":120},"6545095","unify seo: route the blog and project detail pages through the shared useSeo() helper too — they hand-rolled useSeoMeta and omitted twitterDescription, the one field every other page sets; now all 15 pages share one path",[563,565],{"path":564,"add":64,"del":164},"app\u002Fpages\u002Fblog\u002F[slug].vue",{"path":566,"add":64,"del":164},"app\u002Fpages\u002Fprojects\u002F[slug].vue",{"hash":568,"date":412,"subject":569,"files":570,"plus":175,"minus":572},"00adc1a","unify time machine + tidy palette: give the ⌘K travel filter the same live-build fallback (exclude the newest deploy when the manifest has no current flag), and drop the unreachable 'Return to the present' action (its active flag only sets in the instant before travelTo reloads, so it never rendered)",[571],{"path":17,"add":175,"del":572},33,{"hash":574,"date":412,"subject":575,"files":576,"plus":39,"minus":39},"9983bcf","unify time machine: gate the lvOS visit button on row.latest (which carries the no-current-flag fallback) instead of row.deploy.current, so the newest deploy can't show 'live now' yet still offer an enabled 'visit'",[577],{"path":578,"add":39,"del":39},"app\u002Fcomponents\u002Fdesktop\u002FDesktopTimeMachine.vue",{"hash":580,"date":412,"subject":581,"files":582,"plus":28,"minus":448},"5f25f4a","husky: make the staged-file lint NUL-delimited (git -z | grep -z | xargs -0 -r) so it handles filenames with spaces\u002Fnewlines and skips cleanly when nothing matches",[583],{"path":584,"add":28,"del":448},".husky\u002Fpre-commit",{"hash":586,"date":412,"subject":587,"files":588,"plus":39,"minus":40},"ab8094c","fix terminal: route piped command output through writeToPane so a large result (e.g. seq … | cat) respects the 1500-line scrollback cap instead of pushing straight to the pane and over-capping",[589],{"path":115,"add":39,"del":40},{"hash":591,"date":412,"subject":592,"files":593,"plus":72,"minus":11},"f6aefa7","fix offline: precache wasm so Nuxt Content's SQLite engine (_nuxt\u002Fsqlite3*.wasm, 856KB) is available offline — a cold offline launch can now run client-side content queries (palette blog search, terminal blog, latest-posts) instead of failing the fetch",[594],{"path":149,"add":72,"del":11},{"hash":596,"date":412,"subject":597,"files":598,"plus":82,"minus":15},"46f3a96","fix palette mobile: give .palette-label min-width:0 and .palette-hint nowrap+ellipsis+max-width so a long hint (the R39 Time Machine deploy subjects) truncates instead of crushing the label into a 3-line wrap at 375\u002F320px",[599],{"path":334,"add":82,"del":15},{"hash":601,"date":412,"subject":602,"files":603,"plus":39,"minus":40},"ed204d8","fix e2e: the ctrl+c game-interrupt test asserts the game frame unmounts (toHaveCount 0) rather than not.toContainText, which errors on the now-removed element",[604],{"path":431,"add":39,"del":40},{"hash":606,"date":412,"subject":607,"files":608,"plus":461,"minus":39},"31a3569","museum + docs: note the ⌘K time-travel entry on the time-machine exhibit, and document the shared app-name matcher and the new useSeo() \u002F SEO+structured-data setup in architecture.md",[609,610],{"path":103,"add":11,"del":11},{"path":105,"add":171,"del":40},{"hash":612,"date":412,"subject":613,"files":614,"plus":506,"minus":11},"f309dea","command palette: add a Time Machine section with 'Travel to \u003Cversion>' entries (and 'Return to the present' while travelling) so you can jump to a past deploy from ⌘K — reuses the same useTimeMachine the terminal git checkout and the lvOS app use, gated on the service worker being available",[615],{"path":17,"add":506,"del":11},{"hash":617,"date":412,"subject":618,"files":619,"plus":43,"minus":15},"6ca1a4b","mobile: fatten the Time Machine bar's collapse toggle, show-bar handle and return link to ~44px touch targets on coarse pointers",[620],{"path":211,"add":43,"del":15},{"hash":622,"date":412,"subject":623,"files":624,"plus":64,"minus":15},"23f171b","mobile: give the lvOS start-menu search results a 44px min-height on coarse pointers (the shared menu padding left the new tap surface at ~34px)",[625],{"path":269,"add":64,"del":15},{"hash":627,"date":412,"subject":628,"files":629,"plus":302,"minus":39},"c325df1","seo: add an atom:link rel=self and a lastBuildDate to the RSS feed (validator best-practice), and make generate-og.mjs prune stale cards so a renamed slug's orphan OG image (project-jouwwoordklok) no longer gets rasterized and shipped",[630,632],{"path":631,"add":163,"del":40},"scripts\u002Fgenerate-og.mjs",{"path":633,"add":64,"del":11},"server\u002Froutes\u002Frss.xml.ts",{"hash":635,"date":412,"subject":636,"files":637,"plus":200,"minus":15},"9d8c1c4","seo: emit BreadcrumbList structured data on \u002Flife and \u002Fdesktop too (a shared breadcrumbList helper) — the two full-viewport pages that opt out of the visible breadcrumb strip were the only content pages with no BreadcrumbList",[638,640,642],{"path":639,"add":171,"del":15},"app\u002Fcomposables\u002FuseJsonLd.ts",{"path":641,"add":39,"del":15},"app\u002Fpages\u002Fdesktop.vue",{"path":643,"add":39,"del":15},"app\u002Fpages\u002Flife.vue",{"hash":645,"date":412,"subject":646,"files":647,"plus":20,"minus":667,"truncated":40},"8fbc7f3","seo: add a shared useSeo() helper and route every top-level page through it, so each sets a unique title\u002Fdescription AND full Open Graph + Twitter card in one call — fixes ~13 pages that shared the generic social card, the duplicate about\u002Fprojects meta descriptions, and \u002Fworld's og:url pointing at the homepage",[648,649,650,652,654,655,657,659,661,662,664,665],{"path":477,"add":24,"del":15},{"path":484,"add":64,"del":43},{"path":651,"add":72,"del":64},"app\u002Fpages\u002Fblog\u002Findex.vue",{"path":653,"add":72,"del":64},"app\u002Fpages\u002Fchangelog.vue",{"path":27,"add":72,"del":64},{"path":656,"add":72,"del":64},"app\u002Fpages\u002Fcv.vue",{"path":658,"add":28,"del":43},"app\u002Fpages\u002Fkeyboard.vue",{"path":660,"add":28,"del":43},"app\u002Fpages\u002Fmuseum.vue",{"path":513,"add":72,"del":64},{"path":663,"add":64,"del":43},"app\u002Fpages\u002Fprojects\u002Findex.vue",{"path":486,"add":72,"del":64},{"path":666,"add":28,"del":43},"app\u002Fpages\u002Fstatus.vue",86,{"hash":669,"date":412,"subject":670,"files":671,"plus":43,"minus":40},"a7b6bca","unify time machine 'live' marker: terminal 'git tag' now falls back to marking the newest deploy live when the manifest carries no current flag (HEAD already on gh-pages) — matching the lvOS Time Machine app, so the two no longer disagree on which build is live",[672],{"path":36,"add":43,"del":40},{"hash":674,"date":412,"subject":675,"files":676,"plus":150,"minus":263},"02a3e44","unify lvOS app matching: add a shared searchApps() the start menu uses instead of its own inline filter, and give matchApp a substring fallback so the Alt+R run dialog and terminal 'desktop \u003Capp>' agree with search — 'machine' now resolves to Time Machine everywhere instead of launching from the start menu but erroring in the other two",[677,678,680],{"path":269,"add":72,"del":223},{"path":679,"add":302,"del":15},"app\u002Futils\u002FdesktopApps.ts",{"path":681,"add":200,"del":11},"tests\u002FdesktopApps.test.ts",{"hash":683,"date":412,"subject":684,"files":685,"plus":164,"minus":15},"4e9175c","fix museum mobile: let the command badges (.museum-how) wrap below 24rem so long ones like 'type desktop, or desktop \u003Capp>' stop pushing the plaque and the page body ~47px off-screen at 320px",[686],{"path":660,"add":164,"del":15},{"hash":688,"date":412,"subject":689,"files":690,"plus":171,"minus":82},"2c02fbd","fix time machine: resolve HEAD~0 \u002F ~0 \u002F today's date to the present via a shared asTarget helper, so 'git checkout HEAD~0' returns you home instead of printing fake time-travel theatrics and then snapping back to the live site",[691,693],{"path":692,"add":223,"del":82},"app\u002Fcomposables\u002FuseTimeMachine.ts",{"path":694,"add":263,"del":15},"tests\u002FtimeMachine.test.ts",{"hash":696,"date":412,"subject":697,"files":698,"plus":200,"minus":64},"6e3584d","fix terminal ctrl+c: echo ^C straight to the pane instead of through the capture sink, so interrupting an async `| copy`\u002F`> file`\u002F`| jq` no longer leaks a stray ^C into the clipboard\u002Ffile\u002Ffilter (and the ^C actually shows in the transcript); add a ctrl+c game-interrupt e2e",[699,700],{"path":115,"add":182,"del":64},{"path":431,"add":75,"del":15},{"hash":702,"date":412,"subject":703,"files":704,"plus":171,"minus":709},"684fafd","fix husky: replace the lint-staged pre-commit (broken on git 2.25 — 'Needed a single revision' whenever an untracked file is present, forcing --no-verify) with a version-independent check-only staged-file lint; drop the unused lint-staged dep + config",[705,706,708],{"path":584,"add":171,"del":11},{"path":60,"add":15,"del":707},295,{"path":63,"add":15,"del":28},301,{"hash":711,"date":412,"subject":712,"files":713,"plus":39,"minus":15},"f947352","deploy: set GH_REPO on the release step so gh doesn't shell out to git (the deploy job has no checkout) — fixes 'not a git repository' when creating the release",[714],{"path":54,"add":39,"del":15},{"hash":716,"date":412,"subject":717,"files":718,"plus":11,"minus":11},"97fde0b","docs: describe the new approval-gated deploy job in CLAUDE.md",[719],{"path":470,"add":11,"del":11},{"hash":721,"date":412,"subject":722,"files":723,"plus":132,"minus":278},"552709a","deploy: gate the deploy behind a production environment approval instead of a separate workflow_dispatch run — every push to main builds and then waits for a manual 'Review deployments' approval in the same run before shipping, split into a build job (uploads the site artifact) and a deploy job (needs approval, downloads it, ships + tags)",[724],{"path":54,"add":132,"del":278},{"hash":726,"date":727,"subject":728,"files":729,"plus":735,"minus":163},"ebfc8c6","2026-07-14","fix e2e: restore start-menu power labels (lock\u002Freboot\u002Fshut down) so text locators match, and open apps via alt+r \u002F close readme first in the alt+tab, tile and camera tests since the taller two-line icon tiles reflow the desktop grid",[730,732,733,734],{"path":269,"add":731,"del":82},30,{"path":19,"add":11,"del":11},{"path":447,"add":28,"del":11},{"path":162,"add":75,"del":39},48,{"hash":737,"date":727,"subject":738,"files":739,"plus":757,"minus":758,"truncated":11},"d893316","terminal + lvOS polish: ctrl+c interrupts games\u002Fcommands and clears the typed line, start-menu app search with a new launcher icon, centered mobile icon grid with aligned two-line labels, tray ⋯ only shows when it has items, collapsible time-machine overlay bar, linkedin on contact, drop fake skill versions, and rename effect network → effect.ai everywhere",[740,741,743,744,745,747,749,751,752,753,754,755],{"path":501,"add":64,"del":278},{"path":742,"add":11,"del":11},"app\u002Fcomponents\u002FHeroTerminal.vue",{"path":222,"add":444,"del":11},{"path":284,"add":11,"del":15},{"path":746,"add":197,"del":40},"app\u002Fcomponents\u002Fdesktop\u002FDesktopIcons.vue",{"path":269,"add":748,"del":175},181,{"path":271,"add":750,"del":444},46,{"path":115,"add":444,"del":11},{"path":19,"add":28,"del":72},{"path":508,"add":11,"del":40},{"path":528,"add":28,"del":28},{"path":756,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002F{effect-network.png => effect-ai.png}",353,101,{"hash":760,"date":727,"subject":761,"files":762,"plus":43,"minus":72},"e6b838c","deploy: ship via a manual workflow_dispatch step instead of auto-deploying every push to main — pushes (and PRs) still run the full check suite, but the deploy + release only run when triggered by hand",[763],{"path":54,"add":43,"del":72},{"hash":765,"date":727,"subject":766,"files":767,"plus":779,"minus":351},"d590303","time machine: show the live build as the present — fixes the gh-pages one-deploy lag so the current version appears and is marked live (no more travelling back to the previous version thinking it's current); make the lvOS Time Machine app always-dark like the other desktop apps; and show the real build version on the BIOS logo and boot splash instead of a hardcoded 2.0",[768,770,772,774,775,776,778],{"path":769,"add":11,"del":11},"app\u002Fcomponents\u002FBootSplash.vue",{"path":771,"add":72,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopBoot.vue",{"path":578,"add":122,"del":773},53,{"path":692,"add":182,"del":64},{"path":36,"add":72,"del":39},{"path":777,"add":175,"del":11},"server\u002Froutes\u002Ftime-machine.json.ts",{"path":694,"add":171,"del":72},133,{"hash":781,"date":727,"subject":782,"files":783,"plus":791,"minus":82},"799eedd","time machine: a release is all its commits, not just the tagged tip — git show \u003Cversion> expands a release to its full commit list and diffstat, git tag shows per-release commit counts, and the lvOS app rows expand to the commits each deploy shipped",[784,785,786,787,788,790],{"path":578,"add":204,"del":11},{"path":692,"add":278,"del":15},{"path":396,"add":572,"del":15},{"path":36,"add":278,"del":64},{"path":789,"add":28,"del":15},"e2e\u002Ftimemachine.spec.ts",{"path":694,"add":278,"del":11},184,{"hash":793,"date":727,"subject":794,"files":795,"plus":351,"minus":43},"fb20b9f","time machine: add 'git tag' listing every deploy as a release (with the real v2.0.x version where the commit is tagged), let git checkout accept a version, and show the version chip in the lvOS app",[796,797,798,799,800,801],{"path":578,"add":223,"del":15},{"path":692,"add":64,"del":40},{"path":36,"add":255,"del":39},{"path":789,"add":28,"del":15},{"path":777,"add":171,"del":11},{"path":694,"add":43,"del":11},{"hash":803,"date":727,"subject":804,"files":805,"plus":810,"minus":28},"3d660b6","time machine: git checkout now accepts any commit hash from git log, not only deploy points — a non-deploy commit resolves to the deploy that shipped it (with a note saying which)",[806,807,808],{"path":692,"add":506,"del":72},{"path":36,"add":43,"del":11},{"path":694,"add":809,"del":15},68,115,{"hash":812,"date":727,"subject":813,"files":814,"plus":831,"minus":82},"4134207","add a time machine: git checkout \u003Chash> in the terminal and a new lvOS app travel to any past deploy of the site — a service worker serves the real old build (back to the 2020 version) from the gh-pages history via jsdelivr, with a bar to return to the present",[815,816,818,820,821,822,824,825,826,827,829],{"path":284,"add":40,"del":11},{"path":578,"add":817,"del":15},292,{"path":692,"add":819,"del":15},109,{"path":103,"add":40,"del":11},{"path":679,"add":11,"del":15},{"path":823,"add":72,"del":11},"app\u002Futils\u002FstateKeys.ts",{"path":36,"add":107,"del":72},{"path":789,"add":147,"del":15},{"path":149,"add":28,"del":11},{"path":211,"add":828,"del":15},217,{"path":777,"add":830,"del":15},76,794,{"hash":833,"date":727,"subject":834,"files":835,"plus":163,"minus":120},"92ce6d1","don't use monofont for text scramble in header during animation",[836],{"path":837,"add":163,"del":120},"app\u002Fcomposables\u002FuseTextScramble.ts",{"hash":839,"date":727,"subject":840,"files":841,"plus":182,"minus":82},"5d12e23","harden two hydration-race e2e flakes (boss key, ? shortcuts) by retrying the global keypress until the handler has hydrated",[842,844],{"path":843,"add":28,"del":40},"e2e\u002Fnavigation.spec.ts",{"path":45,"add":223,"del":64},{"hash":846,"date":727,"subject":847,"files":848,"plus":64,"minus":40},"d500abe","the about-this-computer and settings screens show the live lvOS version from the github auto-release instead of a hardcoded 2.0",[849,851],{"path":850,"add":39,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopAbout.vue",{"path":852,"add":39,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopSettings.vue",{"hash":854,"date":727,"subject":855,"files":856,"plus":255,"minus":11},"3cc2602","exit now closes the lvOS terminal window too, not only the ~ overlay: the window isn't gated by the shared isOpen flag, so exit signals it to close itself",[857,858,859,860,862],{"path":284,"add":72,"del":15},{"path":115,"add":164,"del":15},{"path":823,"add":40,"del":15},{"path":861,"add":11,"del":11},"app\u002Futils\u002Fterminal\u002FshellCommands.ts",{"path":127,"add":72,"del":15},{"hash":864,"date":727,"subject":865,"files":866,"plus":82,"minus":15},"0f52a64","the clipboard history now also captures plain select-and-copy (Ctrl\u002FCmd+C) anywhere on the site, not just copy buttons and the terminal | copy pipe",[867],{"path":868,"add":82,"del":15},"app\u002Fplugins\u002FclipboardHistory.client.ts",{"hash":870,"date":727,"subject":871,"files":872,"plus":448,"minus":28},"d7839fb","stop the lvOS whiteboard creeping taller for seconds after it opens: the canvas measured its own border-box into the pixel buffer which fed back into the flex height every resize tick — take the canvas out of flow and size the buffer from the layout box",[873],{"path":874,"add":448,"del":28},"app\u002Fcomponents\u002Fdesktop\u002FDesktopCoDraw.vue",{"hash":876,"date":727,"subject":877,"files":878,"plus":24,"minus":46},"ee1422c","the header scramble now animates in a monospace font (uniform glyph widths — no jiggle, and a multi-line heading keeps wrapping); it also respects the site's reduce-motion switch, which now shows a one-click pill in the status bar while it's on",[879,881,883],{"path":880,"add":448,"del":15},"app\u002Fcomponents\u002FAppStatusBar.vue",{"path":882,"add":39,"del":72},"app\u002Fcomponents\u002FScrambleHeading.vue",{"path":837,"add":545,"del":163},{"hash":885,"date":727,"subject":886,"files":887,"plus":43,"minus":15},"66c246d","serialize deploys with a concurrency group so overlapping runs can't race on the same auto-release tag",[888],{"path":54,"add":43,"del":15},{"hash":890,"date":727,"subject":891,"files":892,"plus":163,"minus":164},"a7ff4e9","fix the header scramble flicker: wider scramble glyphs wrapped onto a clipped second line and blanked the right half of the heading — nowrap keeps every frame on one line",[893],{"path":837,"add":163,"del":164},{"hash":895,"date":727,"subject":896,"files":897,"plus":480,"minus":64},"60ef203","publish my ssh public key on the contact page and make the pgp\u002Fssh lines click-to-copy; the pgp command now uses the absolute URL so it works when pasted, and gpg --import actually imports in the terminal",[898,899,901],{"path":23,"add":72,"del":15},{"path":27,"add":900,"del":39},47,{"path":38,"add":163,"del":39},{"hash":903,"date":727,"subject":904,"files":905,"plus":72,"minus":40},"d64a9a7","stop tracking the generated resume pdf — it's rendered at build\u002Fdeploy like the OG images and changes every build",[906],{"path":347,"add":72,"del":40},{"hash":908,"date":727,"subject":909,"files":910,"plus":163,"minus":164},"34e872b","tappy is web3 crypto payment links (usdc on eth+solana, non-custodial) — rewrite its copy accordingly, rename jouw woordklok to WordClock, and add real thumbnails for tappy, wordclock and this portfolio",[911,912,914,916,918],{"path":528,"add":163,"del":164},{"path":913,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fportfolio.png",{"path":915,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Ftappy.png",{"path":917,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fwordclock.png",{"path":919,"add":15,"del":15},"public\u002Flaurens-verspeek-resume.pdf",{"hash":921,"date":727,"subject":922,"files":923,"plus":900,"minus":64},"030efa6","publish my gpg public key (contact page pgp line, \u002Fpgp.txt and the gpg terminal command) and add a keybase.io\u002Flaurensv6 link to the socials",[924,925],{"path":23,"add":750,"del":64},{"path":508,"add":11,"del":15},{"hash":927,"date":727,"subject":928,"files":929,"plus":940,"minus":263},"48b06c0","auto-increment the site version per deploy: CI cuts a github release + tag (patch bump from the latest) on every push to main and bakes SITE_VERSION into the status bar, terminal banner, boot splash and uname\u002Fneofetch",[930,931,932,933,934,936,938,939],{"path":54,"add":25,"del":11},{"path":880,"add":28,"del":40},{"path":769,"add":72,"del":40},{"path":115,"add":39,"del":11},{"path":935,"add":64,"del":39},"app\u002Futils\u002Fterminal\u002FinfoCommands.ts",{"path":937,"add":40,"del":11},"e2e\u002Fterminal.spec.ts",{"path":431,"add":40,"del":11},{"path":149,"add":448,"del":15},71,{"hash":942,"date":727,"subject":943,"files":944,"plus":967,"minus":968},"12b61d6","bake github stats and the real last-year contribution calendar at build time (was fetched live per visitor and only saw ~90 days); render the heatmap on \u002Fabout and fix the terminal contributions command (full year + optional year arg); the museum command now lists its exhibits in the terminal instead of navigating to \u002Fmuseum",[945,948,950,952,954,956,958,959,961,963,964,965],{"path":946,"add":947,"del":15},"app\u002Fcomponents\u002FGithubContributions.vue",121,{"path":949,"add":75,"del":461},"app\u002Fcomponents\u002FGithubStats.vue",{"path":951,"add":11,"del":11},"app\u002Fcomponents\u002FProjectCard.vue",{"path":953,"add":171,"del":185},"app\u002Fcomposables\u002FuseGithubStats.ts",{"path":19,"add":955,"del":15},1528,{"path":957,"add":545,"del":15},"app\u002Fdata\u002Fgithub.ts",{"path":484,"add":11,"del":15},{"path":960,"add":40,"del":11},"app\u002Futils\u002Fterminal\u002FcontribGraph.ts",{"path":962,"add":61,"del":197},"app\u002Futils\u002Fterminal\u002FsiteCommands.ts",{"path":45,"add":175,"del":509},{"path":63,"add":11,"del":11},{"path":966,"add":480,"del":15},"scripts\u002Ffetch-github.mjs",1850,118,{"hash":970,"date":727,"subject":971,"files":972,"plus":980,"minus":203},"ade833c","rewrite the lvOS snake as a square-celled canvas that scales with the window; the shared engine now emits structured board state alongside the ascii frame the terminal still uses",[973,976,977,979],{"path":974,"add":975,"del":150},"app\u002Fcomponents\u002Fdesktop\u002FDesktopSnake.vue",185,{"path":74,"add":461,"del":11},{"path":978,"add":11,"del":15},"app\u002Futils\u002FterminalGames.ts",{"path":162,"add":39,"del":11},211,{"hash":982,"date":727,"subject":983,"files":984,"plus":28,"minus":40},"31d28c5","the chess board scales up with the window via a container query instead of staying a tiny fixed board in a maximised window",[985],{"path":986,"add":28,"del":40},"app\u002Fcomponents\u002Fdesktop\u002FDesktopChess.vue",{"hash":988,"date":727,"subject":989,"files":990,"plus":75,"minus":72},"b1a75a6","the pixel world refits its canvas via a ResizeObserver so maximising no longer stretches the pixels, and the place cooldown goes from 5s to 5 minutes (shown as m ss)",[991,992],{"path":277,"add":263,"del":39},{"path":993,"add":11,"del":11},"realtime\u002Fworld-core.mjs",{"hash":995,"date":727,"subject":996,"files":997,"plus":64,"minus":39},"292017f","the lvOS whiteboard fills the window width when maximised and no longer slowly grows taller on open (min-height floor instead of height:100% against the auto body)",[998],{"path":874,"add":64,"del":39},{"hash":1000,"date":727,"subject":1001,"files":1002,"plus":28,"minus":39},"ef9e524","give the lvOS displays and clock apps their own icons (monitor + a new clock glyph) and add a key icon for the keybase link",[1003,1004],{"path":679,"add":40,"del":40},{"path":1005,"add":39,"del":11},"app\u002Futils\u002Ficons.ts",{"hash":1007,"date":727,"subject":1008,"files":1009,"plus":182,"minus":164},"6917c6f","the header decode animation only promotes inline elements (nav links) to inline-block, leaving block headings as blocks — fixing the width jump and white flicker on the section titles",[1010],{"path":837,"add":182,"del":164},{"hash":1012,"date":727,"subject":1013,"files":1014,"plus":82,"minus":11},"85aa8fd","clicking the terminal no longer steals focus back to the input while transcript text is selected, so a drag-select can be copied",[1015],{"path":222,"add":82,"del":11},{"hash":1017,"date":727,"subject":1018,"files":1019,"plus":39,"minus":40},"d8aadd2","the resting dotted underline under text links now uses the full link colour instead of a 55%-dimmed tint, so it matches the link text",[1020],{"path":418,"add":39,"del":40},{"hash":1022,"date":727,"subject":1023,"files":1024,"plus":731,"minus":15},"e786f08","add tappy (co-founder, 2022–2024) and jouw woordklok (esp8266 word clock) to the projects list",[1025],{"path":528,"add":731,"del":15},{"hash":1027,"date":727,"subject":1028,"files":1029,"plus":11,"minus":11},"22afc8b","the README mobile section and its 375px lvOS screenshot catch up to round 38: the toast that now clears the front app's titlebar (visible in the shot), the touch controls on the new Asteroids and Pet apps and the wrapping four-button whiteboard tools, and the screensaver's 'tap to wake' hint on a phone",[1030,1031],{"path":10,"add":11,"del":11},{"path":89,"add":15,"del":15},{"hash":1033,"date":727,"subject":1034,"files":1035,"plus":43,"minus":64},"bd6bc29","fetchCurrentWeather returns the raw temperature, not a rounded one: the terminal weather report shows the decimal (18.4°C) as it always has, so rounding inside the shared fetch made it read 18°C and broke the report e2e; the fetch now returns raw values and the tray chip (which shows whole degrees) rounds at the point of use, as does the terminal when it feeds the chip",[1036,1038,1040],{"path":1037,"add":11,"del":11},"app\u002Fcomposables\u002FuseWeatherChip.ts",{"path":1039,"add":11,"del":11},"app\u002Futils\u002Fterminal\u002FtoyCommands.ts",{"path":1041,"add":28,"del":72},"app\u002Futils\u002Fweather.ts",{"hash":1043,"date":727,"subject":1044,"files":1045,"plus":40,"minus":15},"2962b97","DesktopStartMenu imports the scss mixins it uses: the extracted start-menu carried the taskbar's lv-glass @include but not the @use '~\u002Fassets\u002Fscss\u002Fmixins' the taskbar had at the top of its style block, so the sass build failed with 'Undefined mixin' (stylelint and typecheck don't resolve mixins, only generate does)",[1046],{"path":269,"add":40,"del":15},{"hash":1048,"date":727,"subject":1049,"files":1050,"plus":425,"minus":46},"baa1eff","the museum and architecture docs catch up to round 38: the app-suite plaque lists Asteroids, the Clipboard, the Pet app and Paint's undo; a dedicated Asteroids exhibit joins the games gallery; the whiteboard plaque notes Ctrl+Z undo and the time-lapse replay; the clipboard plaque covers per-entry delete and the terminal 'clip' command; the pet-economy plaque and the keyclick\u002Fsettings craft plaque tell the truth about the lvOS Pet app, keyclick in Vim\u002FPlayground and the fontsize Settings slider; architecture.md documents the shared useLongPress, the DesktopStartMenu split, the one fetchCurrentWeather, the games→one-economy path, and the whiteboard undo's resolved-sid protocol",[1051,1052],{"path":103,"add":43,"del":64},{"path":105,"add":278,"del":263},{"hash":1054,"date":727,"subject":1055,"files":1056,"plus":1062,"minus":11},"886ba01","an lvOS pet app lets a desktop-only player spend what their games earn: the tamagotchi's coins are minted by game high scores (the shared wallet the terminal 'pet' command spends), but the desktop pet widget was FEED-ONLY, so a keyboard-free visitor could earn coins from windowed games and never see or spend them; the new Pet app shows the creature (face\u002Fstage\u002Fmood\u002Fage), the coin balance, feed\u002Fplay\u002Fsnack, and the shop (bowtie\u002Ftop-hat\u002Fcrown, disabled until affordable), plus an adopt form when there's no pet — all through the one shared usePet state, no second ledger",[1057,1058,1061],{"path":284,"add":40,"del":11},{"path":1059,"add":1060,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopPet.vue",220,{"path":679,"add":11,"del":15},223,{"hash":1064,"date":727,"subject":1065,"files":1066,"plus":1088,"minus":28},"9d7d84f","a new lvOS Asteroids game joins the games gallery: a vector ship rotates, thrusts and fires; asteroids drift and wrap the edges and split large→medium→small→gone when shot; a hit ends the run. The simulation is a pure, DOM-free, seed-injectable app\u002Futils\u002Fgames\u002Fasteroids.ts (wrap\u002Fship physics\u002Fsplit\u002Fcollision\u002Fwave spawn) with 15 unit tests, and DesktopAsteroids.vue drives it through the shared useCanvasScene (DPR\u002Fresize\u002FrAF that pauses on hidden tab + reduced motion) with window-scoped keyboard (arrows\u002FWASD\u002Fspace) and a coarse-pointer thumb pad. It feeds the ONE shared economy like snake\u002Ftetris\u002F2048 — a new lv-asteroids-highscore key through useHighScore.record (hall of fame + global leaderboard + confetti), minting pet coins (floor(score\u002F50)) from the same key",[1067,1068,1071,1073,1074,1077,1079,1081,1083,1086],{"path":284,"add":40,"del":11},{"path":1069,"add":1070,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopAsteroids.vue",417,{"path":1072,"add":11,"del":15},"app\u002Fcomposables\u002FusePet.ts",{"path":679,"add":11,"del":15},{"path":1075,"add":1076,"del":15},"app\u002Futils\u002Fgames\u002Fasteroids.ts",207,{"path":1078,"add":11,"del":15},"app\u002Futils\u002FhallOfFame.ts",{"path":1080,"add":40,"del":15},"app\u002Futils\u002Fpet.ts",{"path":1082,"add":40,"del":11},"realtime\u002Fscores-core.mjs",{"path":1084,"add":1085,"del":15},"tests\u002Fasteroids.test.ts",140,{"path":1087,"add":39,"del":39},"tests\u002Fpet.test.ts",776,{"hash":1090,"date":727,"subject":1091,"files":1092,"plus":11,"minus":11},"cfe83ac","the Paint undo stack is a const, not a let: the snapshot array is only ever mutated (push\u002Fshift\u002Fpop), never reassigned, so it tripped prefer-const — the lint that the Paint-undo commit's typecheck\u002Fstyle checks didn't cover",[1093],{"path":1094,"add":11,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopPaint.vue",{"hash":1096,"date":727,"subject":1097,"files":1098,"plus":735,"minus":223},"be9f4ad","the co-draw whiteboard can replay itself as a time-lapse: like the pixel world's time-lapse, a 'replay' button clears the board and reveals every stroke in the order it was drawn over ~2.5s (paced by stroke count), then resyncs with the live board — the stroke-painting is factored into one drawSegment() the full paint and the replay share, the strokes watcher is paused mid-replay so a remote stroke can't repaint over it, and the button disables on an empty board or while replaying; the four action buttons now wrap on a phone",[1099],{"path":874,"add":735,"del":223},{"hash":1101,"date":727,"subject":1102,"files":1103,"plus":1109,"minus":1107},"d806b96","the lvOS start menu splits out of DesktopTaskbar into its own DesktopStartMenu component: the taskbar carried the whole start-menu markup, its 13 action buttons and their styles inline; the menu now lives in DesktopStartMenu.vue and reports every choice as one typed StartAction (shared in desktopApps.ts) the taskbar routes onto its existing emits, with the wallpaper swatch as a v-model — the bar drops to 489 lines (from 572) and stays about the tray, clock and task strip",[1104,1106,1108],{"path":269,"add":1105,"del":15},107,{"path":271,"add":444,"del":1107},96,{"path":679,"add":64,"del":15},136,{"hash":1111,"date":727,"subject":1112,"files":1113,"plus":1120,"minus":216},"d679efd","the touch long-press-to-menu machine becomes one shared useLongPress composable: the ~500ms press-hold-with-move-cancel-and-click-suppression state machine was hand-rolled twice — in the Files app's row menu and in useDesktopContextMenu — so it now lives once, parameterised by a fire callback that receives where the press landed plus an optional per-target payload (the file row it began on); DesktopFiles drops ~25 lines (539→516) and the desktop context menu its whole inline copy, behaviour unchanged",[1114,1115,1117],{"path":170,"add":82,"del":425},{"path":1116,"add":28,"del":731},"app\u002Fcomposables\u002FuseDesktopContextMenu.ts",{"path":1118,"add":1119,"del":15},"app\u002Fcomposables\u002FuseLongPress.ts",57,70,{"hash":1122,"date":727,"subject":1123,"files":1124,"plus":147,"minus":200},"134155c","one shared open-meteo current-weather fetch, and the terminal keeps the tray in step: the tray chip and the terminal 'weather' command each hit open-meteo their own way, so 'weather' printed a fresh Amsterdam reading and threw it away while the tray showed a stale, disagreeing one; both now go through fetchCurrentWeather() in utils\u002Fweather.ts (temp rounded, optional wind+humidity), and when 'weather' resolves to Amsterdam it pushes the reading into the shared chip state (like the Weather app already does) so all three current-temp surfaces agree",[1125,1126,1127],{"path":1037,"add":28,"del":43},{"path":1039,"add":75,"del":255},{"path":1041,"add":197,"del":15},{"hash":1129,"date":727,"subject":1130,"files":1131,"plus":185,"minus":72},"7fd6394","the lvOS Clipboard app can forget a single copy, not only clear everything: a × on each row removes just that entry (useClipboardHistory gains a remove(text), de-duped by text like record) — handy for dropping one thing you don't want lingering in the history while keeping the rest; the row splits into a copy button + a sibling delete button (no nested buttons), the × turning danger-red on hover",[1132,1134],{"path":1133,"add":509,"del":39},"app\u002Fcomponents\u002Fdesktop\u002FDesktopClipboard.vue",{"path":1135,"add":72,"del":11},"app\u002Fcomposables\u002FuseClipboardHistory.ts",{"hash":1137,"date":727,"subject":1138,"files":1139,"plus":197,"minus":82},"5b9a68c","the whiteboard undo button gives feedback and disables when there's nothing to take back: save flashes 'saved ✓' but undo was silent, and it stayed clickable even with no stroke of yours on the board; useCoDraw now exposes a canUndo (any stroke tagged with your id) and undo() returns whether it removed anything, so the button disables when canUndo is false and briefly flashes 'undone' (via button click or Ctrl+Z) when a stroke actually came back",[1140,1142],{"path":874,"add":1141,"del":72},16,{"path":1143,"add":164,"del":72},"app\u002Fcomposables\u002FuseCoDraw.ts",{"hash":1145,"date":727,"subject":1146,"files":1147,"plus":444,"minus":11},"1d23d44","the co-draw whiteboard undoes with Ctrl+Z \u002F ⌘Z: the new undo was a button only, so add the keyboard shortcut every editor has — a window-scoped keydown fires undo() when the whiteboard is the front-most lvOS window (z-index check, so it can't fire under another app) and never on shift+Z, leaving room for a future redo",[1148],{"path":874,"add":444,"del":11},{"hash":1150,"date":727,"subject":1151,"files":1152,"plus":1154,"minus":39},"4897748","the lvOS Paint app gains an undo, matching the whiteboard's: Paint is a raster canvas (strokes draw straight onto it, so there's no stroke list to pop), so undo is a bounded stack of ImageData snapshots — startStroke and clear each snapshot the canvas first, [undo] restores the most recent (capped at 20 deep) and disables itself when the stack is empty; a reactive canUndo mirrors the depth without making the ImageData itself reactive",[1153],{"path":1094,"add":1154,"del":39},34,{"hash":1156,"date":727,"subject":1157,"files":1158,"plus":223,"minus":15},"9f56d78","lvOS Settings gains a 'terminal text' scale row — the last persisted preference that had only a terminal surface: fontsize \u002F ctrl+= \u002F ctrl+- drive useTermFontScale, but unlike theme\u002Faccent\u002Fvolume\u002Fnight-light\u002Freduce-motion\u002Fwallpaper\u002Fscreensaver\u002Fkeyclick it had no Settings control though the terminal also runs as an lvOS window; a slider (0.7–1.6×) in the appearance section now drives the same shared state",[1159],{"path":852,"add":223,"del":15},{"hash":1161,"date":727,"subject":1162,"files":1163,"plus":43,"minus":15},"a946a23","keyclick reaches the last two lvOS editors — Vim and the Playground: R37 wired the shared typing tick into Notes\u002FMail\u002FChat, but the two most keyboard-heavy surfaces (the Vim window, which edits the same VFS files Notes ticks for, and the code Playground) stayed silent, contradicting the setting's own 'lvOS text fields' copy; both now call the shared useKeyClick().click() on input — Vim only in insert mode, since its buffer is readonly otherwise",[1164,1166],{"path":1165,"add":39,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopPlayground.vue",{"path":1167,"add":72,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopVim.vue",{"hash":1169,"date":727,"subject":1170,"files":1171,"plus":1177,"minus":15},"85c98c9","a terminal 'timer' command sets a countdown that chimes when it is up: 'timer 5m' \u002F 'timer 90s' \u002F 'timer 1h30m' (a bare number is seconds) parses through a tested pure parseDuration, prints the set duration and — fire-and-forget so the serialized run queue isn't blocked — pushes a 'time's up' line into the shared transcript and plays the boot chime through the shared volume (silent when muted) once it elapses; capped at 24h, junk durations rejected; 9 duration unit tests",[1172,1174,1175],{"path":1173,"add":200,"del":15},"app\u002Futils\u002Fterminal\u002Fduration.ts",{"path":36,"add":444,"del":15},{"path":1176,"add":25,"del":15},"tests\u002Fduration.test.ts",85,{"hash":1179,"date":727,"subject":1180,"files":1181,"plus":147,"minus":15},"decaf13","a terminal 'clip' (alias 'clipboard') command mirrors the lvOS Clipboard app: R37's copy history had only an lvOS surface, so the terminal — where '| copy' is the biggest producer of copies — couldn't see or reuse it; 'clip' lists the shared history newest-first with indices (user-derived text escaped before the v-html transcript), and 'clip \u003Cn>' re-copies an entry through the same writeClipboard() chokepoint, the way 'scores' mirrors the hall of fame",[1182],{"path":36,"add":147,"del":15},{"hash":1184,"date":727,"subject":1185,"files":1186,"plus":302,"minus":11},"a84e168","the lvOS screensaver wake hint tells a touch phone the truth: it read 'move the mouse or press a key to wake', but a coarse-pointer device has neither — it now shows 'tap to wake' under @media(pointer:coarse) while the mouse\u002Fkey copy stays for fine pointers (the saver already wakes on pointerdown)",[1187],{"path":235,"add":302,"del":11},{"hash":1189,"date":727,"subject":1190,"files":1191,"plus":28,"minus":28},"137a1dd","a copy-truthfulness sweep: the keyclick command description (and the man page it feeds) plus the lvOS Settings note both still said the tick was 'terminal' only, though R37 wired it into Notes\u002FMail\u002FChat — they now say 'terminal and lvOS text fields'; sysinfo's shell row read 'lvsh 2.0' while uname\u002Fneofetch\u002Fstatus-bar\u002Fdesktop all say 2.0.0, now aligned; and the public blog list + post dates rendered en-US ('Jul 14, 2026') while the same post shows en-GB ('14 jul') in the lvOS Mail app and the whole site standardized on en-GB — both now format '14 Jul 2026'",[1192,1193,1194,1195,1196],{"path":852,"add":11,"del":11},{"path":564,"add":11,"del":11},{"path":651,"add":11,"del":11},{"path":935,"add":11,"del":11},{"path":36,"add":11,"del":11},{"hash":1198,"date":727,"subject":1199,"files":1200,"plus":197,"minus":75},"1d18f02","the whiteboard undo removes exactly the stroke the drawer took back, not the server's guess: client and server each computed 'the sender's highest sid' independently, so if the 8ms flood gate dropped a whole fast pen-drag (sid N+1) the server's highest was still N — the drawer undid the dropped N+1 locally while the server told everyone else to erase N, desyncing the board; undo() now sends its resolved sid and the server honors that exact id, removing nothing (and broadcasting nothing) when it never received that stroke, so a flood-dropped drag can't erase a different one for the others; +1 relay-check (54 total)",[1201,1202,1203,1204],{"path":1143,"add":39,"del":11},{"path":129,"add":163,"del":43},{"path":131,"add":11,"del":11},{"path":136,"add":82,"del":39},{"hash":1206,"date":727,"subject":1207,"files":1208,"plus":39,"minus":39},"4110310","the mobile lvOS toast clears the front app's titlebar instead of covering it: R37 moved coarse-pointer toasts to the top (inset 0.75rem) to dodge a maximized app's bottom controls, but the top edge is the window titlebar — its –\u002F□\u002F× buttons sat under the toast for its ~2.5s life; the toast now anchors at inset 3rem, below the ~45px bar, clearing both the titlebar and the bottom control strip",[1209],{"path":284,"add":39,"del":39},{"hash":1211,"date":727,"subject":1212,"files":1213,"plus":11,"minus":11},"001e1fc","the README mobile section and its 375px lvOS screenshot catch up to round 37's phone fixes: the top-anchored notification toasts that no longer cover a maximized app's controls, the museum 'walk the floor' map fitting at 320px, the thumb-size Settings colour swatches, and the un-clipped \u002Fcontact input and single-line Mail date",[1214,1215],{"path":10,"add":11,"del":11},{"path":89,"add":15,"del":15},{"hash":1217,"date":727,"subject":1218,"files":1219,"plus":28,"minus":28},"6d618a9","the sanitizeStroke tests expect the pen-drag sid the whiteboard-undo change added: it now returns { …, sid } (a provided integer, or 0 when missing), so the two shape assertions include it",[1220],{"path":1221,"add":28,"del":28},"tests\u002FdrawCore.test.ts",{"hash":1223,"date":727,"subject":1224,"files":1225,"plus":11,"minus":72},"3144c48","the mobile-toast reposition uses the inset shorthand: setting all four of top\u002Fright\u002Fbottom\u002Fleft as longhands tripped stylelint's declaration-block-no-redundant-longhand-properties, so the coarse-pointer rule now writes inset: 0.75rem 1rem auto",[1226],{"path":284,"add":11,"del":72},{"hash":1228,"date":727,"subject":1229,"files":1230,"plus":461,"minus":164},"b5309ce","the museum and architecture docs catch up to round 37: the whiteboard plaque notes per-stroke undo, the lvOS 2.0 plaque documents 'desktop \u003Capp>' booting straight into a window, minesweeper's plaque mentions its new terminal command, a Clipboard exhibit joins the pavilion, and the craft-annex keyclick plaque tells the truth that it now ticks in lvOS text fields too; architecture.md's Realtime section documents the whiteboard's by+sid stroke tagging behind draw-undo and the message-handler's hostile-frame hardening (non-object drop, Number.isFinite coords, join gates), and the lvOS section covers useDesktopContextMenu, the desktop \u003Capp> pending-app handoff, and the one writeClipboard chokepoint",[1231,1232],{"path":103,"add":28,"del":72},{"path":105,"add":46,"del":28},{"hash":1234,"date":727,"subject":1235,"files":1236,"plus":1254,"minus":24,"truncated":11},"c8e745b","an lvOS Clipboard app remembers what the site copied: every copy across the site — the terminal's `| copy` pipe, the blog code-block and inline-code buttons, the heading deep-links, the vCard email, the colour picker and the palette 'copy link' — now flows through one writeClipboard() helper that both writes and records into a shared, persisted history; the new Clipboard app lists recent copies (newest-first, de-duplicated, capped at 30) with a preview + relative time, click any to copy it again (which bumps it back to the top), and clear to forget; a client plugin registers the recorder so even the terminal copy path (which runs outside a component) is captured, and `desktop clipboard` opens it",[1237,1238,1239,1241,1243,1244,1245,1247,1249,1250,1251,1253],{"path":261,"add":40,"del":11},{"path":284,"add":40,"del":11},{"path":1133,"add":1240,"del":15},148,{"path":1242,"add":40,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopColorPicker.vue",{"path":1135,"add":1154,"del":15},{"path":17,"add":40,"del":11},{"path":1246,"add":64,"del":164},"app\u002Fcomposables\u002FuseCopyFlag.ts",{"path":1248,"add":461,"del":545},"app\u002Fcomposables\u002FusePostEnhancements.ts",{"path":115,"add":28,"del":39},{"path":868,"add":164,"del":15},{"path":1252,"add":200,"del":15},"app\u002Futils\u002Fclipboard.ts",{"path":679,"add":11,"del":15},263,{"hash":1256,"date":727,"subject":1257,"files":1258,"plus":1266,"minus":197},"b960e22","the co-draw whiteboard can undo your last stroke, synced to everyone: until now the only way to remove anything was clear (which wipes the shared board for all), so a stray line was permanent; each pen-drag now carries a per-drawer sid (bumped on pointer-down, shared by that drag's segments) and the server stamps every stored segment with the drawer's connection id, so a new 'draw-undo' frame drops the sender's highest-sid group from the ring buffer and tells the other clients to drop that by+sid group too — the drawer sees it vanish optimistically, everyone else via the echo; works solo (local-only) with no relay; 4 new relay-check assertions (53 total)",[1259,1260,1261,1262,1264,1265],{"path":874,"add":43,"del":72},{"path":1143,"add":107,"del":43},{"path":129,"add":46,"del":39},{"path":1263,"add":43,"del":72},"realtime\u002Fdraw-core.mjs",{"path":131,"add":182,"del":64},{"path":136,"add":185,"del":11},126,{"hash":1268,"date":727,"subject":1269,"files":1270,"plus":480,"minus":147},"1d417af","the lvOS desktop context-menu + touch long-press logic moves out of WebDesktop.vue into a useDesktopContextMenu composable: the reactive menu state, the on-screen clamping, the right-click handler and the ~500ms long-press state machine (timer, move-cancel, click-suppression, unmount cleanup) were ~46 lines of self-contained plumbing in the shell component; they now live in one composable that returns contextMenu plus the handlers, and WebDesktop just wires them alongside its titleMenu (728 → 693 lines, behaviour unchanged)",[1271,1272],{"path":284,"add":43,"del":147},{"path":1116,"add":1119,"del":15},{"hash":1274,"date":727,"subject":1275,"files":1276,"plus":492,"minus":448},"78f01ff","the lvOS Gallery save\u002Fread logic collapses into one app\u002Futils\u002Fgallery.ts: the 'read → prepend → slice(0,6) → write lvos-shots' ring was copy-pasted across the screenshot tool, the asciicam, Paint and the whiteboard (four writers) plus the Gallery reader — with the 'lvos-shots' key and the cap of 6 duplicated in each, so bumping the cap in one place would silently disagree with the others; a shared addToGallery()\u002FreadGallery() with GALLERY_KEY\u002FGALLERY_MAX now owns the key, the cap and the write, and all five call sites go through it",[1277,1279,1280,1282,1283,1285],{"path":1278,"add":40,"del":39},"app\u002Fcomponents\u002Fdesktop\u002FDesktopCamera.vue",{"path":874,"add":40,"del":39},{"path":1281,"add":40,"del":40},"app\u002Fcomponents\u002Fdesktop\u002FDesktopGallery.vue",{"path":1094,"add":40,"del":39},{"path":1284,"add":40,"del":39},"app\u002Fcomposables\u002FuseDesktopIo.ts",{"path":1286,"add":302,"del":15},"app\u002Futils\u002Fgallery.ts",{"hash":1288,"date":727,"subject":1289,"files":1290,"plus":72,"minus":39},"9a48312","the relay-check non-finite-cursor assertion sends 1e400 as raw json so it actually exercises the guard: JSON.stringify(Infinity) is 'null' (which the old typeof-number check already rejected), so routing the frame through the test client's JSON.stringify never tested the Infinity path — it now writes the frame by hand as raw text, where JSON.parse('1e400') === Infinity reaches the handler and only Number.isFinite drops it; also clears the no-loss-of-precision lint on the bare 1e400 literal",[1291],{"path":136,"add":72,"del":39},{"hash":1293,"date":727,"subject":1294,"files":1295,"plus":39,"minus":15},"ee96cc3","the lvOS Mail inbox date stays on one line at 320px: .mail-date had no white-space:nowrap, so on the narrowest phones the squeezed date column wrapped the day and month onto two lines ('8' over 'jul'); it now holds a single line",[1296],{"path":1297,"add":39,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopMail.vue",{"hash":1299,"date":727,"subject":1300,"files":1301,"plus":39,"minus":15},"cbd8d2d","the \u002Fcontact wizard input stops clipping at 320px: .contact-input is a flex:1 child but had no min-width:0, so it couldn't shrink below its intrinsic content width and its right edge (with the caret) was cut ~18px by the card on the narrowest phones; the standard min-width:0 lets it shrink to the row, harmless at every wider width",[1302],{"path":261,"add":39,"del":15},{"hash":1304,"date":727,"subject":1305,"files":1306,"plus":164,"minus":15},"245809b","the museum 'walk the floor' map fits on a 320px phone: .walk-floor is a fixed-width ASCII grid whose font-size clamp bottoms out at 0.55rem, and with 1rem of padding the rightmost columns spilled ~27px into an overflow-x scroll region — invisible while walking, since the view doesn't pan to follow the player; below 360px the floor now drops to 0.46rem with 0.5rem padding so the entire map is on-screen (375px and up are unchanged)",[1307],{"path":1308,"add":164,"del":15},"app\u002Fcomponents\u002FMuseumWalk.vue",{"hash":1310,"date":727,"subject":1311,"files":1312,"plus":64,"minus":15},"06cad07","the lvOS Settings colour swatches grow to a ~32px tap target on touch: the accent and wallpaper swatches were 1.3rem (~21px) circles — a comfortable click but a fiddly tap for a colour picker on a phone — so on a coarse pointer they now grow to 2rem while staying 1.3rem for a mouse (the fine-pointer layout, and the e2e suite, are untouched)",[1313],{"path":852,"add":64,"del":15},{"hash":1315,"date":727,"subject":1316,"files":1317,"plus":1141,"minus":11},"8fb8b07","the lvOS weather app feeds its reading back into the shared tray chip so the two agree: the taskbar chip fetches Amsterdam's current temp once on mount and the full weather app did an entirely separate open-meteo fetch, leaving the tray and the app as two independent snapshots that could show different temperatures; the app now pushes its fresher current temp+code into the chip's shared state (via a new setCurrent on useWeatherChip) once its own fetch lands, so opening the app reconciles the tray to the same number",[1318,1320],{"path":1319,"add":43,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopWeather.vue",{"path":1037,"add":164,"del":11},{"hash":1322,"date":727,"subject":1323,"files":1324,"plus":263,"minus":40},"68883b6","the keyclick typing sound ticks in lvOS text fields too, not only the terminal: keyclick is a shared, persisted, global setting (its own terminal command plus an lvOS Settings row), but the tick only fired from the terminal console's @input, so typing a sticky note, a mail draft or a chat message stayed silent with the setting on; Notes, Mail (subject + body) and Chat now call the same shared useKeyClick().click() on input, staying silent whenever the setting is off or the volume is muted",[1325,1327,1328],{"path":1326,"add":39,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopChat.vue",{"path":1297,"add":72,"del":11},{"path":1329,"add":72,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopNotes.vue",{"hash":1331,"date":727,"subject":1332,"files":1333,"plus":106,"minus":39},"1b8966b","the terminal 'desktop' command takes an app name so every lvOS app has a shell entry point: 'desktop' only booted the environment, and the run dialog (alt+r) was the sole in-lvOS launcher, so opening a specific app from the main-site terminal meant boot-then-hunt; 'desktop \u003Capp>' now resolves the name through the shared matchApp() (with tab-completion via appCandidates), stashes the id in a lvos-pending-app state, and WebDesktop opens it on mount — or immediately, when the terminal is already on the desktop — falling back to a helpful app list on an unknown name",[1334,1335,1336],{"path":284,"add":75,"del":15},{"path":823,"add":39,"del":11},{"path":301,"add":444,"del":40},{"hash":1338,"date":727,"subject":1339,"files":1340,"plus":302,"minus":15},"68899a1","the terminal gains a 'minesweeper' (alias 'mines') command so the last hall-of-fame game is reachable from the shell: chess and the co-draw whiteboard both have terminal pointer-commands that open their lvOS window on \u002Fdesktop and print guidance elsewhere, but minesweeper — a scored, coin-minting game with three hall-of-fame rows — had none, so a terminal user who ran 'scores' saw its best-time rows with no way in; it now follows the same pattern, opening the window on the desktop and pointing there otherwise",[1341],{"path":1342,"add":302,"del":15},"app\u002Futils\u002Fterminal\u002FgameCommands.ts",{"hash":1344,"date":727,"subject":1345,"files":1346,"plus":75,"minus":15},"efb4b5a","lvOS notification toasts move to the top of the screen on a phone so they stop covering the front app's controls: .lvos-toasts is bottom-anchored just above the taskbar, but on a coarse-pointer viewport the front window is maximized and its bottom control strip (Chat's compose input, the whiteboard save\u002Fclear tools, Gallery's 'set as wallpaper') sits exactly there — an 18rem toast hid it; on (pointer:coarse) the stack now anchors to the top over the titlebar drag-area, which has nothing tappable to obscure",[1347],{"path":284,"add":75,"del":15},{"hash":1349,"date":727,"subject":1350,"files":1351,"plus":82,"minus":15},"14c407b","the relay flood-gates chat\u002Fdraw\u002Fworld room-join frames: joins were the only high-frequency frame type with no CooldownGate, yet each one re-sends the full room state to the requester (up to a 21KB world board or 1200 whiteboard strokes) and broadcasts a fresh member count to everyone in the room, so an already-connected client spamming *-join at line rate amplified one tiny inbound frame into a large self-send plus N broadcasts; a shared 500ms joinGate (registered like the others) drops rapid re-joins while a brand-new connection's first join always passes",[1352],{"path":129,"add":82,"del":15},{"hash":1354,"date":727,"subject":1355,"files":1356,"plus":255,"minus":40},"29fda6b","the relay rejects a non-finite cursor coord instead of relaying it unclamped: world-cursor and the page 'move' handler validated coords with typeof x === 'number', which passes Infinity (JSON.parse('1e400') === Infinity) and NaN, after which Math.max(0, Infinity) stays Infinity and a junk coordinate fanned out to every other visitor's canvas\u002FDOM; both now use Number.isFinite like the draw-cursor guard already did, so the three cursor paths validate identically (relay-check asserts a 1e400 world cursor is dropped)",[1357,1358],{"path":129,"add":64,"del":40},{"path":136,"add":448,"del":15},{"hash":1360,"date":727,"subject":1361,"files":1362,"plus":46,"minus":15},"6246a67","the cursors relay drops a non-object json frame instead of crashing on it: every message handler reads msg.type straight after JSON.parse, but JSON.parse('null') succeeds and returns null, so a single 4-byte 'null' text frame threw a TypeError that — with no uncaughtException handler — took down the whole single-instance relay (cursors, chat, world, whiteboard, every live duel) for all connected visitors; the handler now returns early on any payload that isn't a non-null object, and a relay-check robustness case proves a null\u002Fstring\u002Fnumber frame leaves the relay answering",[1363,1364],{"path":129,"add":72,"del":15},{"path":136,"add":163,"del":15},{"hash":1366,"date":1367,"subject":1368,"files":1369,"plus":11,"minus":11},"6b64954","2026-07-13","refresh the README mobile section and its 375px lvOS screenshot for this round's phone fixes: the wrapping co-draw whiteboard tools (so the confirm-guarded 'clear' stays on-screen), the terminal quick-key row that fits all its keys at 320px, and the visible thumb-size Files delete target",[1370,1371],{"path":10,"add":11,"del":11},{"path":89,"add":15,"del":15},{"hash":1373,"date":1367,"subject":1374,"files":1375,"plus":1381,"minus":1377},"024e5eb","split the relay's three online-duel managers out of cursors-server.mjs (663→401 lines): pong, chess and wpm-race — their state, gates, matchmaking and start\u002Fend\u002Fdrop helpers plus their message-router branches — move into realtime\u002Fserver\u002FonlineDuels.mjs as createPongManager\u002FcreateChessManager\u002FcreateRaceManager factories taking a { wire, sendTo, gate } context (gates still route through the injected gate() so they register for cleanup); the main file now dispatches with pong\u002Fchess\u002Frace.handle(socket,msg,id) and forfeits via .drop(socket) on close, wire behaviour byte-for-byte unchanged (test:relay 47\u002F47)",[1376,1378],{"path":129,"add":1141,"del":1377},278,{"path":1379,"add":1380,"del":15},"realtime\u002Fserver\u002FonlineDuels.mjs",323,339,{"hash":1383,"date":1367,"subject":1384,"files":1385,"plus":223,"minus":39},"a5245f1","the museum and architecture docs catch up to this round: the whiteboard plaque notes the live pens of other visitors, save-to-Gallery and the confirm-before-clear; the appearance\u002Fquiet-it plaque tells the truth that a custom colorscheme #hex now shows as its own Settings swatch and keyclick toggles from the Settings sound section too; and architecture.md's Realtime section documents the draw-cursor live-position frames and the gate() registry that makes forgetConnection(id) clear every rate-limiter on close",[1386,1387],{"path":103,"add":40,"del":40},{"path":105,"add":82,"del":11},{"hash":1389,"date":1367,"subject":1390,"files":1391,"plus":140,"minus":1141},"ae94a67","the co-draw whiteboard can save the board: a 'save' button renders the current strokes onto a fixed 960x600 canvas and both stores it in the shared lvOS Gallery ('lvos-shots', like Camera\u002FPaint\u002FScreenshot) and downloads it as whiteboard.png — the stroke-painting is factored into one paintBoard() the live canvas and the snapshot share, so the export matches what's on screen at any window size",[1392],{"path":874,"add":140,"del":1141},{"hash":1394,"date":1367,"subject":1395,"files":1396,"plus":1403,"minus":461},"dc8f200","the co-draw whiteboard shows where the other visitors are drawing in real time: each client broadcasts its live pen position (normalized 0..1, throttled to ~50ms client-side and 40ms server-side) as a new draw-cursor frame, the relay echoes it to the other board members (except the sender, validating\u002Fclamping coords + pen), and a draw-cursor-gone fires on leave\u002Fdisconnect so a member's dot is removed — the app renders each remote pen as a coloured dot floating over the board, pruned after 3s of silence; still degrades to a solo sketchpad with no relay; 5 new relay-check assertions",[1397,1398,1399,1400,1401,1402],{"path":874,"add":351,"del":448},{"path":1143,"add":572,"del":72},{"path":823,"add":11,"del":15},{"path":129,"add":461,"del":39},{"path":131,"add":164,"del":11},{"path":136,"add":255,"del":15},154,{"hash":1405,"date":1367,"subject":1406,"files":1407,"plus":175,"minus":255},"c8c6f02","the relay registers every CooldownGate so per-connection cleanup can't be forgotten: the nine gates were each cleared by hand in the socket-close handler, and forgetting one (as drawGate was) leaks its per-id map for the relay's whole uptime — gates are now built through a gate() helper that registers them, and one forgetConnection(id) on close clears the id from all of them, closing the whole bug class",[1408],{"path":129,"add":175,"del":255},{"hash":1410,"date":1367,"subject":1411,"files":1412,"plus":75,"minus":11},"5e0f5e9","lvOS Settings shows an active swatch for a custom accent instead of nothing: a custom #hex accent set via the terminal colorscheme command or the colour-picker app persists as accent 'custom', which matched none of the five preset swatches, so Settings highlighted no accent at all despite one being live — it now renders a 'custom' swatch (the live --bulma-primary colour) marked active when accent==='custom', and tapping it opens the colour picker; the simple-app renderer forwards @open so Settings can request it",[1413,1414],{"path":284,"add":11,"del":11},{"path":852,"add":263,"del":15},{"hash":1416,"date":1367,"subject":1417,"files":1418,"plus":164,"minus":15},"fd466db","lvOS Settings gains the keyclick toggle its own 'settings' terminal command already advertised: R35 shipped keyclick (mechanical typing sounds) with a terminal command and shared useKeyClick state but no lvOS surface, yet the terminal 'settings' readout told users to change it 'in lvOS Settings' — the sound section now has an on\u002Foff keyclick row driven by the same shared state, so every persistent preference again has both a terminal and a Settings control",[1419],{"path":852,"add":164,"del":15},{"hash":1421,"date":1367,"subject":1422,"files":1423,"plus":164,"minus":15},"629039a","the Files app delete button is tappable on a phone: the '×' was a 22x14px control at opacity 0.35 revealed only on :hover (which touch lacks), so on a phone it was a faint, tiny target for a destructive action — on coarse pointers it now stays visible and gets a ~40px tap area with a larger glyph",[1424],{"path":170,"add":164,"del":15},{"hash":1426,"date":1367,"subject":1427,"files":1428,"plus":163,"minus":15},"6bf8ef3","the terminal's mobile quick-key row fits on a 320px phone: the 7-8 keys at min-width 2.6rem overflowed the viewport so the rightmost 'esc' sat partly off-screen behind the scroll edge with no visible affordance — below 360px the keys drop their min-width and tighten their gap\u002Fpadding\u002Ffont so the whole row fits without scrolling",[1429],{"path":222,"add":163,"del":15},{"hash":1431,"date":1367,"subject":1432,"files":1433,"plus":545,"minus":39},"0518d7d","the co-draw whiteboard 'clear' asks before wiping the shared board: clearing is destructive and instant for every connected visitor with no undo, so the button now arms on the first tap ('clear all?', danger-coloured) and only wipes on a second tap within 3s, auto-disarming otherwise",[1434],{"path":874,"add":545,"del":39},{"hash":1436,"date":1367,"subject":1437,"files":1438,"plus":40,"minus":15},"b62023c","the co-draw whiteboard 'clear' button stays on-screen on a phone: .codraw-tools was a non-wrapping flex row, so at 320-375px the eight coarse-size pen swatches pushed the 'clear the board for everyone' button off the right edge (fully off-screen at 320px) — the tools row and the pens row now wrap, so clear drops to its own line and stays tappable",[1439],{"path":874,"add":40,"del":15},{"hash":1441,"date":1367,"subject":1442,"files":1443,"plus":11,"minus":15},"43a9dbe","the co-draw whiteboard relay stops leaking per-connection cooldown state: the socket-close handler cleaned up every CooldownGate's per-id map except drawGate (added in R35), so drawGate.last grew one entry per distinct drawing connection for the relay's whole uptime — it now deletes drawGate's entry on close alongside the other gates",[1444],{"path":129,"add":11,"del":15},{"hash":1446,"date":1367,"subject":1447,"files":1448,"plus":200,"minus":11},"ae44380","the lvOS Calculator no longer swallows keystrokes meant for other windows: its window-wide keydown listener grabbed and preventDefault'd digits\u002Foperators even when you were typing into another app's field (Notes, the Run dialog) or a different window was focused; it now fires only when the calculator is the front-most (highest z-index, non-minimized) window and the event target isn't an editable field",[1449],{"path":1450,"add":200,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopCalculator.vue",{"hash":1452,"date":1367,"subject":1453,"files":1454,"plus":185,"minus":72},"d656c93","the lvOS Calculator evaluates negative values instead of erroring: calc.ts had no unary minus, so pressing ± made '-3' and any next op ('x²', a second ±, or the =preview) read the '-'-led string as malformed and showed 'err', while programmer NOT produced a negative whose DEC\u002FHEX\u002FBIN\u002FOCT readout collapsed to '—'; a leading '-' (at the start, after '(' or another operator) is now an internal right-assoc 'u-' negation token in the shunting-yard, so ±\u002FNOT\u002Fnegative literals resolve everywhere",[1455,1457],{"path":1456,"add":461,"del":72},"app\u002Futils\u002Fcalc.ts",{"path":1458,"add":448,"del":15},"tests\u002Fcalc.test.ts",{"hash":1460,"date":1367,"subject":1461,"files":1462,"plus":11,"minus":11},"59e69f5","the README mobile rundown and its 375px lvOS screenshot catch up to this round — the reachable theme toggle, the no-zoom lvOS text fields, the tappable Mail buttons, the fitting Calculator sci\u002Fprogrammer grids and the scannable 320px vCard QR",[1463,1464],{"path":10,"add":11,"del":11},{"path":89,"add":15,"del":15},{"hash":1466,"date":1367,"subject":1467,"files":1468,"plus":171,"minus":39},"fd0f52a","the museum and architecture docs catch up to this round: a whiteboard plaque joins the multiplayer wing, the app-suite and leaderboard plaques mention the calculator's sci\u002Fprogrammer modes and the terminal scores command, the craft exhibit notes custom #hex accents and keyclick, and architecture.md gains a Realtime section documenting the shared-core\u002Fprotocol\u002FrelaySocket-lease pattern the co-draw whiteboard exemplifies",[1469,1470],{"path":103,"add":72,"del":39},{"path":105,"add":46,"del":15},{"hash":1472,"date":1367,"subject":1473,"files":1474,"plus":1482,"minus":1477},"c9db8d9","the lvOS Calculator gains scientific and programmer modes via a basic\u002Fsci\u002Fprog switcher: scientific adds sin\u002Fcos\u002Ftan (deg\u002Frad), ln\u002Flog\u002F√\u002Fx²\u002Fxʸ\u002Fn!\u002F1÷x\u002Fπ\u002Fe with an 'err' guard for invalid ops, programmer shows a live DEC\u002FHEX\u002FBIN\u002FOCT readout plus hex entry and bitwise AND\u002FOR\u002FXOR\u002FNOT\u002Fshifts — the basic keypad is unchanged and the evaluator lives in a tested pure calc.ts",[1475,1478,1480],{"path":1450,"add":1476,"del":1477},232,63,{"path":1456,"add":1479,"del":15},141,{"path":1458,"add":1481,"del":15},90,463,{"hash":1484,"date":1367,"subject":1485,"files":1486,"plus":1502,"minus":40},"156fc66","a co-draw whiteboard over the cursors relay: a shared freehand canvas (normalized-space line segments, distinct from the pixel world's grid) as an lvOS app + terminal 'draw' command — strokes draw optimistically and the server echoes them to everyone else, a ring buffer replays to late joiners, clear wipes it for all, and it degrades to a solo sketchpad with no relay; server-side sanitize\u002Fclamp + a flood gate, shared draw-core, 8 new relay-check assertions",[1487,1488,1490,1492,1493,1494,1495,1497,1499,1500,1501],{"path":284,"add":40,"del":11},{"path":874,"add":1489,"del":15},227,{"path":1143,"add":1491,"del":15},83,{"path":679,"add":11,"del":15},{"path":823,"add":72,"del":15},{"path":1342,"add":46,"del":15},{"path":129,"add":1496,"del":15},49,{"path":1263,"add":1498,"del":15},51,{"path":131,"add":171,"del":11},{"path":136,"add":147,"del":15},{"path":1221,"add":107,"del":15},541,{"hash":1504,"date":1367,"subject":1505,"files":1506,"plus":545,"minus":11},"da0886f","the command palette becomes a universal search: it already indexed pages\u002Fprojects\u002Fblog, so this adds every \u002Fuses gear item (searching a tool name jumps to it — external links open in a tab) plus the previously-missing museum\u002Fkeyboard\u002Fstats\u002Fstatus pages, under a new Uses section",[1507],{"path":17,"add":545,"del":11},{"hash":1509,"date":1367,"subject":1510,"files":1511,"plus":82,"minus":15},"09a7846","the \u002Fcontact vCard QR fits a 320px phone: the fixed-width character grid was clipped ~12px past the right edge there, cutting scannable modules — it shrinks its font and quiet-zone padding below 400px so the whole square (with margins) stays on-screen and scannable",[1512],{"path":27,"add":82,"del":15},{"hash":1514,"date":1367,"subject":1515,"files":1516,"plus":1519,"minus":15},"79c5b43","inline code spans in blog posts are now click\u002Ftap-to-copy, matching the fenced-block copy button — usePostEnhancements marks every non-fenced \u003Ccode> copyable (idempotent), with a hover ring on fine pointers and an accent flash on copy, so grabbing a command or filename works without select-and-copy (which is fiddly on touch)",[1517,1518],{"path":1248,"add":444,"del":15},{"path":564,"add":448,"del":15},37,{"hash":1521,"date":1367,"subject":1522,"files":1523,"plus":82,"minus":15},"51fece0","the lvOS Mail compose and send buttons grow to a ~2.4rem tap target on coarse pointers — they were ~18px with no touch rule at all, while the rest of lvOS enforces ~40px",[1524],{"path":1297,"add":82,"del":15},{"hash":1526,"date":1367,"subject":1527,"files":1528,"plus":302,"minus":15},"b3f5007","the lvOS Chat, Mail-compose and Notes text fields get the 16px coarse-pointer rule the Run dialog and Playground already have, so focusing them on iOS no longer zooms the whole desktop — they inherited the ~12-13px window font and tripped Safari's focus-zoom",[1529,1530,1531],{"path":1326,"add":28,"del":15},{"path":1297,"add":164,"del":15},{"path":1329,"add":28,"del":15},{"hash":1533,"date":1367,"subject":1534,"files":1535,"plus":21,"minus":40},"8bc1089","a 'keyclick' command adds opt-in mechanical-keyboard typing sounds to the terminal — off by default, persisted, and played through the shared volume\u002Fmute (useVolume) so it's silent when muted like every other sound; one reused AudioContext ticks per keystroke (a fresh context per key would exhaust the browser budget), and the setting joins the 'settings' readout",[1536,1537,1539,1541,1542],{"path":222,"add":164,"del":11},{"path":1538,"add":492,"del":15},"app\u002Fcomposables\u002FuseKeyClick.ts",{"path":1540,"add":572,"del":15},"app\u002Futils\u002FkeyClick.ts",{"path":823,"add":11,"del":15},{"path":36,"add":302,"del":11},{"hash":1544,"date":1367,"subject":1545,"files":1546,"plus":731,"minus":223},"eb768f2","the terminal 'colorscheme' command accepts a custom colour — '#ff3e88' or an 'h s l' triple — reaching the same useAccent.applyCustom() the lvOS colour picker uses; it previously only took the named presets and rejected everything else, so an arbitrary accent was lvOS-only despite the underlying state being shared",[1547,1548,1549],{"path":115,"add":39,"del":40},{"path":36,"add":545,"del":82},{"path":127,"add":11,"del":15},{"hash":1551,"date":1367,"subject":1552,"files":1553,"plus":1119,"minus":444},"29ea7d8","a terminal 'scores' command prints your local hall of fame — the same personal-best board the lvOS Scores app shows, now from a shared HALL_OF_FAME list both read (the app dropped its own inline copy); previously 'leaderboard' with no relay could only point you at the desktop app, which the terminal had no sibling for",[1554,1556,1557],{"path":1555,"add":28,"del":461},"app\u002Fcomponents\u002Fdesktop\u002FDesktopScores.vue",{"path":1078,"add":1154,"del":15},{"path":962,"add":120,"del":11},{"hash":1559,"date":1367,"subject":1560,"files":1561,"plus":147,"minus":39},"69a5d2b","a plain personal-best confetti burst can no longer inherit a stale world-record flag: the fx-world-record flag lives ~4.2s (the toast owns it) but the confetti finishes in ~2s, so a second celebration in that window read the lingering flag and played the big variant; the personal-best sink now clears worldRecord unless a real record already owns the tick (guarded so it never clobbers the leaderboard's just-set flag, since that sink runs first) — extracted as the tested planPersonalBest()",[1562,1564,1566],{"path":1563,"add":263,"del":39},"app\u002Fplugins\u002Fcelebration.client.ts",{"path":1565,"add":1141,"del":15},"app\u002Futils\u002Fcelebration.ts",{"path":1567,"add":182,"del":15},"tests\u002Fcelebration.test.ts",{"hash":1569,"date":1367,"subject":1570,"files":1571,"plus":1579,"minus":175},"bf58b8e","unify the two calendars on one Monday-first core: the terminal cal rendered Sunday-first while the lvOS taskbar popover was Monday-first — two hand-rolled grid builders disagreeing on the same month; a shared monthGrid() in calendar.ts (title + Monday-first leading blanks + days-in-month) now backs both, the popover drops its inline offset math, and cal flips to the site's European Monday-first convention (tests + the cal e2e updated)",[1572,1574,1576,1577],{"path":1573,"add":43,"del":164},"app\u002Fcomponents\u002Fdesktop\u002FDesktopCalendarPopover.vue",{"path":1575,"add":572,"del":223},"app\u002Futils\u002Fterminal\u002Fcalendar.ts",{"path":937,"add":11,"del":11},{"path":1578,"add":171,"del":82},"tests\u002Fcalendar.test.ts",62,{"hash":1581,"date":1367,"subject":1582,"files":1583,"plus":61,"minus":448},"3fcb362","extract one tested browserName(ua) helper shared by the terminal sysinfo card and the lvOS About-This-Computer dialog — they parsed the UA separately and disagreed on Opera (sysinfo had no OPR\u002F branch and mislabelled it Chrome); both now call the shared helper, which orders Edge\u002FOpera before Chrome since both carry the Chrome\u002F token",[1584,1585,1587,1588],{"path":850,"add":15,"del":164},{"path":1586,"add":182,"del":15},"app\u002Futils\u002FbrowserName.ts",{"path":935,"add":40,"del":28},{"path":1589,"add":175,"del":15},"tests\u002FbrowserName.test.ts",{"hash":1591,"date":1367,"subject":1592,"files":1593,"plus":72,"minus":11},"607d870","neofetch's 'work' line reads data\u002Fprofile.ts (the roles carrying an '@ org') instead of a hand-copied third phrasing that drifted from the single source — 'Head of Dev @ Nosana' vs profile's 'head of development @ Nosana'",[1594],{"path":935,"add":72,"del":11},{"hash":1596,"date":1367,"subject":1597,"files":1598,"plus":72,"minus":40},"9255eb0","the status bar's offline indicator uses the theme-aware var(--bulma-warning-on-scheme) instead of a hardcoded hsl(45deg,90%,60%) that washed out to near-invisible on the light theme's near-white chrome, honouring the no-hardcoded-colours rule",[1599],{"path":880,"add":72,"del":40},{"hash":1601,"date":1367,"subject":1602,"files":1603,"plus":197,"minus":40},"719a5e8","the status bar's right-hand controls stay on-screen on a phone: the two groups' natural width exceeded the viewport and space-between shoved the right group — with the terminal button and the sun\u002Fmoon toggle, the ONLY tap-accessible theme switch on mobile — off the edge; the left easter-egg group now shrinks and clips its least-essential items first (min-width:0\u002Foverflow:hidden on coarse), the coarse item padding drops 0.85rem->0.55rem, and the local-time clock hides below 360px",[1604],{"path":880,"add":197,"del":40},{"hash":1606,"date":1367,"subject":1607,"files":1608,"plus":64,"minus":43},"9aa2805","the site and lvOS boot sequences skip their typed animation for the manual reduce-motion switch too, not only the OS prefers-reduced-motion query — both gated on usePreferredReducedMotion() and ignored a visitor who flipped the in-app switch; they now read the combined prefersReducedMotion() helper (the startup chime is audio, still guarded by mute\u002Fvolume, so it stays)",[1609,1610],{"path":769,"add":72,"del":72},{"path":771,"add":40,"del":39},{"hash":1612,"date":1367,"subject":1613,"files":1614,"plus":25,"minus":164},"9f576fb","the manual reduce-motion switch now settles the three lvOS runtime loops the R34 migration missed — the Life wallpaper, the spinning visitor globe and the wandering pet each snapshotted prefersReducedMotion() once in onMounted and animated forever, contradicting the museum plaque; a reactive useReducedMotion() computed (manual useState OR the OS query) that each watches starts\u002Fstops the interval live",[1615,1616,1617,1619],{"path":231,"add":82,"del":39},{"path":233,"add":164,"del":39},{"path":1618,"add":72,"del":39},"app\u002Fcomponents\u002Fdesktop\u002FDesktopPetWidget.vue",{"path":1620,"add":448,"del":15},"app\u002Fcomposables\u002FuseReducedMotion.ts",{"hash":1622,"date":1367,"subject":1623,"files":1624,"plus":64,"minus":15},"6017f9c","wait for the status-bar clock (a client-hydration signal) before pressing vim go-to chord keys in the e2e tests, so a pre-hydration 'g' keypress can't be dropped and land the flaky gb->\u002Fblog navigation on \u002Fabout",[1625],{"path":843,"add":64,"del":15},{"hash":1627,"date":1367,"subject":1628,"files":1629,"plus":1638,"minus":185},"d0583f4","the console hunt becomes five one-line devtools rounds (decode base64, inspect lv.vault, read a function's source, char codes, combine your answers back via lv.finish) with no brute-force finale, and its reward is the new gated 'backstage' command — solving unlocks an all-access pass with a one-of-one code and a priority mailto, kept out of secrets and inert until earned",[1630,1631,1634,1635,1636,1637],{"path":103,"add":11,"del":11},{"path":1632,"add":1633,"del":182},"app\u002Fplugins\u002Fconsole-egg.client.ts",116,{"path":823,"add":40,"del":15},{"path":36,"add":147,"del":64},{"path":861,"add":39,"del":11},{"path":843,"add":1498,"del":163},215,{"hash":1640,"date":1367,"subject":1641,"files":1642,"plus":64,"minus":39},"ba5fc00","refresh the README mobile section and 375px lvOS screenshot for this round's phone fixes (stacked \u002Fnow ps table, slimmer \u002Fkeyboard, contained pixel-world minimap, wrapping lvOS Settings options)",[1643,1644,1646],{"path":10,"add":11,"del":11},{"path":1645,"add":28,"del":40},"app\u002Fcomponents\u002FShortcutRows.vue",{"path":89,"add":15,"del":15},{"hash":1648,"date":1367,"subject":1649,"files":1650,"plus":164,"minus":72},"c1e7d0b","the museum and architecture docs catch up to this round: the high-score-burst plaque notes the new world-record tier, the reduce-motion\u002Fchime craft exhibit tells the truth (canvas animations stop too, reachable via the terminal reducemotion\u002Fsettings commands, chime on entering lvOS), and architecture.md documents the prefersReducedMotion() helper and the fx-world-record flag",[1651,1652],{"path":103,"add":40,"del":40},{"path":105,"add":43,"del":40},{"hash":1654,"date":1367,"subject":1655,"files":1656,"plus":509,"minus":15},"6fdd737","the \u002Fnow 'ps aux' table stacks into per-process cards below 600px instead of crushing the COMMAND column to ~44px and breaking words mid-character: the PID\u002FSTAT\u002F%CPU meta sits on one line with the full command spanning the width below, and the misaligned header is hidden on mobile",[1657],{"path":513,"add":509,"del":15},{"hash":1659,"date":1367,"subject":1660,"files":1661,"plus":1675,"minus":223},"f80d464","beating the global #1 on the relay leaderboard now gets its own celebration on top of the personal-best confetti: useLeaderboard flags a world record when a submitted score tops the reigning board entry, ScoreCelebration fires a bigger, longer burst, and a 'new world record — \u003Cgame>!' banner rides the shared worldRecord flag site-wide (terminal and lvOS), degrading to nothing when offline",[1662,1664,1666,1668,1670,1672,1674],{"path":1663,"add":263,"del":43},"app\u002Fcomponents\u002FScoreCelebration.vue",{"path":1665,"add":352,"del":15},"app\u002Fcomponents\u002FWorldRecordToast.vue",{"path":1667,"add":263,"del":15},"app\u002Fcomposables\u002FuseLeaderboard.ts",{"path":1669,"add":72,"del":11},"app\u002Fcomposables\u002FuseSiteEffects.ts",{"path":1671,"add":40,"del":11},"app\u002Flayouts\u002Fdefault.vue",{"path":1673,"add":40,"del":11},"app\u002Flayouts\u002Fdesktop.vue",{"path":823,"add":11,"del":15},111,{"hash":1677,"date":1367,"subject":1678,"files":1679,"plus":28,"minus":11},"7a21f8b","the home-page hero terminal card opens the shell through the lightweight useTerminalLauncher (open\u002FisOpen only) instead of the full useTerminal(), keeping the ~45KB command registry out of the home page's initial bundle — it was the one always-rendered home component still statically importing the registry and quietly undoing R33's lazy-mount win on the most-visited page",[1680],{"path":742,"add":28,"del":11},{"hash":1682,"date":1367,"subject":1683,"files":1684,"plus":43,"minus":15},"4b4f59b","the About 'laurens.ts' code card wraps its lines below 900px instead of clipping them mid-word behind an invisible overflow-x scroll, so on a tablet the pre reads as intact code rather than looking broken",[1685],{"path":1686,"add":43,"del":15},"app\u002Fcomponents\u002FCodeSnippet.vue",{"hash":1688,"date":1367,"subject":1689,"files":1690,"plus":223,"minus":11},"b2736b6","headings stop breaking mid-token at 320px: the \u002Fchangelog commit-count badge ([627]) and the \u002Fmuseum '[ catalog ]' \u002F '[ walk the floor ]' mode tabs get white-space:nowrap so the count doesn't split between digits and the closing bracket doesn't drop to its own line",[1691,1692],{"path":653,"add":43,"del":11},{"path":660,"add":39,"del":15},{"hash":1694,"date":1367,"subject":1695,"files":1696,"plus":263,"minus":11},"755eaad","the \u002Fkeyboard and shortcut cheat-sheet rows drop their fixed 9rem key-column reservation below 520px, so on a phone the label reclaims the ~35% of the row that a single-key shortcut used to waste and stops wrapping to three lines — the reserved width now flows through a custom property so the mobile media query can override it",[1697],{"path":1645,"add":263,"del":11},{"hash":1699,"date":1367,"subject":1700,"files":1701,"plus":72,"minus":72},"633a3d0","the network offline\u002Fonline toast uses theme vars instead of hardcoded amber\u002Fgreen hues: the offline tint now tracks a custom site accent via --lv-primary-hsl and the back-online tint uses the Bulma success var, honouring the no-hardcoded-colors rule",[1702],{"path":1703,"add":72,"del":72},"app\u002Fcomponents\u002FNetworkToast.vue",{"hash":1705,"date":1367,"subject":1706,"files":1707,"plus":39,"minus":11},"2e54cb2","the code-playground preview iframe defaults to the dark window tone instead of a glaring #fff, so a snippet with no background of its own blends into the always-dark lvOS chrome rather than flashing a bright white panel",[1708],{"path":1165,"add":39,"del":11},{"hash":1710,"date":1367,"subject":1711,"files":1712,"plus":64,"minus":15},"a785c2a","the lvOS Settings option rows wrap instead of clipping on a narrow window: the screensaver row's third choice ([mystify]) no longer disappears off the right edge, since .settings-options and .settings-row now flex-wrap",[1713],{"path":852,"add":64,"del":15},{"hash":1715,"date":1367,"subject":1716,"files":1717,"plus":28,"minus":15},"fbbac29","booting or rebooting into lvOS now plays the startup chime, mute\u002Fvolume-guarded, closing the gap where bootChime's own doc claimed it sounds 'entering lvOS' but only the site splash was ever wired to it",[1718],{"path":771,"add":28,"del":15},{"hash":1720,"date":1367,"subject":1721,"files":1722,"plus":1141,"minus":15},"a9ac625","a 'settings' terminal command prints every appearance\u002Fsound\u002Faccessibility setting (theme, accent, volume, wallpaper, screensaver, night light, reduce motion) with its live value in one place, so the state scattered across Settings\u002FBIOS\u002FDisplays has a single readable readout",[1723],{"path":36,"add":1141,"del":15},{"hash":1725,"date":1367,"subject":1726,"files":1727,"plus":120,"minus":15},"5995932","a 'reducemotion [on|off]' terminal command flips the shared reduce-motion switch, so the motion toggle is reachable from the shell like volume\u002Fnightlight\u002Fwallpaper\u002Fscreensaver instead of only from lvOS Settings",[1728],{"path":36,"add":120,"del":15},{"hash":1730,"date":1367,"subject":1731,"files":1732,"plus":223,"minus":72},"e6a727a","the pixel-world minimap stops floating up over the intro text at 320px: the stage gets a 9rem floor plus overflow:hidden so the tall mobile palette\u002FHUD rows can't starve it to a sliver, and the \u002Fworld frame grows with min-height instead of a fixed height on phones so nothing is clipped",[1733,1734],{"path":277,"add":28,"del":11},{"path":1735,"add":28,"del":39},"app\u002Fpages\u002Fworld.vue",{"hash":1737,"date":1367,"subject":1738,"files":1739,"plus":82,"minus":11},"0c0820e","opening the terminal for the first time via a queued chrome command (the footer's git show, the status-bar pet) now still shows the welcome banner: the greet runs before the pending queue drains, since the immediate pending-watch fires during setup and used to fill the transcript before onMounted's greet-if-empty check ran",[1740],{"path":363,"add":82,"del":11},{"hash":1742,"date":1367,"subject":1743,"files":1744,"plus":61,"minus":461,"truncated":82},"8f03c70","the manual 'reduce motion' switch now stops every JS\u002Fcanvas animation too, not just CSS ones: a shared prefersReducedMotion() helper ORs the data-reduce-motion attribute with the OS media query and replaces the ~19 raw matchMedia reads (matrix stays alwaysAnimate), and useCanvasScene gates reactively on both so ambient scenes freeze the moment the switch flips",[1745,1747,1748,1750,1751,1753,1754,1755,1756,1757,1758,1760],{"path":1746,"add":11,"del":11},"app\u002Fcomponents\u002FDomDestroyer.vue",{"path":949,"add":11,"del":11},{"path":1749,"add":11,"del":40},"app\u002Fcomponents\u002FHeroLife.vue",{"path":742,"add":11,"del":11},{"path":1752,"add":11,"del":11},"app\u002Fcomponents\u002FRetroHitCounter.vue",{"path":1663,"add":11,"del":11},{"path":231,"add":11,"del":11},{"path":233,"add":11,"del":11},{"path":1618,"add":11,"del":11},{"path":235,"add":11,"del":11},{"path":1759,"add":11,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopUpdate.vue",{"path":1761,"add":11,"del":11},"app\u002Fcomposables\u002FuseBrandTyping.ts",{"hash":1763,"date":1367,"subject":1764,"files":1765,"plus":72,"minus":11},"72d59ba","the lazily-mounted command palette seeds its active selection on mount (immediate watch) so Enter right after ⌘K navigates the first item instead of doing nothing — the non-immediate watch never fired on the already-open mount, leaving activeId empty and no row highlighted",[1766],{"path":334,"add":72,"del":11},{"hash":1768,"date":1367,"subject":1769,"files":1770,"plus":1633,"minus":203},"bd9c239","harden the lazy terminal\u002Fpalette: a client plugin restores and seeds the VFS on every page (so blog overrides, reseed and Files work without the terminal ever mounting), the sole ~\u002Fctrl+k opener lives in the layout shim (no second handler racing the async mount), the overlay grabs input focus on mount so Escape\u002Ftyping land, the hero polls for the lazily-mounted input, and the palette e2e opens via a retry helper",[1771,1772,1773,1774,1775,1777,1778,1780,1782],{"path":334,"add":72,"del":82},{"path":742,"add":164,"del":39},{"path":363,"add":448,"del":75},{"path":1671,"add":120,"del":72},{"path":1776,"add":448,"del":15},"app\u002Fplugins\u002FterminalFs.client.ts",{"path":42,"add":11,"del":11},{"path":1779,"add":24,"del":171},"app\u002Futils\u002Fterminal\u002FterminalStorage.ts",{"path":1781,"add":164,"del":15},"e2e\u002Fhelpers.ts",{"path":438,"add":72,"del":39},{"hash":1784,"date":1367,"subject":1785,"files":1786,"plus":11,"minus":11},"8920943","the boot-replay pid stays a module-local constant (knip flagged the export as unused now that only bootProcs reads it)",[1787],{"path":95,"add":11,"del":11},{"hash":1789,"date":1367,"subject":1790,"files":1791,"plus":11,"minus":11},"527acda","the readme's phone rundown covers this round's mobile work: the terminal no longer shifts sideways on open, grid games scroll instead of wrapping, the taskbar tray collapses behind a ⋯, and the status bar, filter chips and pixel-world swatches grow to thumb size — down to 320px",[1792],{"path":10,"add":11,"del":11},{"hash":1794,"date":1367,"subject":1795,"files":1796,"plus":448,"minus":39},"f95f86c","the museum's screensaver\u002Fnight-light plaque tells the truth (night light: settings\u002Fdisplays\u002Fbios\u002Fterminal; screensaver: settings\u002Fbios\u002Fterminal — not five ways in), the process-table and thumb plaques cover the boot-replay proc and this round's mobile work, two craft exhibits note the lazy shell and the app\u002Freduce-motion additions, and architecture.md documents the lazy terminal",[1797,1798],{"path":103,"add":28,"del":39},{"path":105,"add":164,"del":15},{"hash":1800,"date":1367,"subject":1801,"files":1802,"plus":545,"minus":72},"7582dd3","the two multi-MB project hover GIFs (1.26MB + 937KB) become ~100-160KB muted looping webm\u002Fmp4 videos that play on hover\u002Ffocus: ProjectCard renders a \u003Cvideo> with webm+mp4 sources for video previews and preloads nothing until hovered, an ~8-10x weight drop on the projects and home grids",[1803,1804,1805,1807,1809,1811,1813,1815],{"path":951,"add":278,"del":40},{"path":528,"add":40,"del":40},{"path":1806,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fkids-age-hover.gif",{"path":1808,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fkids-age-hover.mp4",{"path":1810,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fkids-age-hover.webm",{"path":1812,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fself-coding-website-hover.gif",{"path":1814,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fself-coding-website-hover.mp4",{"path":1816,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fself-coding-website-hover.webm",{"hash":1818,"date":1367,"subject":1819,"files":1820,"plus":122,"minus":15},"d301f05","a 'reduce motion' toggle in lvos settings flattens every animation and transition site-wide (via a data-reduce-motion attribute on the html element), on top of the OS prefers-reduced-motion the site already honours, for visitors who want motion off regardless of their system setting",[1821,1822,1823,1824,1826],{"path":416,"add":72,"del":15},{"path":418,"add":1141,"del":15},{"path":852,"add":263,"del":15},{"path":1825,"add":545,"del":15},"app\u002Fcomposables\u002FuseReduceMotion.ts",{"path":823,"add":39,"del":15},{"hash":1828,"date":1367,"subject":1829,"files":1830,"plus":203,"minus":15},"f943a65","the boot splash plays a short startup chime through a one-shot web-audio arpeggio, gated on the shared volume\u002Fmute so it stays silent when muted; quiet-boot skips the splash (and so the chime), and autoplay policy just mutes it on a gesture-less first visit",[1831,1832],{"path":769,"add":43,"del":15},{"path":1833,"add":61,"del":15},"app\u002Futils\u002FbootChime.ts",{"hash":1835,"date":1367,"subject":1836,"files":1837,"plus":1840,"minus":15},"697b5dc","a dismissible 'install lvOS as an app' chip appears when the browser fires beforeinstallprompt, replaying the native install prompt on tap and staying gone once dismissed or installed",[1838,1839],{"path":420,"add":1109,"del":15},{"path":1671,"add":11,"del":15},137,{"hash":1842,"date":1367,"subject":1843,"files":1844,"plus":263,"minus":72},"49808e8","the contact page builds its ascii QR from a dynamically-imported generator on mount, so qrcode-generator (only ever needed on \u002Fcontact) no longer rides the site-wide preload bundle onto every page; its e2e polls for the client-filled code",[1845,1846],{"path":27,"add":82,"del":39},{"path":438,"add":39,"del":11},{"hash":1848,"date":1367,"subject":1849,"files":1850,"plus":1866,"minus":263},"a5188b0","the terminal and command palette (and the ~45KB command registry they pull in) are lazily mounted on first open instead of on every page: always-mounted chrome opens them through a lightweight launcher, keydown shims in the layout keep ~\u002Fctrl+k working, and useModalMenu engages immediately so a mounted-already-open overlay still traps focus",[1851,1853,1854,1855,1857,1858,1860,1862,1864,1865],{"path":1852,"add":40,"del":11},"app\u002Fcomponents\u002FAppFooter.vue",{"path":248,"add":72,"del":40},{"path":880,"add":72,"del":40},{"path":1856,"add":40,"del":11},"app\u002Fcomponents\u002FMobileNav.vue",{"path":363,"add":46,"del":11},{"path":1859,"add":263,"del":11},"app\u002Fcomposables\u002FuseBlogOverrides.ts",{"path":1861,"add":28,"del":11},"app\u002Fcomposables\u002FuseModalMenu.ts",{"path":1863,"add":572,"del":15},"app\u002Fcomposables\u002FuseTerminalLauncher.ts",{"path":1671,"add":545,"del":40},{"path":823,"add":11,"del":15},105,{"hash":1868,"date":1367,"subject":1869,"files":1870,"plus":72,"minus":11},"3fda168","the lvos welcome toast tells touch visitors to long-press the desktop for a menu (which is what the long-press handler already does) instead of 'right-click', which a phone can't do",[1871],{"path":284,"add":72,"del":11},{"hash":1873,"date":1367,"subject":1874,"files":1875,"plus":46,"minus":15},"5fd4e76","the blog tag chips, projects filter flags and museum mode chips grow to a ~40px tap area on touch pointers, since the ~24px text\u002Ftag buttons were fiddly to hit on a phone",[1876,1878,1879],{"path":1877,"add":28,"del":15},"app\u002Fcomponents\u002FProjectsGrid.vue",{"path":651,"add":43,"del":15},{"path":660,"add":28,"del":15},{"hash":1881,"date":1367,"subject":1882,"files":1883,"plus":461,"minus":15},"93728a9","the bottom status bar grows from ~26px to ~40px on touch pointers so the theme toggle, presence and version controls are actually tappable, and the mobile nav strip above it tracks the taller height",[1884,1885],{"path":880,"add":75,"del":15},{"path":1856,"add":223,"del":15},{"hash":1887,"date":1367,"subject":1888,"files":1889,"plus":197,"minus":15},"acffc12","the boot splash \u002F reboot replay takes a seat in the shared process table like the screensaver does: BootSplash registers a hook while mounted so 'ps'\u002F'kill 200'\u002Fthe task manager see boot-replay.sh while it's on screen, closing the last gap where a fullscreen effect wasn't a process",[1890,1891,1892],{"path":769,"add":64,"del":15},{"path":113,"add":40,"del":15},{"path":95,"add":46,"del":15},{"hash":1894,"date":1367,"subject":1895,"files":1896,"plus":75,"minus":15},"4822edc","the pixel world colour swatches grow from 22px to a thumb-sized ~32px on touch pointers, since picking a colour is the canvas's core interaction and the small squares were easy to mis-tap",[1897],{"path":277,"add":75,"del":15},{"hash":1899,"date":1367,"subject":1900,"files":1901,"plus":223,"minus":39},"0a076ca","the pixel world canvas sizes itself from its flex stage instead of a hardcoded host-minus-116px reserve, so when the palette wraps to two rows on a phone the opaque canvas stops overflowing down over the coordinate hud and the top row of colour swatches",[1902],{"path":277,"add":223,"del":39},{"hash":1904,"date":1367,"subject":1905,"files":1906,"plus":223,"minus":11},"d3def2f","the terminal 'pixel place' command holds at most one world lease for the whole session instead of acquiring a fresh refcounted lease on every call that it never released, which kept the world socket from ever closing",[1907],{"path":301,"add":223,"del":11},{"hash":1909,"date":1367,"subject":1910,"files":1911,"plus":223,"minus":15},"18a51b6","the terminal grid games (snake\u002F2048\u002Ftetris) stop wrapping and corrupting on a phone: the game frame is white-space:pre with its own horizontal scroll instead of inheriting the transcript's pre-wrap, and shrinks its font on coarse pointers so most of the board fits without scrolling",[1912],{"path":222,"add":223,"del":15},{"hash":1914,"date":1367,"subject":1915,"files":1916,"plus":72,"minus":15},"447ffe5","the terminal no longer scrolls ~50px to the left when it opens on a phone: the flex input gets min-width:0 so it can shrink below its intrinsic width instead of overflowing the row and making the autofocus scroll the pane, which was clipping the prompt, welcome line and quick-keys",[1917],{"path":222,"add":72,"del":15},{"hash":1919,"date":1367,"subject":1920,"files":1921,"plus":39,"minus":11},"7c5a785","the mobile taskbar's collapsed ⋯ tray dismisses itself when you tap an item inside it (weather\u002Ftile\u002Ffullscreen): a click on the tray bubbles up and closes moreOpen, so the popover no longer lingers over the app or window the tap just opened",[1922],{"path":271,"add":39,"del":11},{"hash":1924,"date":1367,"subject":1925,"files":1926,"plus":82,"minus":40},"632287b","asciicam and the git-log fetch can't wedge the serialized terminal run queue anymore: asciicam fires its getUserMedia flow without the exec awaiting the non-modal permission prompt, and the git-log.json $fetch gets the 10s timeout every other terminal fetch already has",[1927,1928],{"path":36,"add":39,"del":11},{"path":1039,"add":28,"del":11},{"hash":1930,"date":1367,"subject":1931,"files":1932,"plus":28,"minus":40},"daefca5","an lvos app only stretches to fill its window when it's the window body's single root: the readme window slots several \u003Cp> paragraphs directly, and flex:1 on each was spreading them apart with big equal gaps on a maximized phone window",[1933],{"path":286,"add":28,"del":40},{"hash":1935,"date":1367,"subject":1936,"files":1937,"plus":64,"minus":11},"18ec368","the shared volume defaults to 70 on a fresh visit instead of muting: the restore no longer treats a missing key as Number(null)=0, which was finite and in-range and silently overwrote the default with 0",[1938],{"path":1939,"add":64,"del":11},"app\u002Fcomposables\u002FuseVolume.ts",{"hash":1941,"date":1367,"subject":1942,"files":1943,"plus":164,"minus":28},"2b5252a","the task-manager e2e drives 2048 instead of snake: snake auto-advances and can die (game over) before the taskmgr check under full-suite load, dropping it from the process table; 2048 only moves on input so it stays a live process",[1944],{"path":162,"add":164,"del":28},{"hash":1946,"date":1367,"subject":1947,"files":1948,"plus":64,"minus":72},"1b89772","give the task-manager e2e's queued-command effects (crt toggle, kills) up to 10s each, so the terminal's serialized command queue lagging under full-suite load can't fail them inside the default 5s window",[1949],{"path":162,"add":64,"del":72},{"hash":1951,"date":1367,"subject":1952,"files":1953,"plus":1107,"minus":187},"d22d63e","the destroyer ship is flown on mobile with a proper virtual joystick at the bottom-right (a knob you push to steer and thrust) and the fire button moves to the bottom-left, instead of flying toward wherever you touch",[1954],{"path":1746,"add":1107,"del":187},{"hash":1956,"date":1367,"subject":1957,"files":1958,"plus":263,"minus":39},"aee7e6b","the destroyer ship spawns in the middle of the viewport and its score readout sits at the bottom on desktop (and stays up top on touch, where the bottom is the joystick's)",[1959],{"path":1746,"add":263,"del":39},{"hash":1961,"date":1367,"subject":1962,"files":1963,"plus":72,"minus":11},"860d0b3","the pixel world's palette, time-lapse and hint rows stop spilling out the bottom of the frame (and under the mobile tab bar): the canvas root takes its height from the host frame instead of a competing height:100%",[1964],{"path":277,"add":72,"del":11},{"hash":1966,"date":1367,"subject":1967,"files":1968,"plus":223,"minus":40},"9ecb139","the heading decode animation locks its height and clips overflow (not just width), so a heading near a line's edge can't briefly wrap to a second line and reflow the page while scrambling",[1969],{"path":837,"add":223,"del":40},{"hash":1971,"date":1367,"subject":1972,"files":1973,"plus":164,"minus":40},"0e0f2df","clicking your pet in the status bar opens the terminal and runs 'pet' so you actually see how it's doing, instead of just opening an empty shell",[1974],{"path":880,"add":164,"del":40},{"hash":1976,"date":1367,"subject":1977,"files":1978,"plus":39,"minus":15},"304eadb","the boot screen's ascii logo no longer shows a small scrollbar: it overrides bulma's base overflow-x:auto on \u003Cpre> (the boot screen already clips its own overflow)",[1979],{"path":771,"add":39,"del":15},{"hash":1981,"date":1367,"subject":1982,"files":1983,"plus":163,"minus":40},"efa289a","lvos apps fill their window instead of leaving the lower half empty when maximized: the window body is a flex column that stretches the slotted app to full height, and the gallery drops its fixed width so the image viewer uses the whole pane",[1984,1985],{"path":1281,"add":72,"del":40},{"path":286,"add":164,"del":15},{"hash":1987,"date":1367,"subject":1988,"files":1989,"plus":1993,"minus":25},"4d3f8f8","on a phone the lvos taskbar tray collapses weather, battery, tile and fullscreen behind a ⋯ toggle (a popover dismissed by an outside tap), keeping only volume, notifications and the clock in the row",[1990,1991],{"path":271,"add":137,"del":1154},{"path":1992,"add":223,"del":11},"app\u002Fcomposables\u002FuseTaskbarPopovers.ts",94,{"hash":1995,"date":1367,"subject":1996,"files":1997,"plus":28,"minus":39},"9ddda74","clicking the weather chip in the lvos taskbar opens the weather app instead of doing nothing",[1998],{"path":271,"add":28,"del":39},{"hash":2000,"date":1367,"subject":2001,"files":2002,"plus":39,"minus":11},"b83685e","the task-manager e2e gives the lazily-imported snake chunk up to 10s to render its first frame, so a slow full-suite run can't miss it inside the default 5s assertion window",[2003],{"path":162,"add":39,"del":11},{"hash":2005,"date":1367,"subject":2006,"files":2007,"plus":64,"minus":40},"1ac5a21","the font stacks are parenthesised lists, not quoted strings: '\"Inter\", …' emitted as one invalid quoted family name so every is-family-code element (terminal, museum floor, all ascii grids) silently fell back to a proportional font — now jetbrains mono and inter actually load",[2008],{"path":418,"add":64,"del":40},{"hash":2010,"date":1367,"subject":2011,"files":2012,"plus":352,"minus":1141},"d3e25b0","the bios is reachable and usable on touch: the 'press DEL' hint is a tappable button, and the setup rows tap to select+cycle with a prev\u002Fnext\u002Fsave touch bar, since it was keyboard-only before",[2013,2016],{"path":2014,"add":2015,"del":75},"app\u002Fcomponents\u002Fdesktop\u002FDesktopBios.vue",58,{"path":771,"add":461,"del":72},{"hash":2018,"date":1367,"subject":2019,"files":2020,"plus":940,"minus":43},"78aadd8","letter-driven terminal games (adventure, hangman, wpm) are playable on touch: they mark acceptsText, and the mobile console raises the soft keyboard through an invisible capture field that routes typed characters into the game",[2021,2022,2024,2026,2028],{"path":222,"add":1579,"del":43},{"path":2023,"add":11,"del":15},"app\u002Futils\u002Fgames\u002Fadventure\u002Fhandle.ts",{"path":2025,"add":11,"del":15},"app\u002Futils\u002Fgames\u002Fhangman.ts",{"path":2027,"add":64,"del":15},"app\u002Futils\u002Fgames\u002Ftypes.ts",{"path":2029,"add":11,"del":15},"app\u002Futils\u002Fgames\u002Fwpm.ts",{"hash":2031,"date":1367,"subject":2032,"files":2033,"plus":163,"minus":15},"33dfd79","the terminal quick-key row gains split and close-pane buttons on touch, so tmux panes are reachable on a phone where there's no ctrl+b prefix",[2034],{"path":222,"add":163,"del":15},{"hash":2036,"date":1367,"subject":2037,"files":2038,"plus":200,"minus":72},"d20f6f1","checkmating the house feeds the shared economy like every other game: it tallies lv-chess-ai-wins into the hall of fame and pet coins and fires the win-confetti, instead of only printing a trophy line",[2039,2040,2041,2042,2043],{"path":986,"add":120,"del":15},{"path":1555,"add":11,"del":15},{"path":1072,"add":40,"del":11},{"path":1080,"add":39,"del":15},{"path":1087,"add":39,"del":39},{"hash":2045,"date":1367,"subject":2046,"files":2047,"plus":43,"minus":15},"2b34872","the lock screen sits its password box in the top third on touch, so the on-screen keyboard no longer covers the input while you type your password",[2048],{"path":374,"add":43,"del":15},{"hash":2050,"date":1367,"subject":2051,"files":2052,"plus":28,"minus":15},"c042616","the code playground editor is 16px on touch so ios doesn't zoom the page when you focus it to type code",[2053],{"path":1165,"add":28,"del":15},{"hash":2055,"date":1367,"subject":2056,"files":2057,"plus":263,"minus":15},"6a43739","the terminal input and the run dialog input never render below 16px on touch, so ios safari stops zooming the page in when you focus them",[2058,2059],{"path":222,"add":64,"del":15},{"path":2060,"add":28,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopRunDialog.vue",{"hash":2062,"date":1367,"subject":2063,"files":2064,"plus":82,"minus":28},"66202de","the ? shortcuts overlay actually behaves like the modal it declares: useModalMenu moves focus in, traps tab, restores focus on close, matching the terminal and palette instead of leaking focus to the inert page behind it",[2065],{"path":2066,"add":82,"del":28},"app\u002Fcomponents\u002FShortcutsHelp.vue",{"hash":2068,"date":1367,"subject":2069,"files":2070,"plus":11,"minus":11},"8b248d8","the status bar is a role=toolbar with a label instead of a second contentinfo landmark colliding with the page footer, so screen-reader landmark navigation isn't ambiguous",[2071],{"path":880,"add":11,"del":11},{"hash":2073,"date":1367,"subject":2074,"files":2075,"plus":164,"minus":64},"a294014","the window's south-east resize corner keeps its 28px touch size: the coarse-pointer sizing now comes after the .is-se grip rule, which used to override it back down to the smallest handle",[2076],{"path":286,"add":164,"del":64},{"hash":2078,"date":1367,"subject":2079,"files":2080,"plus":43,"minus":40},"1429102","the night-light warmth default of 45 survives a fresh visit: the restore no longer treats a missing key as a stored 0 and zeroes the tint, so turning night light on actually shows a wash first time",[2081],{"path":2082,"add":43,"del":40},"app\u002Fcomposables\u002FuseNightLight.ts",{"hash":2084,"date":1367,"subject":2085,"files":2086,"plus":164,"minus":28},"d9298aa","the walkable museum floor uses a full-width ascii '.' instead of '·' (u+00b7), which jetbrains mono renders at half an ascii advance — the floor rows no longer collapse leftward, so the walls line up and the doors line up with the floor you walk through",[2087,2089],{"path":2088,"add":72,"del":11},"app\u002Futils\u002FmuseumWalk.ts",{"path":2090,"add":28,"del":72},"tests\u002FmuseumWalk.test.ts",{"hash":2092,"date":2093,"subject":2094,"files":2095,"plus":43,"minus":40},"e9b9e77","2026-07-12","the boss-key e2e fires both b-keydowns in one tick so a slow ci runner's latency between keypresses can't blow the 600ms double-tap arming window into a false failure",[2096],{"path":45,"add":43,"del":40},{"hash":2098,"date":2093,"subject":2099,"files":2100,"plus":461,"minus":15},"36930d5","architecture docs gain a mobile & touch section: gate touch behaviour behind coarse-pointer queries so desktop and e2e stay put, give every right-click\u002Fhover affordance a long-press or toggle, and size game boards to fit the phone",[2101],{"path":105,"add":461,"del":15},{"hash":2103,"date":2093,"subject":2104,"files":2105,"plus":11,"minus":11},"3cc59b6","the readme's mobile rundown mentions the round-31 touch fixes: minesweeper flag mode and fit-to-phone boards, thumb-sized app buttons, file long-press menus, and wrapping tables and \u002Flife controls",[2106],{"path":10,"add":11,"del":11},{"hash":2108,"date":2093,"subject":2109,"files":2110,"plus":11,"minus":11},"3e6550c","the museum's 'built for a thumb' exhibit catches up with round 31: minesweeper flag mode and fit-to-phone boards, file long-press menus, and thumb-sized app buttons",[2111],{"path":103,"add":11,"del":11},{"hash":2113,"date":2093,"subject":2114,"files":2115,"plus":64,"minus":40},"f79424d","winning a live pong, chess or wpm duel sets off the shared high-score confetti burst, matching a solo personal best, instead of only bumping a silent tally",[2116],{"path":2117,"add":64,"del":40},"app\u002Futils\u002Fgames\u002FrelayDuel.ts",{"hash":2119,"date":2093,"subject":2120,"files":2121,"plus":731,"minus":39},"09bc745","night-light warmth is now adjustable from the Settings app and the terminal (nightlight \u003C0-100>), not just BIOS and Displays, so the level isn't fixed depending on which door you came through",[2122,2123],{"path":852,"add":120,"del":15},{"path":36,"add":75,"del":39},{"hash":2125,"date":2093,"subject":2126,"files":2127,"plus":2136,"minus":39},"892620a","the 'commits since your last update' lookup lives in one tested commitsSinceMarker helper, shared by the whats-new toast, the boot nudge and the system update app instead of three copies",[2128,2129,2131,2132,2134],{"path":284,"add":11,"del":11},{"path":2130,"add":11,"del":11},"app\u002Fcomponents\u002FWhatsNew.vue",{"path":1759,"add":11,"del":11},{"path":2133,"add":448,"del":15},"app\u002Futils\u002Fupdates.ts",{"path":2135,"add":461,"del":15},"tests\u002Fupdates.test.ts",39,{"hash":2138,"date":2093,"subject":2139,"files":2140,"plus":302,"minus":263},"7560ac2","harden last round's touch\u002Fterminal changes: the desktop long-press suppresses its click via preventDefault (so a long hold no longer self-dismisses the menu) and cleans its timer on unmount, and the terminal releases its pipe sink if an async command rejects",[2141,2142],{"path":284,"add":263,"del":223},{"path":115,"add":82,"del":11},{"hash":2144,"date":2093,"subject":2145,"files":2146,"plus":64,"minus":39},"0e3ac29","the stats and github commands time out after 10s like curl and weather, so a hung request can no longer wedge every command typed after it now that the terminal runs one at a time",[2147],{"path":962,"add":64,"del":39},{"hash":2149,"date":2093,"subject":2150,"files":2151,"plus":28,"minus":15},"6e4976c","the contact wizard input is 16px on touch so iOS safari stops zooming the whole page in when you focus the field",[2152],{"path":261,"add":28,"del":15},{"hash":2154,"date":2093,"subject":2155,"files":2156,"plus":28,"minus":15},"1ca96ec","the code playground stacks its editor and preview vertically on a phone, instead of squeezing both into ~145px side-by-side panes",[2157],{"path":1165,"add":28,"del":15},{"hash":2159,"date":2093,"subject":2160,"files":2161,"plus":223,"minus":15},"add17a5","the \u002Flife page controls wrap and grow to thumb size on a phone, so the preset row no longer runs its last patterns off the right edge with no way to reach them",[2162],{"path":643,"add":223,"del":15},{"hash":2164,"date":2093,"subject":2165,"files":2166,"plus":43,"minus":15},"26a3697","wide markdown tables in a blog post scroll horizontally inside their own box on mobile, instead of being silently clipped by the page-level overflow-x:clip",[2167],{"path":564,"add":43,"del":15},{"hash":2169,"date":2093,"subject":2170,"files":2171,"plus":147,"minus":15},"2907ad6","paint swatches, calculator keys, media transport and gallery zoom buttons grow to thumb size on touch screens, so the lvos apps are usable with a finger not just a mouse",[2172,2173,2174,2176],{"path":1450,"add":28,"del":15},{"path":1281,"add":82,"del":15},{"path":2175,"add":448,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopMedia.vue",{"path":1094,"add":182,"del":15},{"hash":2178,"date":2093,"subject":2179,"files":2180,"plus":25,"minus":15},"55db21f","long-press a file in the lvos file explorer to open its row menu on touch, so rename, properties and edit-in-vim are reachable with a thumb instead of right-click-only",[2181],{"path":170,"add":25,"del":15},{"hash":2183,"date":2093,"subject":2184,"files":2185,"plus":731,"minus":39},"c83c729","minesweeper gains a flag-mode toggle so you can plant flags with a thumb, since flagging was right-click-only and unreachable on a phone",[2186],{"path":2187,"add":731,"del":39},"app\u002Fcomponents\u002Fdesktop\u002FDesktopMinesweeper.vue",{"hash":2189,"date":2093,"subject":2190,"files":2191,"plus":64,"minus":11},"d2a832e","the minesweeper expert board shrinks its cells to fit a phone window instead of running its 22 columns off the right edge with no way to scroll to them",[2192],{"path":2187,"add":64,"del":11},{"hash":2194,"date":2093,"subject":2195,"files":2196,"plus":72,"minus":39},"fefb5c3","the readme documents the mobile support with a 375px lvos screenshot and a 'works on a phone' rundown of the bottom nav, sheet terminal, thumb-playable games and fitted desktop",[2197,2198],{"path":10,"add":72,"del":39},{"path":89,"add":15,"del":15},{"hash":2200,"date":2093,"subject":2201,"files":2202,"plus":39,"minus":40},"e2a07ba","the museum catches up with round 30: a 'built for a thumb' exhibit for the mobile desktop work, and the screensaver\u002Fnight-light plaque notes the new terminal way in",[2203],{"path":103,"add":39,"del":40},{"hash":2205,"date":2093,"subject":2206,"files":2207,"plus":900,"minus":82},"6b3eaca","a long-press on the lvos desktop or an icon opens the context menu on touch, since the right-click menu was mouse-only and unreachable on a phone",[2208],{"path":284,"add":900,"del":82},{"hash":2210,"date":2093,"subject":2211,"files":2212,"plus":75,"minus":15},"b367595","beating your minesweeper best time now sets off the shared high-score confetti burst like every other game, instead of quietly writing its own key with no celebration",[2213,2214],{"path":2187,"add":28,"del":15},{"path":2215,"add":43,"del":15},"app\u002Futils\u002FterminalGameKit.ts",{"hash":2217,"date":2093,"subject":2218,"files":2219,"plus":223,"minus":64},"54f90bb","the visitor globe and the status-bar badge read one shared online count, and live geo clears on disconnect, so the globe can't show '1 online' or stale visitors while the badge has dropped to 0",[2220,2222,2223],{"path":2221,"add":72,"del":40},"app\u002Fcomponents\u002FLiveCursors.vue",{"path":231,"add":40,"del":40},{"path":2224,"add":72,"del":40},"app\u002Fcomposables\u002FuseVisitorGlobe.ts",{"hash":2226,"date":2093,"subject":2227,"files":2228,"plus":773,"minus":351},"e7a8033","the terminal weather command shares one wmo code-to-condition map with the app and tray via weatherCategory + weatherLabel, so code 1 and 48 no longer read differently in the terminal than on the desktop",[2229,2231,2232],{"path":2230,"add":182,"del":24},"app\u002Futils\u002Fterminal\u002Fweather.ts",{"path":1041,"add":448,"del":15},{"path":2233,"add":278,"del":545},"tests\u002Fweather.test.ts",{"hash":2235,"date":2093,"subject":2236,"files":2237,"plus":448,"minus":82},"7cbd104","the pixel world refcounts its relay lease per consumer, so a terminal `pixel place` or a \u002Fstatus visit no longer leaves an orphaned lease that a later worldcanvas unmount tears the shared socket down through",[2238],{"path":2239,"add":448,"del":82},"app\u002Fcomposables\u002FuseWorld.ts",{"hash":2241,"date":2093,"subject":2242,"files":2243,"plus":509,"minus":255},"52e7193","terminal commands run one at a time through a queue, so an async command still in flight (curl | jq, a redirect) can't have its capture sink hijacked by the next command and dump raw output or write an empty file",[2244],{"path":115,"add":509,"del":255},{"hash":2246,"date":2093,"subject":2247,"files":2248,"plus":255,"minus":15},"1aeb08e","a terminal `screensaver` command picks the idle saver from the cli, driving the same shared choice the Settings and bios pickers do",[2249],{"path":36,"add":255,"del":15},{"hash":2251,"date":2093,"subject":2252,"files":2253,"plus":1141,"minus":15},"f6dd6c5","a terminal `nightlight` command toggles the warm night-light wash, matching crt\u002Fparty\u002Fmatrix which all have a cli into their shared effect state",[2254],{"path":36,"add":1141,"del":15},{"hash":2256,"date":2093,"subject":2257,"files":2258,"plus":147,"minus":15},"5145148","wallpaper joins the rest of the appearance cluster with a terminal `wallpaper` command and a Settings row, so it's no longer only reachable from the start menu, bios and paint",[2259,2260],{"path":852,"add":46,"del":15},{"path":36,"add":197,"del":15},{"hash":2262,"date":2093,"subject":2263,"files":2264,"plus":61,"minus":11},"a183dfb","the lvos snake app gains a touch d-pad and caps its board width, so it's finally playable by thumb instead of keyboard-only and overflowing the window",[2265],{"path":974,"add":61,"del":11},{"hash":2267,"date":2093,"subject":2268,"files":2269,"plus":64,"minus":15},"c407372","the pixel world frame is a touch shorter on phones so its palette and coordinate row clear the fixed mobile tab bar instead of hiding beneath it",[2270],{"path":1735,"add":64,"del":15},{"hash":2272,"date":2093,"subject":2273,"files":2274,"plus":164,"minus":39},"fffe00d","the lvos desktop icons stay reachable on a phone: the grid is bounded on the right and flows into vertically-scrolling rows on touch instead of wrapping columns off the clipped edge",[2275],{"path":746,"add":164,"del":39},{"hash":2277,"date":2093,"subject":2278,"files":2279,"plus":43,"minus":11},"4352a37","the visitor globe no longer clips its own left edge on a phone: the window caps at full width and the ascii globe scrolls inside its box instead of centring itself off-screen",[2280],{"path":231,"add":43,"del":11},{"hash":2282,"date":2093,"subject":2283,"files":2284,"plus":39,"minus":39},"026d634","the task manager, hall-of-fame and rss windows cap their min-width at 100% so they stop forcing an inner horizontal scrollbar inside a phone-width window",[2285,2287,2288],{"path":2286,"add":11,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopRss.vue",{"path":1555,"add":11,"del":11},{"path":2289,"add":11,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopTaskManager.vue",{"hash":2291,"date":2093,"subject":2292,"files":2293,"plus":448,"minus":15},"a1dedf4","the taskbar notification and calendar popovers get a z-index and clear the touch taskbar, so tapping the bell or clock over a maximized window actually shows them on a phone",[2294,2295],{"path":1573,"add":43,"del":15},{"path":2296,"add":43,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopNotifCenter.vue",{"hash":2298,"date":2093,"subject":2299,"files":2300,"plus":61,"minus":46},"b962435","the lvos taskbar keeps its tray reachable on a phone: the open-window buttons scroll as a group on touch instead of shoving the clock, bell and fullscreen toggle off the right edge",[2301],{"path":271,"add":61,"del":46},{"hash":2303,"date":2093,"subject":2304,"files":2305,"plus":461,"minus":39},"f096c6f","lvos windows open reachable on phones: new windows maximize on narrow viewports and the spawn cascade keeps a floating window's right edge and close button on-screen",[2306,2307,2309],{"path":288,"add":28,"del":11},{"path":2308,"add":82,"del":40},"app\u002Futils\u002FwindowOrder.ts",{"path":2310,"add":164,"del":15},"tests\u002FwindowOrder.test.ts",{"hash":2312,"date":2093,"subject":2313,"files":2314,"plus":1141,"minus":39},"5c4b1f0","the destroyer scroll e2e waits for the ship to really point down before thrusting, so a slow ci runner can't under-rotate it into a false failure",[2315,2316],{"path":1746,"add":28,"del":15},{"path":431,"add":263,"del":39},{"hash":2318,"date":2093,"subject":2319,"files":2320,"plus":72,"minus":28},"ef9b99e","tidy lint\u002Fknip: un-export the internal adventure Exit type, drop unsafe spreads in jq, and a stray disable",[2321,2322,2324,2326],{"path":1242,"add":11,"del":11},{"path":2323,"add":11,"del":11},"app\u002Futils\u002Fgames\u002Fadventure\u002Ftypes.ts",{"path":2325,"add":40,"del":40},"app\u002Futils\u002Fterminal\u002Fjq.ts",{"path":2090,"add":15,"del":11},{"hash":2328,"date":2093,"subject":2329,"files":2330,"plus":223,"minus":64},"662259d","museum and readme catch up with round 29: jq, the weather and colour-picker apps, the high-score burst and the \u002Fstatus page",[2331,2332],{"path":10,"add":39,"del":39},{"path":103,"add":43,"del":39},{"hash":2334,"date":2093,"subject":2335,"files":2336,"plus":39,"minus":40},"5089e32","refresh \u002Fnow and \u002Fuses: the site's multiplayer layer and its bulma theming get a mention",[2337,2338],{"path":505,"add":40,"del":40},{"path":479,"add":11,"del":15},{"hash":2340,"date":2093,"subject":2341,"files":2342,"plus":263,"minus":11},"91dd5b8","the blog heading copy-link affordance stays tappable on touch, where there's no hover to reveal it",[2343],{"path":564,"add":263,"del":11},{"hash":2345,"date":2093,"subject":2346,"files":2347,"plus":731,"minus":39},"f615525","homepage section headings decode into place with a scramble the first time they scroll into view",[2348,2350,2351,2352],{"path":2349,"add":11,"del":11},"app\u002Fcomponents\u002FHomeLatestPosts.vue",{"path":525,"add":11,"del":11},{"path":882,"add":200,"del":15},{"path":2353,"add":11,"del":11},"app\u002Fpages\u002Findex.vue",{"hash":2355,"date":2093,"subject":2356,"files":2357,"plus":2368,"minus":39},"e71cc1b","a confetti burst rewards beating a game's personal best, reduced-motion safe",[2358,2360,2361,2362,2363,2364,2365,2366],{"path":1663,"add":2359,"del":15},102,{"path":1669,"add":39,"del":11},{"path":1671,"add":40,"del":11},{"path":1673,"add":40,"del":11},{"path":1563,"add":164,"del":15},{"path":823,"add":11,"del":15},{"path":2215,"add":82,"del":15},{"path":2367,"add":147,"del":15},"tests\u002FcelebrateSink.test.ts",169,{"hash":2370,"date":2093,"subject":2371,"files":2372,"plus":2378,"minus":40},"468f91b","a \u002Fstatus page reports the multiplayer relay's health: who's online, pixel-board fill, and each feature's availability",[2373,2375,2376],{"path":666,"add":2374,"del":15},252,{"path":149,"add":11,"del":11},{"path":2377,"add":40,"del":11},"server\u002Froutes\u002Fsitemap.xml.ts",255,{"hash":2380,"date":2093,"subject":2381,"files":2382,"plus":2393,"minus":2385},"7062796","split the adventure engine into room data, engine and game-handle modules behind a facade",[2383,2386,2388,2390,2392],{"path":2384,"add":164,"del":2385},"app\u002Futils\u002Fgames\u002Fadventure.ts",524,{"path":517,"add":2387,"del":15},259,{"path":2023,"add":2389,"del":15},114,{"path":491,"add":2391,"del":15},130,{"path":2323,"add":25,"del":15},547,{"hash":2395,"date":2093,"subject":2396,"files":2397,"plus":1479,"minus":1675},"f015db4","online pong, chess and wpm-race share one relay-duel helper for the socket lease, lobby spinner, leave frame and win tally",[2398,2400,2402,2403],{"path":2399,"add":302,"del":185},"app\u002Fcomposables\u002FuseChessOnline.ts",{"path":2401,"add":302,"del":187},"app\u002Futils\u002Fgames\u002FpongOnline.ts",{"path":2117,"add":667,"del":15},{"path":2404,"add":46,"del":1154},"app\u002Futils\u002Fgames\u002FwpmRace.ts",{"hash":2406,"date":2093,"subject":2407,"files":2408,"plus":2421,"minus":64},"7e9345a","lvos colour picker: eyedropper sampling, hex\u002Frgb\u002Fhsl readouts, and push any colour to the live site accent",[2409,2410,2412,2414,2416,2417,2418,2419],{"path":284,"add":40,"del":11},{"path":1242,"add":2411,"del":15},214,{"path":2413,"add":182,"del":40},"app\u002Fcomposables\u002FuseAccent.ts",{"path":2415,"add":1496,"del":15},"app\u002Futils\u002Fcolor.ts",{"path":679,"add":11,"del":15},{"path":2325,"add":40,"del":40},{"path":36,"add":11,"del":11},{"path":2420,"add":425,"del":15},"tests\u002Fcolor.test.ts",315,{"hash":2423,"date":2093,"subject":2424,"files":2425,"plus":2432,"minus":448},"8ed706f","lvos gains a weather app: a five-day amsterdam forecast over the same open-meteo source as the tray chip",[2426,2427,2429,2430,2431],{"path":284,"add":40,"del":11},{"path":1319,"add":2428,"del":15},176,{"path":1037,"add":72,"del":163},{"path":679,"add":11,"del":15},{"path":1041,"add":25,"del":15},218,{"hash":2434,"date":2093,"subject":2435,"files":2436,"plus":2448,"minus":106},"88873fb","jq: query the site's own json (resume, profile, projects) with a real subset, as a command and a pipe filter",[2437,2439,2441,2442,2444,2446],{"path":2438,"add":506,"del":15},"app\u002Futils\u002Fresume.ts",{"path":2325,"add":2440,"del":15},190,{"path":36,"add":61,"del":15},{"path":2443,"add":263,"del":11},"app\u002Futils\u002Fterminal\u002Fpipeline.ts",{"path":2445,"add":72,"del":1519},"server\u002Froutes\u002Fresume.json.ts",{"path":2447,"add":289,"del":15},"tests\u002Fjq.test.ts",346,{"hash":2450,"date":2093,"subject":2451,"files":2452,"plus":448,"minus":64},"dcd408a","the pixel world stops caching a stale online\u002Foffline hybrid on a relay drop, and fits its window on narrow phones",[2453,2454],{"path":284,"add":39,"del":11},{"path":2239,"add":263,"del":28},{"hash":2456,"date":2093,"subject":2457,"files":2458,"plus":120,"minus":11},"fd7143e","the relay rejects a wpm-race finish that arrives faster than any human could type, killing the instant-win exploit",[2459,2460],{"path":129,"add":75,"del":11},{"path":136,"add":64,"del":15},{"hash":2462,"date":2093,"subject":2463,"files":2464,"plus":75,"minus":43},"3ed430f","light theme: the terminal reverse-search hint and the chess\u002Fcamera buttons use scheme colours so they read on the always-dark surfaces",[2465,2466,2467],{"path":222,"add":39,"del":11},{"path":1278,"add":72,"del":39},{"path":986,"add":28,"del":39},{"hash":2469,"date":2093,"subject":2470,"files":2471,"plus":448,"minus":82},"f8fbe69","chat heals through a network drop like the other relay rooms, and keeps your message when a send can't go out",[2472,2473,2475],{"path":1326,"add":40,"del":40},{"path":2474,"add":223,"del":72},"app\u002Fcomposables\u002FuseChat.ts",{"path":2476,"add":40,"del":40},"app\u002Futils\u002Fgames\u002FchatRoom.ts",{"hash":2478,"date":2093,"subject":2479,"files":2480,"plus":72,"minus":2499},"3296dc9","retire the til micro-blog: drop the page, feed, collection, footer link, sitemap and og card",[2481,2482,2483,2486,2488,2490,2492,2494,2495,2496,2497],{"path":10,"add":11,"del":11},{"path":1852,"add":15,"del":40},{"path":2484,"add":15,"del":2485},"app\u002Fpages\u002Ftil.vue",135,{"path":2487,"add":15,"del":263},"content.config.ts",{"path":2489,"add":15,"del":164},"content\u002Ftil\u002F2026-07-10-vitest-no-typecheck.md",{"path":2491,"add":15,"del":164},"content\u002Ftil\u002F2026-07-11-slice-negative-zero.md",{"path":2493,"add":15,"del":263},"content\u002Ftil\u002F2026-07-12-detached-effect-scope.md",{"path":149,"add":11,"del":11},{"path":631,"add":11,"del":40},{"path":2377,"add":11,"del":40},{"path":2498,"add":15,"del":106},"server\u002Froutes\u002Ftil.xml.ts",221,{"hash":2501,"date":2093,"subject":2502,"files":2503,"plus":11,"minus":204},"bf4c84e","drop the author-declared proficiency bars from \u002Fabout; the npm-ls skill tree stands on its own",[2504,2506],{"path":501,"add":11,"del":2505},65,{"path":508,"add":15,"del":164},{"hash":2508,"date":2093,"subject":2509,"files":2510,"plus":72,"minus":72},"a2cebf1","the homepage latest-writing block now lists the three most recent posts",[2511],{"path":2349,"add":72,"del":72},{"hash":2513,"date":2093,"subject":2514,"files":2515,"plus":39,"minus":11},"7d0f3aa","the topbar brand gets a touch more presence: larger prompt glyph and text than the nav links",[2516],{"path":248,"add":39,"del":11},{"hash":2518,"date":2093,"subject":2519,"files":2520,"plus":82,"minus":39},"266b57c","lvos shutdown stays collapsed through the route change, so the desktop no longer flashes back before the site returns",[2521],{"path":2522,"add":82,"del":39},"app\u002Fcomposables\u002FuseDesktopPower.ts",{"hash":2524,"date":2093,"subject":2525,"files":2526,"plus":263,"minus":28},"65e095f","the walkable museum floor uses ascii glyphs so jetbrains mono keeps the grid aligned",[2527,2528,2529],{"path":1308,"add":11,"del":11},{"path":2088,"add":39,"del":39},{"path":2090,"add":43,"del":11},{"hash":2531,"date":2093,"subject":2532,"files":2533,"plus":1141,"minus":255},"d085bde","whats-new reads the updater's marker without pre-seeding it, so the update screen keeps its backlog",[2534,2535],{"path":2130,"add":448,"del":302},{"path":162,"add":40,"del":11},{"hash":2537,"date":2093,"subject":2538,"files":2539,"plus":187,"minus":46},"fdb5efb","museum and readme catch up with rounds 24-28: chat, online games, globe, pet economy, updates and more",[2540,2541,2542],{"path":10,"add":263,"del":82},{"path":103,"add":46,"del":39},{"path":2088,"add":163,"del":64},{"hash":2544,"date":2093,"subject":2545,"files":2546,"plus":2559,"minus":11},"ce446d1","found a bug? a terminal command and footer link open a prefilled github issue with page, viewport and version",[2547,2549,2551,2553,2554,2556,2557],{"path":2548,"add":545,"del":15},".github\u002FISSUE_TEMPLATE\u002Fbug_report.md",{"path":2550,"add":448,"del":15},".github\u002FISSUE_TEMPLATE\u002Ffeature_request.md",{"path":2552,"add":263,"del":15},".github\u002FPULL_REQUEST_TEMPLATE.md",{"path":1852,"add":444,"del":11},{"path":2555,"add":1498,"del":15},"app\u002Futils\u002FbugReport.ts",{"path":36,"add":120,"del":15},{"path":2558,"add":25,"del":15},"tests\u002FbugReport.test.ts",178,{"hash":2561,"date":2093,"subject":2562,"files":2563,"plus":2570,"minus":425},"9be20f7","destroy mode's target-picking, debris and edge-scroll math move to a tested util",[2564,2565,2567],{"path":1746,"add":263,"del":425},{"path":2566,"add":1107,"del":15},"app\u002Futils\u002Fdestroyer.ts",{"path":2568,"add":2569,"del":15},"tests\u002Fdestroyer.test.ts",89,196,{"hash":2572,"date":2093,"subject":2573,"files":2574,"plus":2584,"minus":2585},"6fb31ef","split the two biggest pages: about and index shed their sections into focused components",[2575,2576,2578,2579,2580,2582],{"path":501,"add":32,"del":15},{"path":2577,"add":828,"del":15},"app\u002Fcomponents\u002FAboutTimeline.vue",{"path":2349,"add":968,"del":15},{"path":525,"add":2485,"del":15},{"path":484,"add":40,"del":2581},383,{"path":2353,"add":40,"del":2583},236,662,619,{"hash":2587,"date":2093,"subject":2588,"files":2589,"plus":40,"minus":15},"143c93c","ci runs the live relay integration suite, so relay regressions can't slip into a deploy",[2590],{"path":54,"add":40,"del":15},{"hash":2592,"date":2093,"subject":2593,"files":2594,"plus":2610,"minus":164,"truncated":11},"aa5465f","a spinning ASCII earth plots live visitors by timezone: globe command and lvOS app, one shared crowd",[2595,2596,2597,2598,2600,2601,2602,2604,2606,2607,2608,2609],{"path":2221,"add":75,"del":39},{"path":284,"add":40,"del":11},{"path":231,"add":2505,"del":15},{"path":2599,"add":223,"del":11},"app\u002Fcomposables\u002FuseLiveVisitors.ts",{"path":2224,"add":444,"del":15},{"path":679,"add":11,"del":15},{"path":2603,"add":1119,"del":15},"app\u002Futils\u002Fgames\u002Fglobe.ts",{"path":2605,"add":20,"del":15},"app\u002Futils\u002Fglobe.ts",{"path":823,"add":11,"del":15},{"path":1342,"add":163,"del":15},{"path":129,"add":64,"del":11},{"path":131,"add":72,"del":39},359,{"hash":2612,"date":2093,"subject":2613,"files":2614,"plus":2627,"minus":448},"42d0bb7","wpm race: type against a live visitor, the relay holds the stopwatch and the finish line",[2615,2616,2617,2618,2619,2620,2621,2623,2624,2626],{"path":1555,"add":40,"del":11},{"path":1072,"add":11,"del":11},{"path":2029,"add":72,"del":82},{"path":2404,"add":489,"del":15},{"path":1342,"add":448,"del":40},{"path":431,"add":163,"del":15},{"path":129,"add":2622,"del":15},95,{"path":131,"add":255,"del":11},{"path":2625,"add":185,"del":15},"realtime\u002Frace-core.mjs",{"path":136,"add":140,"del":11},400,{"hash":2629,"date":2093,"subject":2630,"files":2631,"plus":2385,"minus":2643},"dba4b1d","one relay socket core: six hand-rolled websockets become leases on a tested connection module",[2632,2633,2634,2635,2636,2637,2638,2640],{"path":2221,"add":106,"del":216},{"path":2474,"add":185,"del":140},{"path":2399,"add":140,"del":289},{"path":1667,"add":1154,"del":1498},{"path":2239,"add":216,"del":14},{"path":2401,"add":187,"del":203},{"path":2639,"add":947,"del":15},"app\u002Futils\u002FrelaySocket.ts",{"path":2641,"add":2642,"del":15},"tests\u002FrelaySocket.test.ts",138,352,{"hash":2645,"date":2093,"subject":2646,"files":2647,"plus":352,"minus":15},"c8b1bf7","the connection speaks up: offline dot in the status bar, toasts on both surfaces when the network drops",[2648,2649,2650,2651],{"path":880,"add":43,"del":15},{"path":1703,"add":2505,"del":15},{"path":284,"add":43,"del":15},{"path":1671,"add":11,"del":15},{"hash":2653,"date":2093,"subject":2654,"files":2655,"plus":203,"minus":40},"1bfff11","arrow keys walk the desktop icons like a real desktop, measured against the wrapped grid",[2656],{"path":746,"add":203,"del":40},{"hash":2658,"date":2093,"subject":2659,"files":2660,"plus":106,"minus":40},"31e807e","the hero card demos itself when you idle: types a command, thinks better of it, tries another",[2661],{"path":742,"add":106,"del":40},{"hash":2663,"date":2093,"subject":2664,"files":2665,"plus":2669,"minus":15},"4790015","a bottom tab bar for thumbs: home, projects, blog and the terminal within reach on phones",[2666,2668],{"path":1856,"add":2667,"del":15},100,{"path":1671,"add":64,"del":15},106,{"hash":2671,"date":2093,"subject":2672,"files":2673,"plus":968,"minus":72},"1e2587c","the mail app grows a compose button: a real mailto draft to the real contact address",[2674],{"path":1297,"add":968,"del":72},{"hash":2676,"date":2093,"subject":2677,"files":2678,"plus":2683,"minus":223},"cc64704","music command: the terminal djs the same chiptune jukebox the media app plays, three tracks now",[2679,2680,2682],{"path":2175,"add":72,"del":40},{"path":2681,"add":425,"del":82},"app\u002Fcomposables\u002FuseChiptune.ts",{"path":1039,"add":106,"del":15},73,{"hash":2685,"date":2093,"subject":2686,"files":2687,"plus":1266,"minus":39},"4f8b66d","settings get mirrors and the screensaver gets a pid: volume command, night light and sound in settings, saver in bios and ps",[2688,2689,2690,2691,2692,2693,2694],{"path":284,"add":28,"del":15},{"path":2014,"add":263,"del":40},{"path":852,"add":506,"del":15},{"path":113,"add":40,"del":15},{"path":95,"add":46,"del":15},{"path":36,"add":1519,"del":15},{"path":305,"add":448,"del":11},{"hash":2696,"date":2093,"subject":2697,"files":2698,"plus":61,"minus":106},"3e8fa19","one truth for every 'new' badge: rss and mail share the post read-set, whats-new reads the updater's marker",[2699,2700,2701,2702],{"path":2130,"add":171,"del":163},{"path":2286,"add":11,"del":40},{"path":1759,"add":43,"del":40},{"path":2703,"add":1141,"del":171},"app\u002Fcomposables\u002FuseLvosRss.ts",{"hash":2705,"date":2093,"subject":2706,"files":2707,"plus":203,"minus":263},"0c9d8ba","pong joins the shared score world: hall of fame, pet coins, leaderboard; online wins finally count",[2708,2709,2710,2711,2713,2714,2715,2716,2717],{"path":1555,"add":43,"del":11},{"path":2399,"add":39,"del":15},{"path":1072,"add":28,"del":11},{"path":2712,"add":28,"del":40},"app\u002Futils\u002Fgames\u002Fpong.ts",{"path":2401,"add":39,"del":11},{"path":1080,"add":64,"del":15},{"path":2215,"add":46,"del":11},{"path":1082,"add":39,"del":40},{"path":1087,"add":39,"del":39},{"hash":2719,"date":2093,"subject":2720,"files":2721,"plus":163,"minus":163},"04af0e6","copy sweep: 24h clock everywhere, uniform mail dates, honest museum counts, shell-style errors",[2722,2724,2726,2728,2729],{"path":2723,"add":11,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopDisplays.vue",{"path":2725,"add":28,"del":28},"app\u002Fcomposables\u002FuseLvosMail.ts",{"path":2727,"add":11,"del":11},"app\u002Fcomposables\u002FuseStatusBarEggs.ts",{"path":103,"add":40,"del":40},{"path":34,"add":72,"del":72},{"hash":2731,"date":2093,"subject":2732,"files":2733,"plus":164,"minus":72},"7ab58b2","the terminal mail test learns the inbox now holds ten messages",[2734],{"path":431,"add":164,"del":72},{"hash":2736,"date":2093,"subject":2737,"files":2738,"plus":72,"minus":15},"c623c74","readme shows the goods: terminal, lvos and pixel-world screenshots",[2739,2740,2742,2744],{"path":10,"add":72,"del":15},{"path":2741,"add":15,"del":15},"docs\u002Fscreenshots\u002Flvos.png",{"path":2743,"add":15,"del":15},"docs\u002Fscreenshots\u002Fterminal.png",{"path":2745,"add":15,"del":15},"docs\u002Fscreenshots\u002Fworld.png",{"hash":2747,"date":2093,"subject":2748,"files":2749,"plus":2763,"minus":11},"9f52760","a chat room over the relay: terminal chat and the lvos chat app share one #lounge",[2750,2751,2753,2754,2755,2756,2757,2758,2759,2760,2761,2762],{"path":284,"add":11,"del":15},{"path":1326,"add":2752,"del":15},127,{"path":2474,"add":1481,"del":15},{"path":679,"add":11,"del":15},{"path":2476,"add":351,"del":15},{"path":1005,"add":11,"del":15},{"path":823,"add":72,"del":15},{"path":38,"add":120,"del":15},{"path":162,"add":1141,"del":15},{"path":129,"add":506,"del":15},{"path":131,"add":1141,"del":11},{"path":136,"add":731,"del":15},413,{"hash":2765,"date":2093,"subject":2766,"files":2767,"plus":2771,"minus":15},"da954a1","test:relay boots the relay and drills world, scores, pong and chess with real clients",[2768,2769],{"path":63,"add":11,"del":15},{"path":136,"add":2770,"del":15},234,235,{"hash":2773,"date":2093,"subject":2774,"files":2775,"plus":2788,"minus":2770},"dafcfc7","chess online: challenge a live visitor, the relay referees with the same shared rules",[2776,2778,2779,2781,2782,2783,2786,2787],{"path":986,"add":2777,"del":64},79,{"path":2399,"add":1633,"del":15},{"path":2780,"add":163,"del":1489},"app\u002Futils\u002Fchess.ts",{"path":1342,"add":302,"del":15},{"path":162,"add":1141,"del":15},{"path":2784,"add":2785,"del":15},"realtime\u002Fchess-core.mjs",249,{"path":129,"add":1491,"del":15},{"path":131,"add":278,"del":11},599,{"hash":2790,"date":2093,"subject":2791,"files":2792,"plus":2798,"minus":75},"f7383a7","pet economy: game high scores mint coins, the shop sells snacks and wearable gear",[2793,2794,2795,2796,2797],{"path":1072,"add":1119,"del":28},{"path":1080,"add":1120,"del":15},{"path":823,"add":11,"del":15},{"path":1039,"add":545,"del":64},{"path":1087,"add":572,"del":11},187,{"hash":2800,"date":2093,"subject":2801,"files":2802,"plus":2807,"minus":15},"5e5401a","system update screen ceremonially installs the repo's real commits, nudges when new ones ship",[2803,2804,2805,2806],{"path":284,"add":197,"del":15},{"path":271,"add":40,"del":15},{"path":1759,"add":186,"del":15},{"path":162,"add":182,"del":15},237,{"hash":2809,"date":2093,"subject":2810,"files":2811,"plus":2817,"minus":444},"7bf4e7e","real blog posts land in the lvos inbox as newsletter mails, badge finally tells the truth",[2812,2813,2814,2815,2816],{"path":284,"add":11,"del":11},{"path":1297,"add":444,"del":82},{"path":2725,"add":506,"del":72},{"path":36,"add":28,"del":72},{"path":162,"add":75,"del":64},81,{"hash":2819,"date":2093,"subject":2820,"files":2821,"plus":2832,"minus":106},"6f7a32a","screensaver gallery: flying toasters and mystify join the starfield, picked in settings",[2822,2823,2824,2826,2828,2829,2830],{"path":235,"add":302,"del":1519},{"path":852,"add":75,"del":15},{"path":2825,"add":255,"del":15},"app\u002Fcomposables\u002FuseScreensaverChoice.ts",{"path":2827,"add":2428,"del":15},"app\u002Futils\u002Fscreensavers.ts",{"path":823,"add":40,"del":11},{"path":162,"add":75,"del":15},{"path":2831,"add":2505,"del":15},"tests\u002Fscreensavers.test.ts",306,{"hash":2834,"date":2093,"subject":2835,"files":2836,"plus":529,"minus":28},"70786dd","volume tray in the taskbar: one persisted volume the chiptune engine actually obeys",[2837,2838,2839,2840,2842,2843,2844,2845],{"path":284,"add":39,"del":15},{"path":271,"add":940,"del":40},{"path":2681,"add":444,"del":11},{"path":2841,"add":40,"del":15},"app\u002Fcomposables\u002FuseDesktopShortcuts.ts",{"path":1992,"add":164,"del":11},{"path":1939,"add":572,"del":15},{"path":823,"add":39,"del":11},{"path":162,"add":448,"del":15},{"hash":2847,"date":2093,"subject":2848,"files":2849,"plus":46,"minus":15},"a015689","text-wrap: balanced headings and pretty prose where the browser can",[2850],{"path":418,"add":46,"del":15},{"hash":2852,"date":2093,"subject":2853,"files":2854,"plus":290,"minus":2859},"fb8aa73","navbar brand typing and taskbar popovers move to composables, crt egg reuses the click armer",[2855,2856,2857,2858],{"path":248,"add":72,"del":506},{"path":271,"add":72,"del":509},{"path":1761,"add":185,"del":15},{"path":1992,"add":107,"del":15},72,{"hash":2861,"date":2093,"subject":2862,"files":2863,"plus":75,"minus":11},"942805a","strictTemplates: template prop and event bindings are type-checked now",[2864,2866,2867],{"path":2865,"add":39,"del":15},"app\u002Fcomponents\u002FCmdButton.vue",{"path":1877,"add":39,"del":11},{"path":149,"add":64,"del":15},{"hash":2869,"date":2093,"subject":2870,"files":2871,"plus":289,"minus":107},"7f4a22c","one lv-glass mixin defines the frosted look for windows, taskbar, menus and tooltips",[2872,2874,2875,2877,2878,2879],{"path":2873,"add":1154,"del":15},"app\u002Fassets\u002Fscss\u002F_mixins.scss",{"path":284,"add":72,"del":28},{"path":2876,"add":72,"del":28},"app\u002Fcomponents\u002Fdesktop\u002FDesktopContextMenu.vue",{"path":271,"add":223,"del":200},{"path":286,"add":72,"del":43},{"path":919,"add":15,"del":15},{"hash":2881,"date":2093,"subject":2882,"files":2883,"plus":255,"minus":82},"a1167de","small screens stop overflowing: mail min-width, keyboard grid, shrinking chess board, files name ellipsis",[2884,2885,2886,2887],{"path":986,"add":43,"del":28},{"path":170,"add":223,"del":11},{"path":1297,"add":40,"del":11},{"path":658,"add":11,"del":11},{"hash":2889,"date":2093,"subject":2890,"files":2891,"plus":43,"minus":43},"526e649","desktop app text stays readable in light theme: always-dark windows get always-light text",[2892,2893,2894,2895,2897],{"path":1278,"add":11,"del":11},{"path":986,"add":11,"del":11},{"path":2723,"add":39,"del":39},{"path":2896,"add":11,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopShortcuts.vue",{"path":237,"add":11,"del":11},{"hash":2899,"date":2093,"subject":2900,"files":2901,"plus":40,"minus":40},"3a98fad","lock screen and screensaver actually cover the taskbar and pinned windows",[2902,2903],{"path":374,"add":11,"del":11},{"path":235,"add":11,"del":11},{"hash":2905,"date":2093,"subject":2906,"files":2907,"plus":1141,"minus":72},"05d871b","taskbar blur moves to a pseudo-element so the start menu keeps outranking windows",[2908,2909],{"path":271,"add":263,"del":40},{"path":447,"add":28,"del":40},{"hash":2911,"date":2093,"subject":2912,"files":2913,"plus":28,"minus":72},"6aafde7","knip housekeeping: un-export internals, ignore the protocol contract file",[2914,2915,2916,2917],{"path":95,"add":11,"del":11},{"path":179,"add":11,"del":11},{"path":2443,"add":11,"del":11},{"path":2918,"add":40,"del":11},"knip.json",{"hash":2920,"date":2093,"subject":2921,"files":2922,"plus":2935,"minus":39},"90377d3","pong online: challenge another live visitor over the relay, server owns the ball",[2923,2925,2926,2928,2929,2930,2932,2933],{"path":2401,"add":2924,"del":15},157,{"path":1342,"add":255,"del":40},{"path":2927,"add":28,"del":15},"docs\u002Fworld-server.md",{"path":431,"add":163,"del":15},{"path":129,"add":174,"del":15},{"path":2931,"add":1105,"del":15},"realtime\u002Fpong-core.mjs",{"path":131,"add":444,"del":11},{"path":2934,"add":2505,"del":15},"tests\u002FpongCore.test.ts",487,{"hash":2937,"date":2093,"subject":2938,"files":2939,"plus":2944,"minus":448},"3d241d6","press DEL during POST for a BIOS setup that edits the real wallpaper, night light, theme and quiet boot",[2940,2942,2943],{"path":2014,"add":2941,"del":15},201,{"path":771,"add":61,"del":448},{"path":162,"add":461,"del":15},268,{"hash":2946,"date":2093,"subject":2947,"files":2948,"plus":2505,"minus":64},"4e998a7","destroy mode works by thumb: v2.0.0 trigger visible on mobile, touch hint and a hold-to-fire button",[2949,2950],{"path":880,"add":40,"del":11},{"path":1746,"add":1477,"del":28},{"hash":2952,"date":2093,"subject":2953,"files":2954,"plus":1154,"minus":15},"fe89548","frosted-glass lvos chrome: windows, taskbar, start menu and popovers blur what's behind them",[2955,2956,2957,2958],{"path":284,"add":28,"del":15},{"path":2876,"add":28,"del":15},{"path":271,"add":46,"del":15},{"path":286,"add":43,"del":15},{"hash":2960,"date":2093,"subject":2961,"files":2962,"plus":2965,"minus":15},"d0f1694","the terminal becomes a bottom sheet on phones with a quick-key row for tab, history and game controls",[2963,2964],{"path":222,"add":216,"del":15},{"path":363,"add":46,"del":15},78,{"hash":2967,"date":2093,"subject":2968,"files":2969,"plus":1496,"minus":15},"0471ce4","lvos grows thumb-sized close buttons, start button and tray targets on touch screens",[2970,2971],{"path":271,"add":444,"del":15},{"path":286,"add":545,"del":15},{"hash":2973,"date":2093,"subject":2974,"files":2975,"plus":2986,"minus":15},"c87853c","lvos chess app with a pure tested engine and a shallow alpha-beta house ai",[2976,2977,2979,2981,2982,2983,2984],{"path":284,"add":11,"del":15},{"path":986,"add":2978,"del":15},219,{"path":2780,"add":2980,"del":15},293,{"path":679,"add":11,"del":15},{"path":1005,"add":11,"del":15},{"path":162,"add":120,"del":15},{"path":2985,"add":2924,"del":15},"tests\u002Fchess.test.ts",690,{"hash":2988,"date":2093,"subject":2989,"files":2990,"plus":2559,"minus":15},"1306bb4","lvos camera app: the asciicam engine in a window, with snap-to-gallery",[2991,2992,2994,2995,2996],{"path":284,"add":11,"del":15},{"path":1278,"add":2993,"del":15},156,{"path":679,"add":11,"del":15},{"path":1005,"add":11,"del":15},{"path":162,"add":302,"del":15},{"hash":2998,"date":2093,"subject":2999,"files":3000,"plus":24,"minus":15},"d019f32","a mail command reads the same inbox as the lvos mail app",[3001,3002],{"path":36,"add":492,"del":15},{"path":431,"add":448,"del":15},{"hash":3004,"date":2093,"subject":3005,"files":3006,"plus":817,"minus":134},"6e08cdc","the blog post page splits into PostToc and four composables plus a tested reading-time util",[3007,3009,3011,3012,3014,3016,3017,3019],{"path":3008,"add":2683,"del":15},"app\u002Fcomponents\u002FPostToc.vue",{"path":3010,"add":545,"del":15},"app\u002Fcomposables\u002FuseHorizontalSwipe.ts",{"path":1248,"add":2683,"del":15},{"path":3013,"add":750,"del":15},"app\u002Fcomposables\u002FusePostToc.ts",{"path":3015,"add":46,"del":15},"app\u002Fcomposables\u002FuseReadingProgress.ts",{"path":564,"add":163,"del":134},{"path":3018,"add":163,"del":15},"app\u002Futils\u002FreadingTime.ts",{"path":3020,"add":425,"del":15},"tests\u002FreadingTime.test.ts",{"hash":3022,"date":2093,"subject":3023,"files":3024,"plus":3032,"minus":29},"17f089e","one typed wire protocol for the relay, shared by the server and all three clients",[3025,3026,3027,3028,3029,3030],{"path":2221,"add":43,"del":263},{"path":1667,"add":263,"del":82},{"path":2239,"add":197,"del":200},{"path":129,"add":120,"del":75},{"path":131,"add":2859,"del":15},{"path":3031,"add":11,"del":11},"tsconfig.scripts.json",134,{"hash":3034,"date":2093,"subject":3035,"files":3036,"plus":444,"minus":46},"3193f33","game engines, the star wars frames and the editors load lazily on first launch",[3037,3039,3040],{"path":3038,"add":64,"del":39},"app\u002Futils\u002Fterminal\u002FfileWriteCommands.ts",{"path":1342,"add":182,"del":75},{"path":38,"add":40,"del":40},{"hash":3042,"date":2093,"subject":3043,"files":3044,"plus":731,"minus":28},"732dbf2","idle loops stop idling: cooldown raf pauses, pet tick needs a pet, scramble timers die with their scope",[3045,3046,3047],{"path":277,"add":164,"del":40},{"path":1072,"add":448,"del":40},{"path":837,"add":43,"del":11},{"hash":3049,"date":2093,"subject":3050,"files":3051,"plus":175,"minus":82},"a9991e0","network commands time out after 10s and json parsing never masquerades as a network failure",[3052,3053,3054,3055],{"path":2221,"add":43,"del":11},{"path":38,"add":1141,"del":28},{"path":962,"add":40,"del":11},{"path":1039,"add":39,"del":11},{"hash":3057,"date":2093,"subject":3058,"files":3059,"plus":1579,"minus":200},"a5a6a02","terminal persistence wires once per app via persistState and the transcript gets a scrollback cap",[3060,3061],{"path":115,"add":263,"del":40},{"path":1779,"add":1498,"del":197},{"hash":3063,"date":2093,"subject":3064,"files":3065,"plus":1154,"minus":75},"d505ef7","relay clients reconnect with capped backoff and world.leave() guards a connecting socket",[3066,3067,3068],{"path":2221,"add":72,"del":40},{"path":1667,"add":82,"del":11},{"path":2239,"add":461,"del":164},{"hash":3070,"date":2093,"subject":3071,"files":3072,"plus":82,"minus":72},"61b0bca","rm -rf joke blocks whole-site wipes (\u002F, ~, *, \u002F*) but allows rm -rf *.txt",[3073],{"path":3038,"add":82,"del":72},{"hash":3075,"date":2093,"subject":3076,"files":3077,"plus":223,"minus":15},"4a4df5d","e2e: code playground persists across reopen and the leaderboard degrades offline",[3078],{"path":162,"add":223,"del":15},{"hash":3080,"date":2093,"subject":3081,"files":3082,"plus":2499,"minus":72},"7ecff11","add a \u002Ftil micro-blog collection with an index page and its own feed",[3083,3084,3085,3086,3087,3088,3089,3090,3091,3092],{"path":1852,"add":39,"del":11},{"path":2484,"add":2485,"del":15},{"path":2487,"add":263,"del":15},{"path":2489,"add":164,"del":15},{"path":2491,"add":164,"del":15},{"path":2493,"add":263,"del":15},{"path":149,"add":11,"del":11},{"path":631,"add":40,"del":11},{"path":2377,"add":40,"del":11},{"path":2498,"add":106,"del":15},{"hash":3094,"date":2093,"subject":3095,"files":3096,"plus":3102,"minus":11},"ea80891","lvOS gains a live system monitor and a displays panel with night light",[3097,3098,3099,3100,3101],{"path":284,"add":302,"del":11},{"path":2723,"add":464,"del":15},{"path":237,"add":1479,"del":15},{"path":2082,"add":425,"del":15},{"path":679,"add":40,"del":15},361,{"hash":3104,"date":2093,"subject":3105,"files":3106,"plus":2683,"minus":15},"d309a6d","about page gains animated skill proficiency meters that fill on scroll",[3107,3108],{"path":508,"add":164,"del":15},{"path":484,"add":480,"del":15},{"hash":3110,"date":2093,"subject":3111,"files":3112,"plus":3117,"minus":11},"57337b5","asciiquarium: a little ASCII fish tank swims in the terminal",[3113,3115,3116],{"path":3114,"add":2569,"del":15},"app\u002Futils\u002Fgames\u002Fasciiquarium.ts",{"path":1342,"add":164,"del":11},{"path":978,"add":11,"del":15},99,{"hash":3119,"date":2093,"subject":3120,"files":3121,"plus":2683,"minus":132},"53db78d","extract the terminal's persistence and seeding into wireTerminalStorage",[3122,3123],{"path":115,"add":28,"del":132},{"path":1779,"add":809,"del":15},{"hash":3125,"date":2093,"subject":3126,"files":3127,"plus":1481,"minus":545},"2aaf3ee","extract useFileExplorer's drag-move into a tested movePath helper",[3128,3129,3130],{"path":173,"add":43,"del":197},{"path":179,"add":1154,"del":15},{"path":181,"add":1496,"del":11},{"hash":3132,"date":2093,"subject":3133,"files":3134,"plus":205,"minus":425},"cb8d62b","persistState wires storage writers in a detached scope so settings keep saving after a window closes",[3135,3136,3138,3140,3142],{"path":2599,"add":72,"del":64},{"path":3137,"add":43,"del":43},"app\u002Fcomposables\u002FuseTermFontScale.ts",{"path":3139,"add":263,"del":223},"app\u002Fcomposables\u002FuseTrash.ts",{"path":3141,"add":82,"del":82},"app\u002Fcomposables\u002FuseWallpaper.ts",{"path":3143,"add":1519,"del":15},"app\u002Futils\u002FpersistState.ts",{"hash":3145,"date":2093,"subject":3146,"files":3147,"plus":1519,"minus":1141},"dd68e63","modal scroll lock is reference-counted so overlapping modals don't fight",[3148],{"path":1861,"add":1519,"del":1141},{"hash":3150,"date":2093,"subject":3151,"files":3152,"plus":182,"minus":164},"3ceda02","route progress unhooks and anchor highlight clears both retries on unmount",[3153,3155],{"path":3154,"add":223,"del":72},"app\u002Fcomponents\u002FRouteProgress.vue",{"path":3156,"add":28,"del":28},"app\u002Fcomposables\u002FuseAnchorHighlight.ts",{"hash":3158,"date":2093,"subject":3159,"files":3160,"plus":82,"minus":72},"5c44cf0","cp\u002Fmv into home works and rm -rf gag only blocks whole-site targets",[3161],{"path":3038,"add":82,"del":72},{"hash":3163,"date":2093,"subject":3164,"files":3165,"plus":140,"minus":72},"850f319","quote-aware pipe split and head\u002Ftail short -N form in the terminal",[3166,3167],{"path":2443,"add":425,"del":72},{"path":3168,"add":278,"del":15},"tests\u002FterminalPipeline.test.ts",{"hash":3170,"date":2093,"subject":3171,"files":3172,"plus":263,"minus":82},"97770cd","typecheck the leaderboard test and validSubmission so CI deploys again",[3173,3174],{"path":1082,"add":72,"del":39},{"path":3175,"add":43,"del":28},"tests\u002FscoresCore.test.ts",{"hash":3177,"date":2093,"subject":3178,"files":3179,"plus":3185,"minus":3181},"09d938e","DesktopFiles' model moves into a useFileExplorer composable",[3180,3182,3184],{"path":170,"add":182,"del":3181},244,{"path":173,"add":3183,"del":15},270,{"path":919,"add":15,"del":15},285,{"hash":3187,"date":2093,"subject":3188,"files":3189,"plus":2667,"minus":2859},"6a11838","WebDesktop's iso download and screenshot move into useDesktopIo",[3190,3191],{"path":284,"add":28,"del":2859},{"path":1284,"add":2622,"del":15},{"hash":3193,"date":2093,"subject":3194,"files":3195,"plus":72,"minus":39},"24b57e2","the files list ignores keys from the inline rename input (fix rename navigating away)",[3196],{"path":170,"add":72,"del":39},{"hash":3198,"date":2093,"subject":3199,"files":3200,"plus":39,"minus":11},"b81c80a","e2e: contributions now sits in the co-completion cycle",[3201],{"path":937,"add":39,"del":11},{"hash":3203,"date":2093,"subject":3204,"files":3205,"plus":3216,"minus":11},"57a1a94","an online game leaderboard: finished games submit to the relay, the scores app and a leaderboard command show the global top",[3206,3207,3208,3210,3211,3212,3213,3214,3215],{"path":1555,"add":107,"del":11},{"path":1667,"add":2965,"del":15},{"path":3209,"add":43,"del":15},"app\u002Fplugins\u002Fleaderboard.client.ts",{"path":962,"add":175,"del":15},{"path":2215,"add":82,"del":15},{"path":431,"add":223,"del":15},{"path":129,"add":24,"del":15},{"path":1082,"add":132,"del":15},{"path":3175,"add":735,"del":15},320,{"hash":3218,"date":3219,"subject":3220,"files":3221,"plus":29,"minus":15},"8209869","2026-07-11","component tests for the modal focus traps and the settings danger zone",[3222,3224],{"path":3223,"add":278,"del":15},"tests\u002Fnuxt\u002FDesktopSettings.test.ts",{"path":3225,"add":25,"del":15},"tests\u002Fnuxt\u002FmodalTrap.test.ts",{"hash":3227,"date":3219,"subject":3228,"files":3229,"plus":3234,"minus":15},"72060a8","a code playground app: edit html\u002Fcss\u002Fjs with a live sandboxed preview",[3230,3231,3232,3233],{"path":284,"add":11,"del":15},{"path":1165,"add":32,"del":15},{"path":679,"add":11,"del":15},{"path":162,"add":120,"del":15},208,{"hash":3236,"date":3219,"subject":3237,"files":3238,"plus":3243,"minus":15},"107e79a","a contributions command draws recent github activity as an ascii heatmap",[3239,3240,3241],{"path":960,"add":830,"del":15},{"path":962,"add":175,"del":15},{"path":3242,"add":147,"del":15},"tests\u002FcontribGraph.test.ts",146,{"hash":3245,"date":3219,"subject":3246,"files":3247,"plus":3260,"minus":64},"d217a75","a less-style pager: | less pages long output with j\u002Fk, search and q to quit",[3248,3249,3251,3253,3255,3256,3258],{"path":115,"add":223,"del":40},{"path":3250,"add":40,"del":11},"app\u002Fdata\u002Fshortcuts.ts",{"path":3252,"add":352,"del":15},"app\u002Futils\u002Fterminal\u002Fpager.ts",{"path":3254,"add":82,"del":39},"app\u002Futils\u002Fterminal\u002FplanRun.ts",{"path":431,"add":182,"del":15},{"path":3257,"add":140,"del":15},"tests\u002Fpager.test.ts",{"path":3259,"add":164,"del":15},"tests\u002FplanRun.test.ts",179,{"hash":3262,"date":3219,"subject":3263,"files":3264,"plus":3267,"minus":64},"15d1595","the files app gains keyboard navigation and drag-and-drop to move files between folders",[3265,3266],{"path":170,"add":125,"del":64},{"path":447,"add":2136,"del":15},200,{"hash":3269,"date":3219,"subject":3270,"files":3271,"plus":39,"minus":40},"fedea84","the footer shortcuts link gets its own class so strict selectors still resolve",[3272],{"path":1852,"add":39,"del":40},{"hash":3274,"date":3219,"subject":3275,"files":3276,"plus":163,"minus":15},"4db3bdb","e2e: the \u002Fkeyboard reference page",[3277],{"path":45,"add":163,"del":15},{"hash":3279,"date":3219,"subject":3280,"files":3281,"plus":171,"minus":15},"55c75e4","swipe left\u002Fright between blog posts on touch",[3282],{"path":564,"add":171,"del":15},{"hash":3284,"date":3219,"subject":3285,"files":3286,"plus":3296,"minus":289},"bf98c60","a printable \u002Fkeyboard reference, sharing one shortcut dataset with the ? overlay and lvOS cheat sheet",[3287,3288,3289,3290,3291,3293,3294,3295],{"path":1852,"add":11,"del":15},{"path":2066,"add":39,"del":187},{"path":2896,"add":39,"del":75},{"path":3250,"add":351,"del":15},{"path":658,"add":3292,"del":15},82,{"path":149,"add":11,"del":11},{"path":631,"add":40,"del":11},{"path":2377,"add":40,"del":11},163,{"hash":3298,"date":3219,"subject":3299,"files":3300,"plus":900,"minus":11},"08b986c","the terminal suggests the nearest command on a typo (did you mean?)",[3301,3302,3304],{"path":115,"add":72,"del":11},{"path":3303,"add":171,"del":15},"app\u002Futils\u002Fterminal\u002FnearestCommand.ts",{"path":3305,"add":461,"del":15},"tests\u002FnearestCommand.test.ts",{"hash":3307,"date":3219,"subject":3308,"files":3309,"plus":2391,"minus":572},"0441cc8","extract the destroyer flight model into a pure, unit-tested shipPhysics module",[3310,3311,3313],{"path":1746,"add":82,"del":572},{"path":3312,"add":809,"del":15},"app\u002Futils\u002FshipPhysics.ts",{"path":3314,"add":132,"del":15},"tests\u002FshipPhysics.test.ts",{"hash":3316,"date":3219,"subject":3317,"files":3318,"plus":444,"minus":43},"2a5db6a","window manager: closing the focused window focuses the next; spawns stay on-screen",[3319,3320,3321],{"path":288,"add":28,"del":11},{"path":2308,"add":82,"del":39},{"path":2310,"add":223,"del":39},{"hash":3323,"date":3219,"subject":3324,"files":3325,"plus":140,"minus":75},"48b1e8d","terminal\u002Fworld fixes: quote-aware redirect, rm dir+child, a denied world pixel reverts",[3326,3327,3328,3329],{"path":2239,"add":1141,"del":39},{"path":3038,"add":223,"del":11},{"path":2443,"add":255,"del":82},{"path":3168,"add":164,"del":15},{"hash":3331,"date":3219,"subject":3332,"files":3333,"plus":263,"minus":11},"5265319","game fixes: a new game stops the old one, snake can follow its tail, tetris hold tops out",[3334,3335,3336],{"path":115,"add":39,"del":15},{"path":74,"add":28,"del":11},{"path":3337,"add":39,"del":15},"app\u002Futils\u002Fgames\u002Ftetris.ts",{"hash":3339,"date":3219,"subject":3340,"files":3341,"plus":3355,"minus":572,"truncated":263},"efac630","share previews render as PNG (social platforms reject SVG), plus rel=canonical and \u002Fstats + \u002Fmuseum in the sitemap",[3342,3343,3345,3346,3347,3348,3349,3350,3351,3352,3353,3354],{"path":416,"add":163,"del":15},{"path":3344,"add":11,"del":11},"app\u002Fpages\u002F418.vue",{"path":484,"add":11,"del":11},{"path":564,"add":11,"del":11},{"path":651,"add":11,"del":11},{"path":653,"add":11,"del":11},{"path":27,"add":11,"del":11},{"path":656,"add":11,"del":11},{"path":641,"add":11,"del":11},{"path":643,"add":11,"del":11},{"path":660,"add":11,"del":11},{"path":513,"add":11,"del":11},110,{"hash":3357,"date":3219,"subject":3358,"files":3359,"plus":1141,"minus":11},"82e9466","relay: prune per-connection cooldowns on disconnect, rate-limit chat and cursor fan-out",[3360],{"path":129,"add":1141,"del":11},{"hash":3362,"date":3219,"subject":3363,"files":3364,"plus":40,"minus":40},"513d6af","align the terminal env\u002Falias useState keys with the STATE_KEYS registry",[3365],{"path":115,"add":40,"del":40},{"hash":3367,"date":3219,"subject":3368,"files":3369,"plus":197,"minus":39},"9b5de0e","pause idle animation loops: the teapot, the media visualizer while paused, the hero trail on a hidden tab",[3370,3371,3372],{"path":1749,"add":28,"del":15},{"path":2175,"add":163,"del":11},{"path":3344,"add":43,"del":40},{"hash":3374,"date":3219,"subject":3375,"files":3376,"plus":107,"minus":163},"8381592","the destroyer chews through nested elements first; a big parent survives until its children are cleared",[3377],{"path":1746,"add":107,"del":163},{"hash":3379,"date":3219,"subject":3380,"files":3381,"plus":163,"minus":11},"b5dc59f","coalesce pixel-world redraws into one frame instead of a full board scan per pan move",[3382],{"path":277,"add":163,"del":11},{"hash":3384,"date":3219,"subject":3385,"files":3386,"plus":223,"minus":40},"a11cd09","LiveCursors no longer reconnects a socket after the component unmounts",[3387],{"path":2221,"add":223,"del":40},{"hash":3389,"date":3219,"subject":3390,"files":3391,"plus":444,"minus":223},"3b12873","escape visitor-supplied strings before v-html: alias\u002Fenv, the 404 shell's echo, link() and the stored nick",[3392,3394,3395,3396],{"path":3393,"add":39,"del":11},"app\u002Fcomposables\u002FuseIdentity.ts",{"path":115,"add":43,"del":40},{"path":482,"add":82,"del":28},{"path":861,"add":28,"del":40},{"hash":3398,"date":3219,"subject":3399,"files":3400,"plus":758,"minus":546},"f9db22b","the destroyer flies like Asteroids: rotate + thrust forward + space to fire, heavy momentum, no mouse-aiming",[3401,3402],{"path":1746,"add":290,"del":204},{"path":431,"add":163,"del":163},{"hash":3404,"date":3219,"subject":3405,"files":3406,"plus":1119,"minus":164},"8217dfb","touch: grabbable resize handles, long-press window menu, a reachable delete, and the ship only scrolls when flown",[3407,3408,3409],{"path":1746,"add":43,"del":28},{"path":170,"add":39,"del":40},{"path":286,"add":900,"del":40},{"hash":3411,"date":3219,"subject":3412,"files":3413,"plus":43,"minus":39},"5edea57","screen-reader polish: terminal output is a log, the pixel world a labelled image",[3414,3415],{"path":222,"add":28,"del":39},{"path":277,"add":40,"del":15},{"hash":3417,"date":3219,"subject":3418,"files":3419,"plus":263,"minus":39},"db8859a","dvh viewport units kill the mobile url-bar jump; \u002Flife gets a heading",[3420,3421],{"path":1671,"add":72,"del":40},{"path":643,"add":43,"del":11},{"hash":3423,"date":3219,"subject":3424,"files":3425,"plus":278,"minus":82},"a0c56f8","footer and contact-wizard links become real buttons keyboards can reach",[3426,3427,3428,3429],{"path":418,"add":11,"del":11},{"path":1852,"add":75,"del":40},{"path":261,"add":43,"del":40},{"path":843,"add":72,"del":39},{"hash":3431,"date":3219,"subject":3432,"files":3433,"plus":61,"minus":223},"9892b19","the command palette and terminal overlay trap focus like real dialogs",[3434,3435,3436],{"path":334,"add":43,"del":40},{"path":363,"add":171,"del":72},{"path":1861,"add":46,"del":72},{"hash":3438,"date":3219,"subject":3439,"files":3440,"plus":120,"minus":28},"3502b91","harden the pixel-world meta load and keep restored site folders seed-tracked",[3441,3442],{"path":2239,"add":223,"del":72},{"path":3443,"add":82,"del":11},"app\u002Futils\u002Ftrash.ts",{"hash":3445,"date":3219,"subject":3446,"files":3447,"plus":1120,"minus":43},"32605bc","reconcile filesystem, trash and tombstones across tabs via the storage event",[3448,3450,3451],{"path":3449,"add":203,"del":15},"app\u002Fplugins\u002FstorageSync.client.ts",{"path":179,"add":75,"del":43},{"path":42,"add":64,"del":15},{"hash":3453,"date":3219,"subject":3454,"files":3455,"plus":171,"minus":40},"e329291","deleting a blog post sticks even before its async seed lands",[3456,3457],{"path":42,"add":223,"del":11},{"path":3458,"add":263,"del":11},"tests\u002FsiteFs.test.ts",{"hash":3460,"date":3219,"subject":3461,"files":3462,"plus":506,"minus":40},"8af23e7","a full localStorage no longer loses files silently — the terminal and lvOS shout",[3463,3464,3465,3466,3467],{"path":284,"add":64,"del":15},{"path":115,"add":75,"del":15},{"path":179,"add":72,"del":11},{"path":42,"add":39,"del":11},{"path":3468,"add":182,"del":15},"app\u002Futils\u002Fterminal\u002FstorageHealth.ts",{"hash":3470,"date":3219,"subject":3471,"files":3472,"plus":3474,"minus":82},"e270681","the destroyer works on touch: drag flies, tap fires, and the hud grows an exit",[3473],{"path":1746,"add":3474,"del":82},66,{"hash":3476,"date":3219,"subject":3477,"files":3478,"plus":61,"minus":448},"fa3447d","paint masterpieces can hang in the gallery, not just on the wall",[3479,3480],{"path":1094,"add":2136,"del":75},{"path":162,"add":64,"del":40},{"hash":3482,"date":3219,"subject":3483,"files":3484,"plus":809,"minus":185},"646dbac","sticky notes become real files in ~\u002Fstickies — terminal, files app and badge all agree",[3485,3486,3487],{"path":284,"add":72,"del":39},{"path":1329,"add":480,"del":509},{"path":823,"add":15,"del":11},{"hash":3489,"date":3219,"subject":3490,"files":3491,"plus":150,"minus":46},"f0cfc4a","vim opens any file — the files app grows an 'edit in vim' menu (blog posts included)",[3492,3493,3494],{"path":284,"add":164,"del":11},{"path":170,"add":75,"del":40},{"path":1167,"add":492,"del":448},{"hash":3496,"date":3219,"subject":3497,"files":3498,"plus":2667,"minus":3504},"5468a11","copy sweep: one mail dataset, alt+r in the cheat sheet, an honest hero terminal",[3499,3500,3501,3502,3503],{"path":742,"add":39,"del":40},{"path":1297,"add":28,"del":1177},{"path":1555,"add":11,"del":11},{"path":2896,"add":11,"del":15},{"path":2725,"add":1481,"del":72},92,{"hash":3506,"date":3219,"subject":3507,"files":3508,"plus":163,"minus":11},"80abfa8","the white rabbit stays in lvOS — site effects render on the desktop now",[3509,3510],{"path":852,"add":11,"del":11},{"path":1673,"add":75,"del":15},{"hash":3512,"date":3219,"subject":3513,"files":3514,"plus":40,"minus":900},"606bfd5","delete the unreachable projects window — its buttons escaped the desktop",[3515,3516,3517],{"path":284,"add":15,"del":107},{"path":679,"add":11,"del":40},{"path":95,"add":11,"del":11},{"hash":3519,"date":3219,"subject":3520,"files":3521,"plus":3535,"minus":1491},"774ba09","site files really delete now (tombstones) — reseed and factory-reset bring things back",[3522,3524,3525,3526,3527,3528,3529,3530,3531,3532,3533,3534],{"path":3523,"add":11,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopBlog.vue",{"path":170,"add":263,"del":461},{"path":852,"add":2015,"del":11},{"path":3139,"add":72,"del":15},{"path":564,"add":11,"del":11},{"path":459,"add":1141,"del":15},{"path":3038,"add":175,"del":120},{"path":179,"add":28,"del":40},{"path":861,"add":171,"del":15},{"path":42,"add":3292,"del":255},{"path":431,"add":75,"del":64},{"path":3458,"add":185,"del":75},275,{"hash":3537,"date":3219,"subject":3538,"files":3539,"plus":72,"minus":72},"2777b6a","echo's redirect goes through writeFileAt — no more clobbering folders into files",[3540],{"path":3038,"add":72,"del":72},{"hash":3542,"date":3219,"subject":3543,"files":3544,"plus":28,"minus":39},"ba42d3c","tree resolves against the current directory like every other file command",[3545],{"path":34,"add":28,"del":39},{"hash":3547,"date":3219,"subject":3548,"files":3549,"plus":461,"minus":164},"fbc058c","tab completion keeps the argument's case — TODO.md completes now",[3550,3552],{"path":3551,"add":448,"del":82},"app\u002Futils\u002Fterminal\u002Fcompletion.ts",{"path":3553,"add":82,"del":11},"tests\u002Fcompletion.test.ts",{"hash":3555,"date":3219,"subject":3556,"files":3557,"plus":140,"minus":40},"ef4e90e","the destroyer flies the whole page: edge-diving scrolls it, manual scrolling is grounded",[3558,3559],{"path":1746,"add":731,"del":40},{"path":431,"add":197,"del":15},{"hash":3561,"date":3219,"subject":3562,"files":3563,"plus":3566,"minus":175},"a5f67e6","the destroyer ship flies: wasd\u002Farrows with thrust and inertia, tracer bullets toward the cursor",[3564,3565],{"path":1746,"add":2752,"del":200},{"path":301,"add":11,"del":11},128,{"hash":3568,"date":3219,"subject":3569,"files":3570,"plus":15,"minus":15},"b7289c2","favicon.ico is finally our own mark, not the nuxt default",[3571],{"path":3572,"add":15,"del":15},"public\u002Ffavicon.ico",{"hash":3574,"date":3219,"subject":3575,"files":3576,"plus":3594,"minus":3181,"truncated":223},"1491131","the site becomes a real filesystem: pages are folders, blog posts are editable markdown, cd stays in the shell",[3577,3578,3579,3582,3583,3584,3585,3588,3589,3590,3592,3593],{"path":284,"add":15,"del":39},{"path":3523,"add":302,"del":72},{"path":170,"add":3580,"del":3581},124,112,{"path":1859,"add":1496,"del":15},{"path":115,"add":197,"del":82},{"path":564,"add":185,"del":28},{"path":3586,"add":3587,"del":15},"app\u002Futils\u002FmarkdownLite.ts",129,{"path":34,"add":750,"del":1154},{"path":3551,"add":11,"del":11},{"path":3591,"add":289,"del":735},"app\u002Futils\u002Fterminal\u002FfileCommands.ts",{"path":3038,"add":200,"del":263},{"path":179,"add":506,"del":39},1092,{"hash":3596,"date":3219,"subject":3597,"files":3598,"plus":819,"minus":28},"8720a23","the gallery learns zoom, pan and download",[3599],{"path":1281,"add":819,"del":28},{"hash":3601,"date":3219,"subject":3602,"files":3603,"plus":3608,"minus":506},"c5ce388","the screenshot tool draws the real desktop: wallpaper, icons, windows, the terminal transcript",[3604,3605],{"path":284,"add":185,"del":506},{"path":3606,"add":3607,"del":15},"app\u002Futils\u002FdesktopShot.ts",282,318,{"hash":3610,"date":3219,"subject":3611,"files":3612,"plus":1479,"minus":2752},"d5d190c","one process table for ps, top and the task manager — same pids, same daemons, all really killable",[3613,3614,3615,3617,3618,3620,3621,3622,3623],{"path":284,"add":11,"del":11},{"path":2289,"add":461,"del":29},{"path":113,"add":3616,"del":15},75,{"path":115,"add":40,"del":75},{"path":3619,"add":302,"del":255},"app\u002Futils\u002Fgames\u002Ftop.ts",{"path":301,"add":64,"del":255},{"path":95,"add":28,"del":40},{"path":1342,"add":43,"del":223},{"path":447,"add":72,"del":39},{"hash":3625,"date":3219,"subject":3626,"files":3627,"plus":39,"minus":15},"0839515","quiet eslint on the untyped .vue dynamic imports — a fresh .nuxt broke lint",[3628],{"path":284,"add":39,"del":15},{"hash":3630,"date":3219,"subject":3631,"files":3632,"plus":2683,"minus":28},"b450a95","resume.pdf opens a real viewer window instead of leaving the desktop",[3633,3634,3636],{"path":284,"add":11,"del":39},{"path":3635,"add":1120,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopResume.vue",{"path":679,"add":40,"del":40},{"hash":3638,"date":3219,"subject":3639,"files":3640,"plus":457,"minus":3650},"e722f8f","media player and visualizer merge into one app — closing it stops the music",[3641,3642,3644,3647,3648,3649],{"path":284,"add":15,"del":11},{"path":2175,"add":3643,"del":506},125,{"path":3645,"add":15,"del":3646},"app\u002Fcomponents\u002Fdesktop\u002FDesktopVisualizer.vue",165,{"path":2681,"add":39,"del":39},{"path":103,"add":11,"del":11},{"path":679,"add":40,"del":39},213,{"hash":3652,"date":3219,"subject":3653,"files":3654,"plus":75,"minus":120},"2c062dc","esc dismisses menus and popups instead of logging out of lvOS",[3655,3656,3657,3658],{"path":284,"add":40,"del":39},{"path":2896,"add":11,"del":11},{"path":2841,"add":82,"del":163},{"path":301,"add":11,"del":11},{"hash":3660,"date":3219,"subject":3661,"files":3662,"plus":28,"minus":11},"9cb01c4","real glyphs: a trash icon for the recycle bin, play and image for media and gallery",[3663,3664],{"path":679,"add":11,"del":11},{"path":1005,"add":72,"del":15},{"hash":3666,"date":3219,"subject":3667,"files":3668,"plus":3670,"minus":150},"f3616fd","taskbar tray: battery moves right, a tile-windows button, popovers close like popovers",[3669],{"path":271,"add":3670,"del":150},93,{"hash":3672,"date":3219,"subject":3673,"files":3674,"plus":28,"minus":15},"1e5b978","the terminal overlay opens scrolled to the latest output",[3675],{"path":222,"add":28,"del":15},{"hash":3677,"date":3219,"subject":3678,"files":3679,"plus":263,"minus":28},"91f761b","the terminal input keeps just its blinking caret — no focus ring",[3680,3681],{"path":418,"add":40,"del":11},{"path":843,"add":164,"del":72},{"hash":3683,"date":3219,"subject":3684,"files":3685,"plus":11,"minus":11},"6935b8a","WORLD_PLOTS stays module-private",[3686],{"path":993,"add":11,"del":11},{"hash":3688,"date":3219,"subject":3689,"files":3690,"plus":14,"minus":40},"2c94e78","museum multiplayer wing, a relay deploy guide, and a \u002Fworld OG card",[3691,3692,3693,3694,3695],{"path":10,"add":39,"del":15},{"path":103,"add":223,"del":15},{"path":1735,"add":64,"del":11},{"path":2927,"add":289,"del":15},{"path":631,"add":40,"del":11},{"hash":3697,"date":3219,"subject":3698,"files":3699,"plus":278,"minus":1141},"7bc11a3","prop-less desktop apps dispatch through one component map",[3700],{"path":284,"add":278,"del":1141},{"hash":3702,"date":3219,"subject":3703,"files":3704,"plus":3714,"minus":1579},"fb5adcd","pixel world grows a minimap, live coords, named plots and a time-lapse",[3705,3706,3707,3709,3710,3711,3712],{"path":277,"add":2978,"del":29},{"path":2239,"add":75,"del":11},{"path":3708,"add":1119,"del":15},"app\u002Futils\u002FworldCamera.ts",{"path":45,"add":444,"del":15},{"path":129,"add":223,"del":40},{"path":993,"add":46,"del":15},{"path":3713,"add":185,"del":15},"tests\u002FworldCamera.test.ts",374,{"hash":3716,"date":3219,"subject":3717,"files":3718,"plus":3728,"minus":3729},"793d98e","split the three giant e2e specs in half for parallelism",[3719,3721,3722,3724,3725,3726],{"path":447,"add":40,"del":3720},449,{"path":162,"add":3720,"del":15},{"path":438,"add":15,"del":3723},460,{"path":45,"add":3723,"del":15},{"path":937,"add":15,"del":3723},{"path":431,"add":3727,"del":15},462,1373,1369,{"hash":3731,"date":3219,"subject":3732,"files":3733,"plus":3746,"minus":28,"truncated":39},"ae2fa38","round-22 batch: tips, file properties, gallery wallpaper, more badges, tray weather, live hero terminal, what's-new toast, README badge",[3734,3735,3736,3737,3738,3739,3740,3741,3742,3743,3744,3745],{"path":10,"add":40,"del":15},{"path":742,"add":278,"del":40},{"path":284,"add":64,"del":40},{"path":2130,"add":3355,"del":15},{"path":170,"add":137,"del":15},{"path":1281,"add":200,"del":15},{"path":2286,"add":72,"del":15},{"path":271,"add":223,"del":11},{"path":2703,"add":492,"del":15},{"path":1037,"add":1154,"del":15},{"path":1671,"add":11,"del":15},{"path":1039,"add":171,"del":15},409,{"hash":3748,"date":3219,"subject":3749,"files":3750,"plus":3566,"minus":164},"73d9435","adventure act 2: an attic, a trunk, a lantern and a roof that remembers you",[3751,3752],{"path":2384,"add":2817,"del":164},{"path":3753,"add":900,"del":15},"tests\u002Fadventure.test.ts",{"hash":3755,"date":3219,"subject":3756,"files":3757,"plus":40,"minus":11},"a8a2952","the museum acquires the pixel world",[3758],{"path":103,"add":40,"del":11},{"hash":3760,"date":3219,"subject":3761,"files":3762,"plus":3778,"minus":15},"a725e64","a hidden multiplayer pixel world: place, persist, gossip",[3763,3764,3765,3767,3769,3770,3771,3772,3773,3774,3776],{"path":347,"add":11,"del":15},{"path":284,"add":28,"del":15},{"path":277,"add":3766,"del":15},273,{"path":2239,"add":3768,"del":15},191,{"path":1735,"add":187,"del":15},{"path":679,"add":11,"del":15},{"path":301,"add":132,"del":15},{"path":438,"add":1154,"del":15},{"path":129,"add":3581,"del":15},{"path":993,"add":3775,"del":15},119,{"path":3777,"add":24,"del":15},"tests\u002FworldCore.test.ts",874,{"hash":3780,"date":3219,"subject":3781,"files":3782,"plus":3790,"minus":40},"7aad6c1","the battery is real now: command, tray icon and an honest boot nudge",[3783,3784,3785,3787,3788,3789],{"path":284,"add":182,"del":40},{"path":271,"add":182,"del":15},{"path":3786,"add":750,"del":15},"app\u002Fcomposables\u002FuseBattery.ts",{"path":935,"add":302,"del":15},{"path":447,"add":223,"del":15},{"path":937,"add":82,"del":15},113,{"hash":3792,"date":3219,"subject":3793,"files":3794,"plus":809,"minus":11},"3b3a12d","the start menu takes stylized screenshots into the gallery",[3795,3796,3797,3798],{"path":284,"add":900,"del":15},{"path":1281,"add":28,"del":11},{"path":271,"add":40,"del":15},{"path":447,"add":448,"del":15},{"hash":3800,"date":3219,"subject":3801,"files":3802,"plus":1993,"minus":120},"72c113e","desktop icons wear live badges for mail and the bin",[3803,3804,3805,3806,3807],{"path":284,"add":223,"del":40},{"path":746,"add":278,"del":40},{"path":1297,"add":28,"del":263},{"path":2725,"add":731,"del":15},{"path":447,"add":197,"del":39},{"hash":3809,"date":3219,"subject":3810,"files":3811,"plus":1105,"minus":15},"6bc3dca","a hall of fame gathers every high score",[3812,3813,3814,3815],{"path":284,"add":11,"del":15},{"path":1555,"add":1481,"del":15},{"path":679,"add":11,"del":15},{"path":447,"add":182,"del":15},{"hash":3817,"date":3219,"subject":3818,"files":3819,"plus":43,"minus":39},"69c9bb4","the museum acquires its round-19 pieces, including itself",[3820],{"path":103,"add":43,"del":39},{"hash":3822,"date":3219,"subject":3823,"files":3824,"plus":187,"minus":40},"827c697","the changelog walks the whole history, a screenful at a time",[3825,3826,3827],{"path":653,"add":492,"del":11},{"path":438,"add":223,"del":15},{"path":3828,"add":40,"del":11},"server\u002Froutes\u002Fgit-log.json.ts",{"hash":3830,"date":3219,"subject":3831,"files":3832,"plus":2368,"minus":72},"672e1f4","a temporary terminal-style curtain gates the private preview",[3833,3834,3835,3836],{"path":416,"add":43,"del":39},{"path":456,"add":457,"del":15},{"path":438,"add":461,"del":15},{"path":463,"add":164,"del":11},{"hash":3838,"date":3219,"subject":3839,"files":3840,"plus":3844,"minus":15},"f9e83df","docs: an architecture tour for cold starts",[3841,3842],{"path":10,"add":28,"del":15},{"path":105,"add":3843,"del":15},103,108,{"hash":3846,"date":3219,"subject":3847,"files":3848,"plus":1266,"minus":21},"2a470f6","TerminalConsole hands search, completion and the spinner to a composable",[3849,3850],{"path":222,"add":263,"del":21},{"path":3851,"add":810,"del":15},"app\u002Fcomposables\u002FuseTerminalConsole.ts",{"hash":3853,"date":3219,"subject":3854,"files":3855,"plus":531,"minus":2669},"19c3c45","WebDesktop sheds its keyboard and power logic into composables",[3856,3857,3858,3859],{"path":284,"add":302,"del":1866},{"path":2286,"add":11,"del":11},{"path":2522,"add":203,"del":15},{"path":2841,"add":3860,"del":15},98,{"hash":3862,"date":3219,"subject":3863,"files":3864,"plus":2683,"minus":174},"7d8c061","one static server, shared by the e2e suite and the resume step",[3865,3866,3868],{"path":199,"add":82,"del":1519},{"path":3867,"add":72,"del":122},"scripts\u002Fserve-static.mjs",{"path":213,"add":216,"del":15},{"hash":3870,"date":3219,"subject":3871,"files":3872,"plus":147,"minus":15},"ce67008","the start menu ships lvos-2.0.iso, a very real file",[3873,3874,3875],{"path":284,"add":425,"del":15},{"path":271,"add":40,"del":15},{"path":447,"add":164,"del":15},{"hash":3877,"date":3219,"subject":3878,"files":3879,"plus":2581,"minus":15},"79ad3b3","lvOS gets a mail inbox and a feed reader that reads this site",[3880,3881,3883,3885,3886],{"path":284,"add":40,"del":15},{"path":1297,"add":3882,"del":15},238,{"path":2286,"add":3884,"del":15},117,{"path":679,"add":40,"del":15},{"path":447,"add":278,"del":15},{"hash":3888,"date":3219,"subject":3889,"files":3890,"plus":2505,"minus":15},"23d18bf","the career timeline draws itself as you scroll",[3891,3892],{"path":484,"add":150,"del":15},{"path":438,"add":182,"del":15},{"hash":3894,"date":3219,"subject":3895,"files":3896,"plus":203,"minus":43},"8bcb0f2","the file explorer navigates by breadcrumb",[3897,3898],{"path":170,"add":1496,"del":28},{"path":447,"add":39,"del":40},{"hash":3900,"date":3219,"subject":3901,"files":3902,"plus":3581,"minus":120},"390a684","minesweeper learns three board sizes, a timer and best times",[3903,3904],{"path":2187,"add":21,"del":120},{"path":447,"add":171,"del":15},{"hash":3906,"date":3219,"subject":3907,"files":3908,"plus":3912,"minus":15},"3d8de04","the tamagotchi wanders the lvOS desktop",[3909,3910,3911],{"path":284,"add":39,"del":15},{"path":1618,"add":3587,"del":15},{"path":447,"add":46,"del":15},149,{"hash":3914,"date":3219,"subject":3915,"files":3916,"plus":3922,"minus":40},"17f2f3e","the museum floor becomes walkable, plaque by plaque",[3917,3918,3919,3920,3921],{"path":1308,"add":464,"del":15},{"path":660,"add":1154,"del":40},{"path":2088,"add":3243,"del":15},{"path":438,"add":492,"del":15},{"path":2090,"add":3474,"del":15},443,{"hash":3924,"date":3219,"subject":3925,"files":3926,"plus":2643,"minus":11},"9dc6c30","418: the site is a teapot, interactive and ASCII-rendered",[3927,3929,3931,3932,3933,3934],{"path":3344,"add":3928,"del":15},122,{"path":3930,"add":2993,"del":15},"app\u002Futils\u002Fteapot.ts",{"path":36,"add":263,"del":15},{"path":438,"add":197,"del":15},{"path":631,"add":40,"del":11},{"path":3935,"add":185,"del":15},"tests\u002Fteapot.test.ts",{"hash":3937,"date":3219,"subject":3938,"files":3939,"plus":82,"minus":72},"56cfbb9","runtime config is captured at setup, not inside lazy computeds",[3940,3941],{"path":2599,"add":72,"del":11},{"path":564,"add":72,"del":39},{"hash":3943,"date":3944,"subject":3945,"files":3946,"plus":40,"minus":11},"5bda5d2","2026-07-10","the ps e2e keeps up with the unified process table",[3947],{"path":937,"add":40,"del":11},{"hash":3949,"date":3944,"subject":3950,"files":3951,"plus":448,"minus":223},"9960fe7","the README catches up with six rounds of features",[3952,3953],{"path":10,"add":163,"del":164},{"path":103,"add":11,"del":11},{"hash":3955,"date":3944,"subject":3956,"files":3957,"plus":1489,"minus":15},"9de5ff7","\u002Fmuseum catalogues every exhibit the site ships",[3958,3959,3960,3961],{"path":103,"add":1105,"del":15},{"path":660,"add":174,"del":15},{"path":962,"add":82,"del":15},{"path":438,"add":182,"del":15},{"hash":3963,"date":3944,"subject":3964,"files":3965,"plus":174,"minus":43},"7572e7d","every top-level page gets its own OG card",[3966,3967,3968,3969,3970,3971,3972,3973,3974,3975,3976],{"path":484,"add":82,"del":15},{"path":651,"add":82,"del":11},{"path":653,"add":82,"del":11},{"path":27,"add":82,"del":11},{"path":656,"add":82,"del":11},{"path":513,"add":82,"del":11},{"path":663,"add":82,"del":15},{"path":486,"add":82,"del":11},{"path":488,"add":82,"del":11},{"path":438,"add":82,"del":15},{"path":631,"add":46,"del":15},{"hash":3978,"date":3944,"subject":3979,"files":3980,"plus":185,"minus":43},"693ed14","the node scripts get checkJs coverage via JSDoc",[3981,3982,3983,3984,3985,3986,3987],{"path":54,"add":11,"del":15},{"path":63,"add":11,"del":15},{"path":129,"add":40,"del":40},{"path":631,"add":164,"del":40},{"path":199,"add":43,"del":39},{"path":3867,"add":40,"del":15},{"path":3031,"add":448,"del":15},{"hash":3989,"date":3944,"subject":3990,"files":3991,"plus":3117,"minus":1481},"f4f02f1","about this computer moves into its own component",[3992,3993],{"path":284,"add":40,"del":1481},{"path":850,"add":174,"del":15},{"hash":3995,"date":3944,"subject":3996,"files":3997,"plus":197,"minus":39},"29142a3","project cards morph into their detail pages via view transitions",[3998,3999,4000],{"path":951,"add":64,"del":40},{"path":566,"add":40,"del":11},{"path":843,"add":46,"del":15},{"hash":4002,"date":3944,"subject":4003,"files":4004,"plus":2807,"minus":302},"40e31cd","the file explorer gets a right-click menu with a real rename",[4005,4006,4007,4008],{"path":170,"add":1240,"del":120},{"path":179,"add":200,"del":15},{"path":447,"add":731,"del":15},{"path":181,"add":509,"del":11},{"hash":4010,"date":3944,"subject":4011,"files":4012,"plus":2978,"minus":11},"d832712","alt+r summons a run dialog that launches apps by name",[4013,4014,4015,4016,4017,4018],{"path":284,"add":255,"del":11},{"path":2060,"add":947,"del":15},{"path":271,"add":40,"del":15},{"path":679,"add":461,"del":15},{"path":447,"add":171,"del":15},{"path":681,"add":572,"del":15},{"hash":4020,"date":3944,"subject":4021,"files":4022,"plus":4035,"minus":773,"truncated":72},"ccfcee5","one process table: ps, kill, top and the task manager share a world",[4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033,4034],{"path":170,"add":39,"del":40},{"path":2289,"add":1496,"del":64},{"path":1167,"add":461,"del":164},{"path":115,"add":120,"del":39},{"path":3619,"add":82,"del":39},{"path":301,"add":492,"del":223},{"path":95,"add":1498,"del":72},{"path":3591,"add":11,"del":11},{"path":3038,"add":40,"del":40},{"path":1342,"add":255,"del":164},{"path":38,"add":11,"del":11},{"path":1039,"add":11,"del":11},345,{"hash":4037,"date":3944,"subject":4038,"files":4039,"plus":263,"minus":28},"051d2af","github stats wait to count up until they scroll into view",[4040],{"path":949,"add":263,"del":28},{"hash":4042,"date":3944,"subject":4043,"files":4044,"plus":3844,"minus":223},"acc14d9","about this computer reports real uptime, display and storage",[4045,4046,4047,4048,4049],{"path":284,"add":1481,"del":43},{"path":2876,"add":11,"del":11},{"path":271,"add":11,"del":11},{"path":679,"add":11,"del":11},{"path":447,"add":182,"del":15},{"hash":4051,"date":3944,"subject":4052,"files":4053,"plus":3843,"minus":72},"641ad68","tile windows arranges every open window into a grid",[4054,4055,4056,4057,4059,4060],{"path":284,"add":39,"del":11},{"path":271,"add":40,"del":15},{"path":288,"add":255,"del":40},{"path":4058,"add":545,"del":15},"app\u002Futils\u002FsnapZones.ts",{"path":447,"add":302,"del":15},{"path":4061,"add":572,"del":11},"tests\u002FsnapZones.test.ts",{"hash":4063,"date":3944,"subject":4064,"files":4065,"plus":4079,"minus":1141},"658e698","rm sends files to a restorable lvOS recycle bin",[4066,4067,4068,4071,4072,4073,4074,4075,4076,4077],{"path":284,"add":11,"del":15},{"path":170,"add":289,"del":263},{"path":4069,"add":4070,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopTrash.vue",123,{"path":3139,"add":150,"del":15},{"path":679,"add":11,"del":15},{"path":823,"add":11,"del":15},{"path":3038,"add":28,"del":28},{"path":3443,"add":2777,"del":15},{"path":447,"add":572,"del":15},{"path":4078,"add":216,"del":15},"tests\u002Ftrash.test.ts",410,{"hash":4081,"date":3944,"subject":4082,"files":4083,"plus":830,"minus":15},"a765e95","sh runs shell scripts from the virtual filesystem",[4084,4085,4086,4087],{"path":115,"add":1154,"del":15},{"path":861,"add":200,"del":15},{"path":127,"add":40,"del":15},{"path":937,"add":163,"del":15},{"hash":4089,"date":3944,"subject":4090,"files":4091,"plus":4098,"minus":4097},"5fd36f3","systemCommands splits into shell, file-write and misc modules",[4092,4093,4094,4095],{"path":3038,"add":3650,"del":15},{"path":36,"add":1076,"del":15},{"path":861,"add":1076,"del":15},{"path":4096,"add":82,"del":4097},"app\u002Futils\u002Fterminal\u002FsystemCommands.ts",597,635,{"hash":4100,"date":3944,"subject":4101,"files":4102,"plus":11,"minus":11},"b5276f5","loadAdventure stays module-private",[4103],{"path":2384,"add":11,"del":11},{"hash":4105,"date":3944,"subject":4106,"files":4107,"plus":4115,"minus":15},"767fa65","pet: a tamagotchi hatches into the status bar",[4108,4109,4110,4111,4112,4113,4114],{"path":880,"add":164,"del":15},{"path":1072,"add":1477,"del":15},{"path":1080,"add":1177,"del":15},{"path":823,"add":39,"del":15},{"path":1039,"add":750,"del":15},{"path":937,"add":197,"del":15},{"path":1087,"add":1477,"del":15},294,{"hash":4117,"date":3944,"subject":4118,"files":4119,"plus":39,"minus":39},"76d6479","bootDesktop waits for the desktop itself, not the blink-fast BIOS",[4120],{"path":1781,"add":39,"del":39},{"hash":4122,"date":3944,"subject":4123,"files":4124,"plus":4139,"minus":448},"b2080df","tmux: ctrl+b splits the terminal into independent panes",[4125,4126,4127,4128,4130,4131,4132,4134,4135,4136,4137],{"path":222,"add":263,"del":82},{"path":363,"add":11,"del":11},{"path":316,"add":2667,"del":15},{"path":4129,"add":11,"del":11},"app\u002Fcomponents\u002Fdesktop\u002FDesktopTerminal.vue",{"path":115,"add":2859,"del":39},{"path":823,"add":39,"del":15},{"path":4133,"add":289,"del":15},"app\u002Futils\u002Fterminal\u002Fpanes.ts",{"path":4096,"add":182,"del":11},{"path":127,"add":64,"del":15},{"path":937,"add":278,"del":15},{"path":4138,"add":735,"del":15},"tests\u002Fpanes.test.ts",337,{"hash":4141,"date":3944,"subject":4142,"files":4143,"plus":4150,"minus":11},"68c3e1f","adventure: the site becomes a text-adventure dungeon with a grue",[4144,4146,4147,4148,4149],{"path":2384,"add":4145,"del":15},454,{"path":1342,"add":223,"del":11},{"path":978,"add":11,"del":15},{"path":937,"add":171,"del":15},{"path":3753,"add":819,"del":15},595,{"hash":4152,"date":3944,"subject":4153,"files":4154,"plus":492,"minus":545},"670f2b1","e2e opens the terminal with a hydration-safe retried keypress",[4155,4156,4157,4158],{"path":1781,"add":75,"del":39},{"path":843,"add":72,"del":28},{"path":438,"add":39,"del":72},{"path":937,"add":223,"del":448},{"hash":4160,"date":3944,"subject":4161,"files":4162,"plus":40,"minus":11},"594d2a5","reduced-motion shutdown e2e gets the same 10s timeout as its sibling",[4163],{"path":447,"add":40,"del":11},{"hash":4165,"date":3944,"subject":4166,"files":4167,"plus":48,"minus":72},"679565b","fireworks command lights up the sky as killable pid 1231",[4168,4170,4171,4172,4173,4174,4175,4176,4177,4178],{"path":4169,"add":1085,"del":15},"app\u002Fcomponents\u002FFireworksShow.vue",{"path":1669,"add":40,"del":11},{"path":115,"add":40,"del":11},{"path":1671,"add":40,"del":11},{"path":823,"add":11,"del":15},{"path":301,"add":302,"del":15},{"path":95,"add":40,"del":11},{"path":127,"add":11,"del":15},{"path":937,"add":197,"del":15},{"path":305,"add":72,"del":15},{"hash":4180,"date":3944,"subject":4181,"files":4182,"plus":75,"minus":11},"886b273","persistence e2e tests wait for the storage watchers before reloading",[4183],{"path":937,"add":75,"del":11},{"hash":4185,"date":3944,"subject":4186,"files":4187,"plus":4198,"minus":4191},"db08a82","contentCommands splits into site, browse and file modules",[4188,4189,4192,4194,4196],{"path":34,"add":3646,"del":15},{"path":4190,"add":164,"del":4191},"app\u002Futils\u002Fterminal\u002FcontentCommands.ts",555,{"path":3591,"add":4193,"del":15},224,{"path":4195,"add":900,"del":15},"app\u002Futils\u002Fterminal\u002FpostHelpers.ts",{"path":962,"add":4197,"del":15},162,607,{"hash":4200,"date":3944,"subject":4201,"files":4202,"plus":134,"minus":11},"57b3f23","boss key: b-b swaps the site for a very important spreadsheet",[4203,4205,4206,4207,4208,4209,4210,4211],{"path":4204,"add":530,"del":15},"app\u002Fcomponents\u002FBossScreen.vue",{"path":115,"add":11,"del":15},{"path":1671,"add":11,"del":15},{"path":823,"add":11,"del":15},{"path":301,"add":164,"del":15},{"path":95,"add":40,"del":11},{"path":127,"add":11,"del":15},{"path":438,"add":302,"del":15},{"hash":4213,"date":3944,"subject":4214,"files":4215,"plus":3504,"minus":15},"05c1bab","the offline page ships a pocket snake",[4216,4217],{"path":438,"add":223,"del":15},{"path":4218,"add":3292,"del":15},"public\u002Foffline.html",{"hash":4220,"date":3944,"subject":4221,"files":4222,"plus":255,"minus":11},"1652e70","the hit counter rolls up from zero",[4223],{"path":1752,"add":255,"del":11},{"hash":4225,"date":3944,"subject":4226,"files":4227,"plus":82,"minus":39},"a833ffc","stats bars load as skeletons",[4228],{"path":486,"add":82,"del":39},{"hash":4230,"date":3944,"subject":4231,"files":4232,"plus":1496,"minus":28},"ee4e510","changelog commits carry github-style diffstat blocks",[4233,4234,4235],{"path":653,"add":25,"del":72},{"path":396,"add":64,"del":11},{"path":438,"add":82,"del":15},{"hash":4237,"date":3944,"subject":4238,"files":4239,"plus":509,"minus":15},"ddcf2a8","theme-color meta follows the real surface color",[4240,4242],{"path":4241,"add":444,"del":15},"app\u002Fplugins\u002FthemeColor.client.ts",{"path":843,"add":164,"del":15},{"hash":4244,"date":3944,"subject":4245,"files":4246,"plus":1519,"minus":15},"ef2262a","the tab favicon becomes a prompt block while the terminal is open",[4247,4249],{"path":4248,"add":197,"del":15},"app\u002Fplugins\u002FterminalFavicon.client.ts",{"path":843,"add":75,"del":15},{"hash":4251,"date":3944,"subject":4252,"files":4253,"plus":147,"minus":15},"4edc718","emacs: politely declined, with escalation",[4254,4255],{"path":301,"add":425,"del":15},{"path":937,"add":263,"del":15},{"hash":4257,"date":3944,"subject":4258,"files":4259,"plus":1498,"minus":263},"439adbd","unit-test the wallpaper list and index-restore logic",[4260,4261],{"path":3141,"add":302,"del":263},{"path":4262,"add":509,"del":15},"tests\u002Fwallpaper.test.ts",{"hash":4264,"date":3944,"subject":4265,"files":4266,"plus":4271,"minus":1120},"ca4de9e","extract the status bar's easter eggs into a tested composable",[4267,4268,4269],{"path":880,"add":223,"del":1120},{"path":2727,"add":3790,"del":15},{"path":4270,"add":150,"del":15},"tests\u002FstatusBarEggs.test.ts",173,{"hash":4273,"date":3944,"subject":4274,"files":4275,"plus":4284,"minus":4279},"aaabdca","split funCommands into game, toy, net and effect modules",[4276,4277,4280,4281,4283],{"path":301,"add":3032,"del":15},{"path":4278,"add":263,"del":4279},"app\u002Futils\u002Fterminal\u002FfunCommands.ts",432,{"path":1342,"add":352,"del":15},{"path":38,"add":4282,"del":15},120,{"path":1039,"add":2485,"del":15},480,{"hash":4286,"date":3944,"subject":4287,"files":4288,"plus":11,"minus":11},"39d929a","drop a redundant String() on the stats page",[4289],{"path":486,"add":11,"del":11},{"hash":4291,"date":3944,"subject":4292,"files":4293,"plus":72,"minus":72},"95d955c","knip housekeeping: list the eslint plugin, unexport internal types",[4294,4296,4297,4298,4299],{"path":4295,"add":15,"del":40},"app\u002Fcomponents\u002FAppIcon.vue",{"path":1645,"add":11,"del":11},{"path":127,"add":11,"del":11},{"path":60,"add":11,"del":15},{"path":63,"add":11,"del":15},{"hash":4301,"date":3944,"subject":4302,"files":4303,"plus":940,"minus":11},"9716091","pgp publishing scaffold: \u002Fpgp.txt, contact line and a gpg command, all waiting for a key",[4304,4305,4306,4307,4308,4309],{"path":23,"add":82,"del":15},{"path":27,"add":302,"del":15},{"path":4278,"add":1141,"del":15},{"path":438,"add":1141,"del":15},{"path":149,"add":40,"del":11},{"path":4310,"add":223,"del":15},"server\u002Froutes\u002Fpgp.txt.ts",{"hash":4312,"date":3944,"subject":4313,"files":4314,"plus":529,"minus":15},"69db360","a public \u002Fstats page draws the goatcounter numbers as bars",[4315,4317,4318],{"path":486,"add":4316,"del":15},151,{"path":4190,"add":11,"del":15},{"path":438,"add":64,"del":15},{"hash":4320,"date":3944,"subject":4321,"files":4322,"plus":3292,"minus":164},"b41f559","hang a paint-app drawing as the lvOS wallpaper",[4323,4324,4325,4326],{"path":1094,"add":1154,"del":39},{"path":3141,"add":175,"del":28},{"path":823,"add":40,"del":11},{"path":447,"add":120,"del":15},{"hash":4328,"date":3944,"subject":4329,"files":4330,"plus":3581,"minus":40},"2901f21","ctrl+alt+arrows snap lvOS windows from the keyboard",[4331,4332,4333,4334,4335,4336],{"path":284,"add":163,"del":15},{"path":2896,"add":11,"del":15},{"path":288,"add":444,"del":11},{"path":4058,"add":731,"del":15},{"path":447,"add":255,"del":15},{"path":4061,"add":197,"del":11},{"hash":4338,"date":3944,"subject":4339,"files":4340,"plus":351,"minus":28},"9025df8","fetching commands show a braille spinner",[4341,4342,4343,4344,4345,4346,4347],{"path":222,"add":461,"del":11},{"path":115,"add":263,"del":11},{"path":823,"add":11,"del":15},{"path":4190,"add":28,"del":40},{"path":4278,"add":28,"del":11},{"path":127,"add":39,"del":15},{"path":937,"add":461,"del":15},{"hash":4349,"date":3944,"subject":4350,"files":4351,"plus":302,"minus":15},"b822581","the status bar tells the time",[4352,4353],{"path":880,"add":163,"del":15},{"path":843,"add":64,"del":15},{"hash":4355,"date":3944,"subject":4356,"files":4357,"plus":140,"minus":203},"1ae769a","share the cheat-sheet rows between the site and lvOS overlays",[4358,4359,4360],{"path":1645,"add":735,"del":15},{"path":2066,"add":39,"del":200},{"path":2896,"add":72,"del":197},{"hash":4362,"date":3944,"subject":4363,"files":4364,"plus":3292,"minus":572},"cf2d07b","help categories live on the command definitions, not a central map",[4365,4366,4367,4369,4370,4371,4372],{"path":4190,"add":46,"del":15},{"path":4278,"add":1141,"del":15},{"path":4368,"add":64,"del":197},"app\u002Futils\u002Fterminal\u002FhelpGroups.ts",{"path":935,"add":64,"del":15},{"path":4096,"add":278,"del":15},{"path":127,"add":28,"del":15},{"path":4373,"add":82,"del":82},"tests\u002FhelpGroups.test.ts",{"hash":4375,"date":3944,"subject":4376,"files":4377,"plus":464,"minus":968,"truncated":200},"9fdbace","adopt typescript-eslint strict type-checked linting",[4378,4379,4380,4381,4382,4383,4384,4386,4387,4388,4389,4390],{"path":1852,"add":11,"del":11},{"path":4295,"add":28,"del":106},{"path":334,"add":11,"del":11},{"path":261,"add":39,"del":72},{"path":1749,"add":11,"del":11},{"path":2221,"add":75,"del":64},{"path":4385,"add":11,"del":11},"app\u002Fcomponents\u002FPartyMode.vue",{"path":1877,"add":40,"del":40},{"path":222,"add":40,"del":40},{"path":284,"add":39,"del":39},{"path":746,"add":11,"del":11},{"path":852,"add":11,"del":11},{"hash":4392,"date":3944,"subject":4393,"files":4394,"plus":182,"minus":163},"75a007f","enable exactOptionalPropertyTypes",[4395,4396,4398,4400,4401,4403,4404,4405],{"path":2865,"add":39,"del":39},{"path":4397,"add":11,"del":11},"app\u002Fcomposables\u002FuseCanvasScene.ts",{"path":4399,"add":11,"del":11},"app\u002Fcomposables\u002FuseDesktopToasts.ts",{"path":288,"add":39,"del":39},{"path":4402,"add":40,"del":40},"app\u002Futils\u002FdrawLife.ts",{"path":396,"add":11,"del":11},{"path":4368,"add":11,"del":11},{"path":149,"add":39,"del":11},{"hash":4407,"date":3944,"subject":4408,"files":4409,"plus":39,"minus":40},"9c041e3","resume script grabs a free port instead of hardcoding 4199",[4410],{"path":199,"add":39,"del":40},{"hash":4412,"date":3944,"subject":4413,"files":4414,"plus":302,"minus":82},"fec6013","harden the three timing-sensitive navigation e2e tests",[4415],{"path":843,"add":302,"del":82},{"hash":4417,"date":3944,"subject":4418,"files":4419,"plus":11,"minus":11},"dd67bda","keep ResizeDir local to DesktopWindow",[4420],{"path":286,"add":11,"del":11},{"hash":4422,"date":3944,"subject":4423,"files":4424,"plus":1107,"minus":15},"4a505b9","retro odometer hit counter in the footer (public goatcounter)",[4425,4426,4427,4429,4430],{"path":1852,"add":11,"del":15},{"path":1752,"add":809,"del":15},{"path":4428,"add":43,"del":15},"app\u002Futils\u002FformatHits.ts",{"path":438,"add":64,"del":15},{"path":4431,"add":448,"del":15},"tests\u002FformatHits.test.ts",{"hash":4433,"date":3944,"subject":4434,"files":4435,"plus":1519,"minus":15},"01280ef","greet view-sourcers with an ascii banner in the page source",[4436,4437],{"path":438,"add":82,"del":15},{"path":4438,"add":492,"del":15},"server\u002Fplugins\u002FsourceBanner.ts",{"hash":4440,"date":4441,"subject":4442,"files":4443,"plus":4457,"minus":122,"truncated":40},"a9893dc","2026-07-09","add stylelint (standard-scss) to lint, lint-staged and CI",[4444,4445,4446,4447,4448,4449,4450,4451,4452,4453,4454,4455],{"path":54,"add":11,"del":15},{"path":418,"add":72,"del":39},{"path":261,"add":11,"del":11},{"path":2221,"add":40,"del":40},{"path":222,"add":11,"del":11},{"path":170,"add":11,"del":11},{"path":746,"add":11,"del":40},{"path":852,"add":11,"del":11},{"path":286,"add":11,"del":11},{"path":482,"add":11,"del":11},{"path":513,"add":11,"del":11},{"path":60,"add":4456,"del":61},1191,1249,{"hash":4459,"date":4441,"subject":4460,"files":4461,"plus":4465,"minus":1377},"9625c83","extract lvOS window chrome into DesktopWindow.vue",[4462,4463],{"path":284,"add":830,"del":1377},{"path":286,"add":4464,"del":15},256,332,{"hash":4467,"date":4441,"subject":4468,"files":4469,"plus":197,"minus":2136},"7bff5c1","one global keycap style for kbd, deduped from six scoped copies",[4470,4471,4472,4473,4474,4475,4476,4477],{"path":418,"add":163,"del":15},{"path":1852,"add":15,"del":64},{"path":334,"add":15,"del":64},{"path":2066,"add":15,"del":64},{"path":2896,"add":15,"del":28},{"path":653,"add":15,"del":164},{"path":2353,"add":15,"del":43},{"path":438,"add":75,"del":15},{"hash":4479,"date":4441,"subject":4480,"files":4481,"plus":1496,"minus":11},"0da4cac","contact shows my live local time with a reply-window hint",[4482,4483],{"path":27,"add":187,"del":11},{"path":438,"add":82,"del":15},{"hash":4485,"date":4441,"subject":4486,"files":4487,"plus":4495,"minus":40},"baaebec","telnet towel.blinkenlights.nl plays an ascii star wars homage",[4488,4490,4491,4492,4493],{"path":4489,"add":186,"del":15},"app\u002Futils\u002Fgames\u002Fstarwars.ts",{"path":4278,"add":444,"del":11},{"path":978,"add":11,"del":15},{"path":937,"add":182,"del":15},{"path":4494,"add":545,"del":11},"tests\u002FterminalGames.test.ts",260,{"hash":4497,"date":4441,"subject":4498,"files":4499,"plus":107,"minus":40},"997e715","man pages gain a SEE ALSO section from the help groups",[4500,4501,4502,4503],{"path":4368,"add":223,"del":15},{"path":4096,"add":64,"del":11},{"path":937,"add":75,"del":15},{"path":4373,"add":1141,"del":11},{"hash":4505,"date":4441,"subject":4506,"files":4507,"plus":4514,"minus":182},"13678dd","chain commands with &&, || and ;",[4508,4509,4511,4512],{"path":115,"add":1477,"del":182},{"path":4510,"add":809,"del":15},"app\u002Futils\u002Fterminal\u002Fchain.ts",{"path":937,"add":461,"del":15},{"path":4513,"add":1477,"del":15},"tests\u002Fchain.test.ts",216,{"hash":4516,"date":4441,"subject":4517,"files":4518,"plus":200,"minus":72},"a058716","type analytics events through a catalog module",[4519,4521,4522,4524],{"path":4520,"add":72,"del":40},"app\u002Fcomposables\u002FuseAnalytics.ts",{"path":115,"add":11,"del":40},{"path":4523,"add":163,"del":15},"app\u002Futils\u002FanalyticsEvents.ts",{"path":4525,"add":164,"del":15},"tests\u002FanalyticsEvents.test.ts",{"hash":4527,"date":4441,"subject":4528,"files":4529,"plus":223,"minus":15},"9efd1d4","enable noUncheckedIndexedAccess",[4530],{"path":149,"add":223,"del":15},{"hash":4532,"date":4441,"subject":4533,"files":4534,"plus":39,"minus":11},"2a16ede","exclude nuxt component tests from the pure-logic vitest run",[4535],{"path":4536,"add":39,"del":11},"vitest.config.ts",{"hash":4538,"date":4441,"subject":4539,"files":4540,"plus":39,"minus":15},"37d3b41","gitignore the generated .nuxtrc",[4541],{"path":347,"add":39,"del":15},{"hash":4543,"date":4441,"subject":4544,"files":4545,"plus":3504,"minus":15},"c502082","add chmod, tail -f and PWA app shortcuts",[4546,4547,4548,4549,4550],{"path":4190,"add":750,"del":15},{"path":4096,"add":182,"del":15},{"path":438,"add":164,"del":15},{"path":937,"add":1141,"del":15},{"path":149,"add":64,"del":15},{"hash":4552,"date":4441,"subject":4553,"files":4554,"plus":11,"minus":11},"0c49afc","keep ASCII_LOGO local to infoCommands",[4555],{"path":935,"add":11,"del":11},{"hash":4557,"date":4441,"subject":4558,"files":4559,"plus":118,"minus":1633},"d2fe9ac","split the system-info commands out of systemCommands",[4560,4561],{"path":935,"add":2391,"del":15},{"path":4096,"add":40,"del":1633},{"hash":4563,"date":4441,"subject":4564,"files":4565,"plus":2777,"minus":75},"65b2e1f","add a dev console treasure hunt with a hire reward command",[4566,4567,4568],{"path":1632,"add":187,"del":263},{"path":4096,"add":263,"del":11},{"path":843,"add":200,"del":15},{"hash":4570,"date":4441,"subject":4571,"files":4572,"plus":572,"minus":11},"a3c1b72","hide a CRT-mode toggle behind a triple-click on the brand glyph",[4573,4574],{"path":248,"add":171,"del":11},{"path":843,"add":75,"del":15},{"hash":4576,"date":4441,"subject":4577,"files":4578,"plus":572,"minus":120},"6e9fc33","share one copy-flag composable for the copy\u002Frss\u002Fshare affordances",[4579,4580,4581],{"path":1246,"add":492,"del":15},{"path":564,"add":40,"del":43},{"path":651,"add":40,"del":263},{"hash":4583,"date":4441,"subject":4584,"files":4585,"plus":3474,"minus":64},"684a495","add an ls -l long listing",[4586,4587,4588,4589],{"path":4190,"add":182,"del":28},{"path":179,"add":461,"del":15},{"path":937,"add":223,"del":15},{"path":181,"add":302,"del":11},{"hash":4591,"date":4441,"subject":4592,"files":4593,"plus":255,"minus":15},"d7d6ef7","add id and hostname commands",[4594,4595],{"path":4096,"add":263,"del":15},{"path":937,"add":164,"del":15},{"hash":4597,"date":4441,"subject":4598,"files":4599,"plus":3616,"minus":750},"099fdcd","consolidate the lvOS window titles, icons and layout into one app registry",[4600,4601],{"path":284,"add":182,"del":750},{"path":679,"add":122,"del":15},{"hash":4603,"date":4441,"subject":4604,"files":4605,"plus":1496,"minus":11},"2a10b8b","add arrow-key navigation across the project cards",[4606,4607],{"path":1877,"add":425,"del":11},{"path":438,"add":120,"del":15},{"hash":4609,"date":4441,"subject":4610,"files":4611,"plus":2015,"minus":28},"51657c6","animate theme toggles with a circular reveal from the click point",[4612,4613,4614,4615,4617],{"path":418,"add":11,"del":15},{"path":880,"add":40,"del":72},{"path":17,"add":11,"del":11},{"path":4616,"add":61,"del":15},"app\u002Fcomposables\u002FuseThemeSwitch.ts",{"path":843,"add":164,"del":15},{"hash":4619,"date":4441,"subject":4620,"files":4621,"plus":25,"minus":15},"4fe8f27","reflect the running terminal command in the browser tab title",[4622,4623],{"path":115,"add":302,"del":15},{"path":937,"add":1141,"del":15},{"hash":4625,"date":4441,"subject":4626,"files":4627,"plus":731,"minus":15},"65ec4d5","add line numbers to blog code blocks",[4628,4629],{"path":564,"add":46,"del":15},{"path":438,"add":163,"del":15},{"hash":4631,"date":4441,"subject":4632,"files":4633,"plus":3117,"minus":197},"fedc80b","extract the ps\u002Fkill effect-process registry into a tested module",[4634,4635,4636],{"path":95,"add":735,"del":15},{"path":4278,"add":223,"del":197},{"path":305,"add":187,"del":15},{"hash":4638,"date":4441,"subject":4639,"files":4640,"plus":147,"minus":263},"e20384a","make curl actually fetch cors-friendly urls",[4641,4642],{"path":4278,"add":492,"del":263},{"path":937,"add":163,"del":15},{"hash":4644,"date":4441,"subject":4645,"files":4646,"plus":255,"minus":15},"9d88181","add an uptime command",[4647,4648],{"path":4096,"add":448,"del":15},{"path":937,"add":64,"del":15},{"hash":4650,"date":4441,"subject":4651,"files":4652,"plus":4663,"minus":40},"69d9133","add nuxt-environment component tests for the status bar and palette",[4653,4654,4655,4656,4657,4659,4661],{"path":54,"add":11,"del":15},{"path":2918,"add":40,"del":11},{"path":60,"add":2393,"del":15},{"path":63,"add":28,"del":11},{"path":4658,"add":731,"del":15},"tests\u002Fnuxt\u002FAppStatusBar.test.ts",{"path":4660,"add":175,"del":15},"tests\u002Fnuxt\u002FCommandPalette.test.ts",{"path":4662,"add":263,"del":15},"vitest.nuxt.config.ts",624,{"hash":4665,"date":4441,"subject":4666,"files":4667,"plus":2359,"minus":46},"88ca4aa","extract the terminal run() parse\u002Fexpand phase into a tested planner",[4668,4669,4670],{"path":115,"add":182,"del":46},{"path":3254,"add":735,"del":15},{"path":3259,"add":2136,"del":15},{"hash":4672,"date":4441,"subject":4673,"files":4674,"plus":2859,"minus":509,"truncated":82},"dda227b","centralize the shared useState keys in a typed registry",[4675,4676,4677,4678,4680,4681,4682,4683,4684,4685,4686,4687],{"path":880,"add":11,"del":11},{"path":769,"add":11,"del":11},{"path":4385,"add":11,"del":11},{"path":4679,"add":11,"del":11},"app\u002Fcomponents\u002FSlTrain.vue",{"path":222,"add":11,"del":11},{"path":1329,"add":11,"del":11},{"path":852,"add":40,"del":40},{"path":2289,"add":11,"del":11},{"path":2413,"add":11,"del":11},{"path":17,"add":40,"del":40},{"path":3393,"add":11,"del":11},{"path":2599,"add":39,"del":39},{"hash":4689,"date":4441,"subject":4690,"files":4691,"plus":24,"minus":11},"00fef41","show a git-derived updated date and dateModified on edited posts",[4692,4693,4694],{"path":564,"add":223,"del":15},{"path":438,"add":182,"del":15},{"path":149,"add":120,"del":11},{"hash":4696,"date":4441,"subject":4697,"files":4698,"plus":132,"minus":11},"2236fc4","add a JSON Resume endpoint generated from profile data",[4699,4700,4701],{"path":438,"add":75,"del":15},{"path":149,"add":11,"del":11},{"path":2445,"add":187,"del":15},{"hash":4703,"date":4441,"subject":4704,"files":4705,"plus":82,"minus":15},"7aee567","advertise the rss feed with a rel=alternate link",[4706,4707],{"path":438,"add":64,"del":15},{"path":149,"add":40,"del":15},{"hash":4709,"date":4441,"subject":4710,"files":4711,"plus":735,"minus":15},"dd1ee1f","add a print stylesheet for blog posts with url footnotes",[4712,4713],{"path":418,"add":25,"del":15},{"path":438,"add":163,"del":15},{"hash":4715,"date":4441,"subject":4716,"files":4717,"plus":492,"minus":15},"b13f20b","give outline-stripped inputs a keyboard focus ring too",[4718,4719],{"path":418,"add":163,"del":15},{"path":843,"add":1141,"del":15},{"hash":4721,"date":4441,"subject":4722,"files":4723,"plus":546,"minus":15},"9d9302e","add a terminal-styled route loading bar",[4724,4725,4726],{"path":3154,"add":204,"del":15},{"path":1671,"add":11,"del":15},{"path":843,"add":75,"del":15},{"hash":4728,"date":4441,"subject":4729,"files":4730,"plus":1154,"minus":545},"3f71170","share the e2e terminal and desktop drivers in a helpers module",[4731,4732,4733],{"path":1781,"add":731,"del":15},{"path":447,"add":40,"del":75},{"path":937,"add":40,"del":448},{"hash":4735,"date":4441,"subject":4736,"files":4737,"plus":106,"minus":120},"3f71270","rewrite the README feature tour to match the current site",[4738],{"path":10,"add":106,"del":120},{"hash":4740,"date":4441,"subject":4741,"files":4742,"plus":3243,"minus":11},"e7fe6dd","add an asciicam toy that renders the webcam as ascii",[4743,4745,4746,4747,4748],{"path":4744,"add":185,"del":15},"app\u002Futils\u002FasciiCam.ts",{"path":4278,"add":29,"del":15},{"path":937,"add":75,"del":15},{"path":463,"add":223,"del":11},{"path":4749,"add":492,"del":15},"tests\u002FasciiCam.test.ts",{"hash":4751,"date":4441,"subject":4752,"files":4753,"plus":750,"minus":15},"42dffef","give every loading skeleton a shared shimmer",[4754,4755],{"path":418,"add":185,"del":15},{"path":438,"add":223,"del":15},{"hash":4757,"date":4441,"subject":4758,"files":4759,"plus":2965,"minus":11},"090eded","add a pointer trail overlay to the hero life canvas",[4760,4761],{"path":1749,"add":122,"del":11},{"path":438,"add":120,"del":15},{"hash":4763,"date":4441,"subject":4764,"files":4765,"plus":1498,"minus":11},"bca2f55","hide a playable snake in the 404 recovery shell behind play",[4766,4767],{"path":482,"add":1519,"del":11},{"path":438,"add":448,"del":15},{"hash":4769,"date":4441,"subject":4770,"files":4771,"plus":545,"minus":11},"20fd364","add a shared terminal error formatter for consistent lvsh copy",[4772,4773,4775],{"path":115,"add":40,"del":11},{"path":4774,"add":75,"del":15},"app\u002Futils\u002Fterminal\u002Ferrors.ts",{"path":4776,"add":75,"del":15},"tests\u002Ferrors.test.ts",{"hash":4778,"date":4441,"subject":4779,"files":4780,"plus":830,"minus":64},"b8c510d","add a say command that shows a speech bubble on your live cursor",[4781,4782,4783,4784,4785],{"path":2221,"add":506,"del":28},{"path":2599,"add":72,"del":11},{"path":4190,"add":1141,"del":15},{"path":937,"add":64,"del":15},{"path":129,"add":223,"del":15},{"hash":4787,"date":4441,"subject":4788,"files":4789,"plus":1240,"minus":15},"e7ed69c","add a keyboard cheat sheet to lvOS (press ?)",[4790,4791,4792],{"path":284,"add":120,"del":15},{"path":2896,"add":968,"del":15},{"path":447,"add":75,"del":15},{"hash":4794,"date":4441,"subject":4795,"files":4796,"plus":1085,"minus":15},"ec3e5e2","add an image viewer app to lvOS for the site art",[4797,4798,4799],{"path":284,"add":39,"del":15},{"path":1281,"add":947,"del":15},{"path":447,"add":1141,"del":15},{"hash":4801,"date":4441,"subject":4802,"files":4803,"plus":2669,"minus":15},"55ed150","add snake as an lvOS desktop app",[4804,4805,4806],{"path":284,"add":39,"del":15},{"path":974,"add":21,"del":15},{"path":447,"add":75,"del":15},{"hash":4808,"date":4441,"subject":4809,"files":4810,"plus":750,"minus":72},"15d1219","add a hold slot and ghost piece to terminal tetris",[4811,4812],{"path":3337,"add":1154,"del":72},{"path":4494,"add":75,"del":15},{"hash":4814,"date":4441,"subject":4815,"files":4816,"plus":506,"minus":15},"1f6a082","add a sysinfo command with a neofetch-style session card",[4817,4818],{"path":4096,"add":731,"del":15},{"path":937,"add":223,"del":15},{"hash":4820,"date":4441,"subject":4821,"files":4822,"plus":3355,"minus":82},"2331ea5","group the help listing into command categories",[4823,4824,4825,4826],{"path":4368,"add":140,"del":15},{"path":4096,"add":75,"del":82},{"path":937,"add":164,"del":15},{"path":4373,"add":1154,"del":15},{"hash":4828,"date":4441,"subject":4829,"files":4830,"plus":4845,"minus":4844},"237473e","split terminalGames into per-game modules behind a barrel",[4831,4833,4834,4836,4837,4838,4840,4841,4842,4843],{"path":4832,"add":2389,"del":15},"app\u002Futils\u002Fgames\u002Fgame2048.ts",{"path":2025,"add":1177,"del":15},{"path":4835,"add":480,"del":15},"app\u002Futils\u002Fgames\u002Flife.ts",{"path":2712,"add":2485,"del":15},{"path":74,"add":819,"del":15},{"path":3337,"add":4839,"del":15},167,{"path":3619,"add":140,"del":15},{"path":2027,"add":75,"del":15},{"path":2029,"add":3581,"del":15},{"path":978,"add":163,"del":4844},841,866,{"hash":4847,"date":4441,"subject":4848,"files":4849,"plus":2777,"minus":72},"f6b0dbc","extract shared ascii box-drawing helpers for the games",[4850,4852,4853],{"path":4851,"add":750,"del":15},"app\u002Futils\u002FasciiFrame.ts",{"path":978,"add":39,"del":72},{"path":4854,"add":731,"del":15},"tests\u002FasciiFrame.test.ts",{"hash":4856,"date":4441,"subject":4857,"files":4858,"plus":11,"minus":11},"6931761","run knip via npx so the npm script resolves it",[4859],{"path":63,"add":11,"del":11},{"hash":4861,"date":4441,"subject":4862,"files":4863,"plus":4871,"minus":28},"88c998f","add real nano and vim editors to the terminal",[4864,4865,4866,4868,4869],{"path":222,"add":28,"del":40},{"path":4096,"add":203,"del":39},{"path":4867,"add":757,"del":15},"app\u002Futils\u002FterminalEditors.ts",{"path":937,"add":509,"del":15},{"path":4870,"add":1109,"del":15},"tests\u002FterminalEditors.test.ts",578,{"hash":4873,"date":4441,"subject":4874,"files":4875,"plus":1477,"minus":11},"a1a13e7","show a live visitor count that toggles the cursor dots (relay-gated)",[4876,4877,4878,4879],{"path":880,"add":461,"del":15},{"path":2221,"add":163,"del":11},{"path":2599,"add":461,"del":15},{"path":843,"add":64,"del":15},{"hash":4881,"date":4441,"subject":4882,"files":4883,"plus":39,"minus":15},"eec26c6","run the full check suite on pull requests without deploying",[4884],{"path":54,"add":39,"del":15},{"hash":4886,"date":4441,"subject":4887,"files":4888,"plus":150,"minus":40},"f948854","give every blog tag its own stable hue",[4889,4890,4891,4893],{"path":564,"add":75,"del":11},{"path":651,"add":43,"del":11},{"path":4892,"add":164,"del":15},"app\u002Futils\u002FtagHue.ts",{"path":4894,"add":461,"del":15},"tests\u002FtagHue.test.ts",{"hash":4896,"date":4441,"subject":4897,"files":4898,"plus":509,"minus":40},"cc190ef","count the github stats up when they load",[4899,4900],{"path":949,"add":255,"del":40},{"path":438,"add":75,"del":15},{"hash":4902,"date":4441,"subject":4903,"files":4904,"plus":39,"minus":40},"d651a65","avoid terminal-closing navigation in the pushd e2e test",[4905],{"path":937,"add":39,"del":40},{"hash":4907,"date":4441,"subject":4908,"files":4909,"plus":3185,"minus":64},"fc7f3b9","add destroy mode: a ship that shoots the site to pieces (esc repairs)",[4910,4911,4912,4913,4914,4915,4916,4917],{"path":880,"add":255,"del":11},{"path":1746,"add":3650,"del":15},{"path":1669,"add":40,"del":11},{"path":115,"add":40,"del":11},{"path":1671,"add":39,"del":40},{"path":4278,"add":263,"del":11},{"path":127,"add":11,"del":15},{"path":937,"add":572,"del":15},{"hash":4919,"date":4441,"subject":4920,"files":4921,"plus":3843,"minus":28},"1bedcb7","add a titlebar context menu with pin-on-top to lvOS windows",[4922,4923,4924],{"path":284,"add":2859,"del":28},{"path":288,"add":82,"del":15},{"path":447,"add":444,"del":15},{"hash":4926,"date":4441,"subject":4927,"files":4928,"plus":1498,"minus":15},"2c99da3","teach the terminal to do a barrel roll",[4929,4930,4931],{"path":418,"add":263,"del":15},{"path":4278,"add":197,"del":15},{"path":937,"add":182,"del":15},{"hash":4933,"date":4441,"subject":4934,"files":4935,"plus":546,"minus":39},"e380073","add persisted terminal font scaling via fontsize and ctrl keys",[4936,4937,4938,4939],{"path":222,"add":444,"del":39},{"path":3137,"add":461,"del":15},{"path":4096,"add":461,"del":15},{"path":937,"add":255,"del":15},{"hash":4941,"date":4441,"subject":4942,"files":4943,"plus":351,"minus":28},"c5fa4e1","remember directories: cd - plus pushd and popd",[4944,4945],{"path":4190,"add":1496,"del":28},{"path":937,"add":255,"del":15},{"hash":4947,"date":4441,"subject":4948,"files":4949,"plus":545,"minus":11},"ef88cf4","let | copy pipe any command output to the clipboard",[4950,4951],{"path":115,"add":75,"del":11},{"path":937,"add":448,"del":15},{"hash":4953,"date":4441,"subject":4954,"files":4955,"plus":3216,"minus":11},"de69abf","add pre-commit hooks running eslint on staged files",[4956,4957,4959],{"path":584,"add":11,"del":15},{"path":60,"add":4958,"del":15},312,{"path":63,"add":43,"del":11},{"hash":4961,"date":4441,"subject":4962,"files":4963,"plus":171,"minus":1579},"59af98c","prune dead code found by knip and add it as an npm script",[4964,4966,4967,4968,4969,4970,4971,4972,4973],{"path":4965,"add":15,"del":289},"app\u002Fcomponents\u002FThemeToggle.vue",{"path":746,"add":11,"del":11},{"path":479,"add":11,"del":11},{"path":179,"add":11,"del":11},{"path":396,"add":11,"del":11},{"path":127,"add":11,"del":11},{"path":2918,"add":448,"del":15},{"path":63,"add":11,"del":15},{"path":199,"add":11,"del":11},{"hash":4975,"date":4441,"subject":4976,"files":4977,"plus":163,"minus":15},"76f4c26","typecheck the unit tests with vue-tsc in CI",[4978,4979,4980],{"path":54,"add":11,"del":15},{"path":63,"add":11,"del":15},{"path":4981,"add":263,"del":15},"tsconfig.tests.json",{"hash":4983,"date":4441,"subject":4984,"files":4985,"plus":25,"minus":11},"6e41f3a","add a stats command and CI wiring to enable goatcounter via repo variable",[4986,4987,4988],{"path":54,"add":39,"del":15},{"path":4190,"add":545,"del":11},{"path":937,"add":64,"del":15},{"hash":4990,"date":4441,"subject":4991,"files":4992,"plus":39,"minus":11},"a884b17","fix void return type of the 404 shell's push helper",[4993],{"path":482,"add":39,"del":11},{"hash":4995,"date":4441,"subject":4996,"files":4997,"plus":122,"minus":1519},"3821483","let the 404 shell reuse the terminal's command shape and completion",[4998],{"path":482,"add":122,"del":1519},{"hash":5000,"date":4441,"subject":5001,"files":5002,"plus":830,"minus":39},"9948ce7","serve a terminal-styled offline fallback for uncached navigations",[5003,5004,5005,5006],{"path":438,"add":120,"del":15},{"path":149,"add":43,"del":40},{"path":4218,"add":900,"del":15},{"path":3867,"add":72,"del":11},{"hash":5008,"date":4441,"subject":5009,"files":5010,"plus":150,"minus":15},"1a21a20","add a share affordance to blog posts (web share with copy fallback)",[5011,5012],{"path":564,"add":2136,"del":15},{"path":438,"add":263,"del":15},{"hash":5014,"date":4441,"subject":5015,"files":5016,"plus":3580,"minus":40},"825abf6","add a game of life live wallpaper to lvOS and persist the wallpaper choice",[5017,5018,5019,5020],{"path":284,"add":39,"del":15},{"path":233,"add":204,"del":15},{"path":3141,"add":197,"del":40},{"path":447,"add":461,"del":15},{"hash":5022,"date":4441,"subject":5023,"files":5024,"plus":175,"minus":15},"a25b498","add a qr command that encodes any text or the current url",[5025,5026],{"path":4096,"add":46,"del":15},{"path":937,"add":263,"del":15},{"hash":5028,"date":4441,"subject":5029,"files":5030,"plus":203,"minus":11},"e89174c","add an ssh easter egg that rehomes the prompt for the session",[5031,5032,5033],{"path":222,"add":39,"del":11},{"path":4096,"add":1519,"del":15},{"path":937,"add":75,"del":15},{"hash":5035,"date":4441,"subject":5036,"files":5037,"plus":2569,"minus":39},"fa0a53b","support bash-style history expansion: !!, !n and !prefix",[5038,5039,5041,5042],{"path":115,"add":263,"del":39},{"path":5040,"add":425,"del":15},"app\u002Futils\u002Fterminal\u002Fhistory.ts",{"path":937,"add":163,"del":15},{"path":5043,"add":1154,"del":15},"tests\u002FhistoryExpansion.test.ts",{"hash":5045,"date":4441,"subject":5046,"files":5047,"plus":509,"minus":40},"fd1e6cf","morph blog titles between list and post with view transitions",[5048,5049,5050,5051],{"path":418,"add":163,"del":15},{"path":564,"add":11,"del":11},{"path":651,"add":72,"del":11},{"path":438,"add":448,"del":15},{"hash":5053,"date":4441,"subject":5054,"files":5055,"plus":1498,"minus":15},"21bad06","show a which-key hint in the status bar while a g chord is pending",[5056,5057,5059],{"path":880,"add":223,"del":15},{"path":5058,"add":182,"del":15},"app\u002Fcomposables\u002FuseVimScroll.ts",{"path":843,"add":545,"del":15},{"hash":5061,"date":4441,"subject":5062,"files":5063,"plus":1579,"minus":15},"ff469af","flash-highlight the section a deep link lands on",[5064,5065,5066],{"path":418,"add":197,"del":15},{"path":3156,"add":1154,"del":15},{"path":1671,"add":39,"del":15},{"hash":5068,"date":4441,"subject":5069,"files":5070,"plus":223,"minus":15},"4975a21","link the changelog from the footer build stamp",[5071],{"path":1852,"add":223,"del":15},{"hash":5073,"date":4441,"subject":5074,"files":5075,"plus":545,"minus":82},"c771522","share one escapeHtml util across the html-emitting modules",[5076,5078,5080,5081,5083,5085],{"path":5077,"add":43,"del":15},"app\u002Futils\u002FescapeHtml.ts",{"path":5079,"add":11,"del":40},"app\u002Futils\u002FminimarkHtml.ts",{"path":396,"add":40,"del":40},{"path":5082,"add":40,"del":40},"app\u002Futils\u002Fterminal\u002Fsearch.ts",{"path":5084,"add":40,"del":40},"app\u002Futils\u002FterminalMarkdown.ts",{"path":5086,"add":75,"del":15},"tests\u002FescapeHtml.test.ts",{"hash":5088,"date":4441,"subject":5089,"files":5090,"plus":187,"minus":15},"a5ba941","emit BreadcrumbList structured data alongside the visible trail",[5091,5093],{"path":5092,"add":197,"del":15},"app\u002Fcomponents\u002FPathBreadcrumbs.vue",{"path":438,"add":1141,"del":15},{"hash":5095,"date":4441,"subject":5096,"files":5097,"plus":2569,"minus":171},"47e14ac","derive last-updated dates from git for \u002Fnow and the sitemap",[5098,5099,5100,5101,5102,5103],{"path":505,"add":40,"del":11},{"path":513,"add":72,"del":11},{"path":4190,"add":72,"del":11},{"path":438,"add":182,"del":15},{"path":149,"add":263,"del":11},{"path":2377,"add":773,"del":46},{"hash":5105,"date":4441,"subject":5106,"files":5107,"plus":5112,"minus":11},"0ee52ba","add a \u002Fchangelog page rendering the repo's baked history",[5108,5109,5110,5111],{"path":17,"add":11,"del":15},{"path":653,"add":118,"del":15},{"path":438,"add":263,"del":15},{"path":2377,"add":11,"del":11},145,{"hash":5114,"date":4441,"subject":5115,"files":5116,"plus":197,"minus":43},"59faabd","run e2e spec files in parallel, halving suite wall time",[5117,5118],{"path":438,"add":171,"del":64},{"path":463,"add":72,"del":11},{"hash":5120,"date":4441,"subject":5121,"files":5122,"plus":514,"minus":5112},"ecaf700","split the taskbar's calendar and notification center into components",[5123,5124,5125],{"path":1573,"add":2777,"del":15},{"path":2296,"add":3504,"del":15},{"path":271,"add":39,"del":5112},{"hash":5127,"date":4441,"subject":5128,"files":5129,"plus":216,"minus":163},"e3e8aa4","extract reverse-i-search matching into a tested module",[5130,5131,5133],{"path":222,"add":72,"del":163},{"path":5132,"add":255,"del":15},"app\u002Futils\u002Fterminal\u002FreverseSearch.ts",{"path":5134,"add":1519,"del":15},"tests\u002FreverseSearch.test.ts",{"hash":5136,"date":4441,"subject":5137,"files":5138,"plus":2924,"minus":15},"c93d82e","add a ceremonial lock screen to lvOS",[5139,5140,5141,5142],{"path":284,"add":1141,"del":15},{"path":374,"add":4282,"del":15},{"path":271,"add":40,"del":15},{"path":447,"add":302,"del":15},{"hash":5144,"date":4441,"subject":5145,"files":5146,"plus":5152,"minus":255},"3513b0e","support * wildcards in ls, cat, cp, mv and rm",[5147,5148,5149,5150,5151],{"path":4190,"add":278,"del":40},{"path":179,"add":492,"del":15},{"path":4096,"add":425,"del":46},{"path":937,"add":444,"del":15},{"path":181,"add":61,"del":11},152,{"hash":5154,"date":4441,"subject":5155,"files":5156,"plus":489,"minus":15},"75fda1e","add df and du showing the site's real localStorage footprint",[5157,5159,5160,5161],{"path":5158,"add":940,"del":15},"app\u002Futils\u002Fterminal\u002FstorageUsage.ts",{"path":4096,"add":182,"del":15},{"path":937,"add":263,"del":15},{"path":5162,"add":1477,"del":15},"tests\u002FstorageUsage.test.ts",{"hash":5164,"date":4441,"subject":5165,"files":5166,"plus":351,"minus":40},"5caf3bd","add ps and kill commands wired to the real site effects",[5167,5168,5169,5170],{"path":115,"add":40,"del":11},{"path":4278,"add":735,"del":15},{"path":127,"add":40,"del":11},{"path":937,"add":46,"del":15},{"hash":5172,"date":4441,"subject":5173,"files":5174,"plus":82,"minus":11},"3eea6bd","make the svg favicon adapt to the browser color scheme",[5175],{"path":5176,"add":82,"del":11},"public\u002Ffavicon.svg",{"hash":5178,"date":4441,"subject":5179,"files":5180,"plus":2136,"minus":15},"6fb182d","make the terminal greeting time-of-day aware",[5181,5182,5184,5185],{"path":115,"add":40,"del":15},{"path":5183,"add":164,"del":15},"app\u002Futils\u002Fterminal\u002Fgreeting.ts",{"path":937,"add":64,"del":15},{"path":5186,"add":461,"del":15},"tests\u002Fgreeting.test.ts",{"hash":5188,"date":4441,"subject":5189,"files":5190,"plus":1491,"minus":40},"837bedc","add shutdown and reboot to lvOS with a CRT power-off collapse",[5191,5192,5193],{"path":284,"add":140,"del":11},{"path":271,"add":28,"del":11},{"path":447,"add":444,"del":15},{"hash":5195,"date":4441,"subject":5196,"files":5197,"plus":1476,"minus":15},"0a308fb","add an lvOS task manager where kill really closes windows",[5198,5199,5200],{"path":284,"add":39,"del":15},{"path":2289,"add":48,"del":15},{"path":447,"add":425,"del":15},{"hash":5202,"date":4441,"subject":5203,"files":5204,"plus":2389,"minus":39},"7c82ef4","ship full post content in the rss feed via content:encoded",[5205,5206,5207,5208],{"path":5079,"add":140,"del":15},{"path":438,"add":263,"del":15},{"path":633,"add":82,"del":39},{"path":5209,"add":506,"del":15},"tests\u002FminimarkHtml.test.ts",{"hash":5211,"date":4441,"subject":5212,"files":5213,"plus":791,"minus":15},"420ab14","add a weather command with wttr-style ascii output",[5214,5215,5216,5217],{"path":4278,"add":61,"del":15},{"path":2230,"add":809,"del":15},{"path":937,"add":444,"del":15},{"path":2233,"add":735,"del":15},{"hash":5219,"date":4441,"subject":5220,"files":5221,"plus":186,"minus":39},"4ca668f","add pong to the terminal games",[5222,5223,5224,5225],{"path":4278,"add":82,"del":11},{"path":978,"add":2485,"del":11},{"path":937,"add":263,"del":15},{"path":4494,"add":187,"del":11},{"hash":5227,"date":4441,"subject":5228,"files":5229,"plus":2389,"minus":15},"8d74305","persist terminal aliases and exported env vars across visits",[5230,5231,5233,5234],{"path":115,"add":263,"del":15},{"path":5232,"add":61,"del":15},"app\u002Futils\u002Fterminal\u002FshellState.ts",{"path":937,"add":182,"del":15},{"path":5235,"add":24,"del":15},"tests\u002FshellState.test.ts",{"hash":5237,"date":4441,"subject":5238,"files":5239,"plus":2428,"minus":2622},"8992196","consolidate localStorage access into a safeStorage util",[5240,5241,5242,5243,5244,5245,5246,5247,5248,5249],{"path":1329,"add":263,"del":46},{"path":1167,"add":164,"del":223},{"path":2413,"add":72,"del":164},{"path":17,"add":43,"del":75},{"path":3393,"add":28,"del":75},{"path":459,"add":132,"del":15},{"path":179,"add":182,"del":1141},{"path":5040,"add":72,"del":164},{"path":2215,"add":72,"del":223},{"path":5250,"add":1477,"del":15},"tests\u002FsafeStorage.test.ts",{"hash":5252,"date":4441,"subject":5253,"files":5254,"plus":187,"minus":40},"c16ea84","add vim go-to chords: gh, gb and gp navigate",[5255,5256,5257],{"path":2066,"add":40,"del":11},{"path":5058,"add":255,"del":11},{"path":843,"add":302,"del":15},{"hash":5259,"date":4441,"subject":5260,"files":5261,"plus":509,"minus":15},"ce70e8e","mark external prose links with a small arrow glyph",[5262,5263],{"path":418,"add":302,"del":15},{"path":438,"add":163,"del":15},{"hash":5265,"date":4441,"subject":5266,"files":5267,"plus":480,"minus":40},"f3d2477","stamp the footer with the build date and clickable git hash",[5268,5269,5270],{"path":1852,"add":25,"del":11},{"path":843,"add":182,"del":15},{"path":149,"add":448,"del":11},{"hash":5272,"date":4441,"subject":5273,"files":5274,"plus":263,"minus":11},"4b87086","let full-viewport pages opt out of the breadcrumb strip",[5275,5276,5277],{"path":5092,"add":72,"del":11},{"path":643,"add":39,"del":15},{"path":843,"add":72,"del":15},{"hash":5279,"date":4441,"subject":5280,"files":5281,"plus":1489,"minus":40},"f6453d0","add a downloadable vCard with scannable ascii QR on \u002Fcontact and contact --qr",[5282,5283,5284,5286,5287,5288,5289,5290,5291,5292,5293,5295],{"path":222,"add":263,"del":15},{"path":27,"add":216,"del":15},{"path":5285,"add":147,"del":15},"app\u002Futils\u002FqrAscii.ts",{"path":4190,"add":75,"del":11},{"path":438,"add":46,"del":15},{"path":937,"add":82,"del":15},{"path":149,"add":11,"del":11},{"path":60,"add":43,"del":15},{"path":63,"add":11,"del":15},{"path":3867,"add":11,"del":15},{"path":5294,"add":444,"del":15},"server\u002Froutes\u002Fcontact.vcf.ts",{"path":5296,"add":24,"del":15},"tests\u002FqrAscii.test.ts",{"hash":5298,"date":4441,"subject":5299,"files":5300,"plus":514,"minus":15},"9b657c2","add a search command that greps the blog's full text",[5301,5302,5303,5304,5305,5306],{"path":115,"add":40,"del":15},{"path":4190,"add":197,"del":15},{"path":5082,"add":122,"del":15},{"path":127,"add":39,"del":15},{"path":937,"add":223,"del":15},{"path":5307,"add":204,"del":15},"tests\u002Fsearch.test.ts",{"hash":5309,"date":4441,"subject":5310,"files":5311,"plus":1105,"minus":46},"3b69ca8","make blog tags clickable filters with a shareable ?tag= param",[5312,5313],{"path":651,"add":1481,"del":46},{"path":438,"add":46,"del":15},{"hash":5315,"date":4441,"subject":5316,"files":5317,"plus":5321,"minus":492},"0c52fea","extract pure window-manager order and resize math into a tested util",[5318,5319,5320],{"path":288,"add":171,"del":492},{"path":2308,"add":351,"del":15},{"path":2310,"add":2505,"del":15},155,{"hash":5323,"date":4441,"subject":5324,"files":5325,"plus":5333,"minus":3790},"1cbc997","extract navbar scramble, modal menu and scroll progress into composables",[5326,5327,5328,5330,5331],{"path":248,"add":64,"del":3790},{"path":1861,"add":2683,"del":15},{"path":5329,"add":444,"del":15},"app\u002Fcomposables\u002FuseScrollProgress.ts",{"path":837,"add":735,"del":15},{"path":5332,"add":200,"del":15},"tests\u002FtextScramble.test.ts",177,{"hash":5335,"date":4441,"subject":5336,"files":5337,"plus":5342,"minus":492},"948b9cc","let the lvOS files app browse the terminal's persistent filesystem",[5338,5340,5341],{"path":170,"add":5339,"del":175},150,{"path":115,"add":40,"del":11},{"path":447,"add":444,"del":15},175,{"hash":5344,"date":5345,"subject":5346,"files":5347,"plus":2440,"minus":40},"7ef4fd7","2026-07-08","add a wpm typing test game to the terminal",[5348,5349,5350,5351],{"path":4278,"add":82,"del":11},{"path":978,"add":3355,"del":15},{"path":937,"add":263,"del":15},{"path":4494,"add":216,"del":11},{"hash":5353,"date":5345,"subject":5354,"files":5355,"plus":5366,"minus":11},"eb11bb7","add a git command replaying the repo's real commit history",[5356,5357,5358,5359,5360,5361,5362,5363,5364,5365],{"path":54,"add":39,"del":15},{"path":222,"add":64,"del":15},{"path":396,"add":2389,"del":15},{"path":4096,"add":480,"del":15},{"path":937,"add":263,"del":15},{"path":149,"add":11,"del":11},{"path":60,"add":120,"del":15},{"path":63,"add":11,"del":15},{"path":3828,"add":182,"del":15},{"path":399,"add":3670,"del":15},326,{"hash":5368,"date":5345,"subject":5369,"files":5370,"plus":3355,"minus":15},"15ec0fe","add pwd-style clickable breadcrumbs to subpages",[5371,5372,5373],{"path":5092,"add":1107,"del":15},{"path":1671,"add":11,"del":15},{"path":843,"add":163,"del":15},{"hash":5375,"date":5345,"subject":5376,"files":5377,"plus":21,"minus":11},"9f5a0c4","add vim-style j\u002Fk and gg\u002FG page scrolling",[5378,5379,5380,5381],{"path":2066,"add":39,"del":11},{"path":5058,"add":132,"del":15},{"path":1671,"add":39,"del":15},{"path":843,"add":425,"del":15},{"hash":5383,"date":5345,"subject":5384,"files":5385,"plus":24,"minus":15},"523ed91","add humans.txt and RFC 9116 security.txt",[5386,5387,5389],{"path":438,"add":263,"del":15},{"path":5388,"add":72,"del":15},"public\u002F.well-known\u002Fsecurity.txt",{"path":5390,"add":175,"del":15},"public\u002Fhumans.txt",{"hash":5392,"date":5345,"subject":5393,"files":5394,"plus":61,"minus":43},"24bf431","genie lvOS window minimize toward its taskbar button and respect reduced motion",[5395,5396,5397],{"path":284,"add":506,"del":64},{"path":271,"add":11,"del":15},{"path":447,"add":72,"del":11},{"hash":5399,"date":5345,"subject":5400,"files":5401,"plus":2859,"minus":15},"41115a1","add copyable deep-link anchors to blog section headings",[5402,5403],{"path":564,"add":140,"del":15},{"path":438,"add":46,"del":15},{"hash":5405,"date":5345,"subject":5406,"files":5407,"plus":545,"minus":11},"4158856","highlight called-out code lines in blog posts via fence directive",[5408,5409,5411],{"path":564,"add":1141,"del":15},{"path":5410,"add":11,"del":11},"content\u002Fblog\u002Fgame-of-life-everywhere.md",{"path":438,"add":164,"del":15},{"hash":5413,"date":5345,"subject":5414,"files":5415,"plus":509,"minus":15},"a9fab1c","add cp and mv commands to the virtual filesystem",[5416],{"path":4096,"add":509,"del":15},{"hash":5418,"date":5345,"subject":5419,"files":5420,"plus":132,"minus":39},"ec7b527","add grep -n and -c flags to the terminal pipe filter",[5421,5422,5423],{"path":2443,"add":163,"del":39},{"path":937,"add":278,"del":15},{"path":3168,"add":46,"del":15},{"hash":5425,"date":5345,"subject":5426,"files":5427,"plus":940,"minus":72},"b37d54a","make About timeline commits expand to a git-show stack diff",[5428,5429,5430],{"path":508,"add":82,"del":72},{"path":484,"add":1498,"del":15},{"path":438,"add":75,"del":15},{"hash":5432,"date":5345,"subject":5433,"files":5434,"plus":1675,"minus":43},"33c8e49","support > and >> output redirection for any terminal command",[5435,5436,5437,5438,5439,5440],{"path":115,"add":120,"del":28},{"path":179,"add":461,"del":15},{"path":2443,"add":182,"del":15},{"path":937,"add":163,"del":15},{"path":181,"add":278,"del":11},{"path":3168,"add":302,"del":11},{"hash":5442,"date":5345,"subject":5443,"files":5444,"plus":40,"minus":11},"cace8cb","let the lvOS boot own the \u002Fdesktop entrance instead of the page wipe",[5445],{"path":641,"add":40,"del":11},{"hash":5447,"date":5345,"subject":5448,"files":5449,"plus":3292,"minus":72},"4888e77","add sort and uniq pipe filters to the terminal",[5450,5451,5452],{"path":2443,"add":107,"del":40},{"path":937,"add":164,"del":15},{"path":3168,"add":492,"del":40},{"hash":5454,"date":5345,"subject":5455,"files":5456,"plus":5463,"minus":4495},"3c3d777","extract a useLifeBoard composable shared by the hero, \u002Flife and lvOS Life",[5457,5458,5460,5462],{"path":1749,"add":75,"del":205},{"path":5459,"add":46,"del":290},"app\u002Fcomponents\u002Fdesktop\u002FDesktopLife.vue",{"path":5461,"add":2924,"del":15},"app\u002Fcomposables\u002FuseLifeBoard.ts",{"path":643,"add":120,"del":1866},204,{"hash":5465,"date":5345,"subject":5466,"files":5467,"plus":46,"minus":278},"3dd3881","lazy-load the matrix effect so its canvas code stays out of the initial bundle",[5468,5469],{"path":229,"add":263,"del":444},{"path":1671,"add":64,"del":11},{"hash":5471,"date":5345,"subject":5472,"files":5473,"plus":1120,"minus":39},"00e56b1","add a copyable RSS subscribe affordance to the blog",[5474,5475,5476],{"path":4295,"add":40,"del":11},{"path":651,"add":1119,"del":40},{"path":438,"add":263,"del":15},{"hash":5478,"date":5345,"subject":5479,"files":5480,"plus":24,"minus":11},"c66de1b","add arrow-key navigation and roving tabindex to the project filters",[5481,5482],{"path":1877,"add":492,"del":11},{"path":438,"add":448,"del":15},{"hash":5484,"date":5345,"subject":5485,"files":5486,"plus":122,"minus":223},"48f5e6c","add erase (shift\u002Fright-click) and zoom controls to \u002Flife",[5487,5488],{"path":643,"add":572,"del":223},{"path":438,"add":200,"del":15},{"hash":5490,"date":5345,"subject":5491,"files":5492,"plus":1498,"minus":572},"dd93fa1","extract a shared drawGrid helper for the Life canvases",[5493,5494,5495,5496],{"path":1749,"add":40,"del":164},{"path":5459,"add":40,"del":82},{"path":643,"add":40,"del":1141},{"path":4402,"add":61,"del":15},{"hash":5498,"date":5345,"subject":5499,"files":5500,"plus":1498,"minus":1496},"6d11114","refactor MatrixRain onto useCanvasScene and add matrix e2e coverage",[5501,5502,5503],{"path":229,"add":106,"del":735},{"path":4397,"add":72,"del":11},{"path":937,"add":164,"del":15},{"hash":5505,"date":5345,"subject":5506,"files":5507,"plus":2136,"minus":11},"aa224ce","generate OG images for \u002Flife and \u002Fdesktop and wire their meta tags",[5508,5509,5510,5511],{"path":641,"add":223,"del":15},{"path":643,"add":223,"del":11},{"path":438,"add":43,"del":15},{"path":631,"add":75,"del":15},{"hash":5513,"date":5345,"subject":5514,"files":5515,"plus":302,"minus":15},"6f62029","show match position\u002Fcount and key hints in ctrl+r reverse search",[5516,5517],{"path":222,"add":46,"del":15},{"path":937,"add":40,"del":15},{"hash":5519,"date":5345,"subject":5520,"files":5521,"plus":106,"minus":43},"cf40219","start \u002Flife and the lvOS Life app paused under reduced motion",[5522,5523,5524,5525],{"path":5459,"add":72,"del":40},{"path":4397,"add":263,"del":40},{"path":643,"add":164,"del":39},{"path":438,"add":448,"del":15},{"hash":5527,"date":5345,"subject":5528,"files":5529,"plus":3580,"minus":185},"cc872dc","add a shared Life pattern library (glider, LWSS, pulsar, pentadecathlon, acorn, gun)",[5530,5531,5532,5534],{"path":5459,"add":200,"del":15},{"path":643,"add":72,"del":185},{"path":5533,"add":2015,"del":15},"app\u002Futils\u002FlifePatterns.ts",{"path":5535,"add":25,"del":15},"tests\u002FlifePatterns.test.ts",{"hash":5537,"date":5345,"subject":5538,"files":5539,"plus":809,"minus":11},"4c0f362","add blog post: one Game of Life, four places",[5540,5541],{"path":5410,"add":3474,"del":15},{"path":2377,"add":40,"del":11},{"hash":5543,"date":5345,"subject":5544,"files":5545,"plus":5463,"minus":15},"7fb99bb","add a Game of Life app to lvOS reusing the shared engine",[5546,5547,5549],{"path":284,"add":39,"del":15},{"path":5459,"add":5548,"del":15},189,{"path":447,"add":75,"del":15},{"hash":5551,"date":5345,"subject":5552,"files":5553,"plus":3292,"minus":11},"b8b7865","add a life terminal command that plays Conway's Game of Life in ASCII",[5554,5555,5556],{"path":4278,"add":82,"del":11},{"path":978,"add":1477,"del":15},{"path":937,"add":263,"del":15},{"hash":5558,"date":5345,"subject":5559,"files":5560,"plus":1266,"minus":2669},"a4fda72","extract a useCanvasScene composable and adopt it in the hero and \u002Flife",[5561,5562,5563],{"path":1749,"add":197,"del":289},{"path":4397,"add":205,"del":15},{"path":643,"add":1154,"del":150},{"hash":5565,"date":5345,"subject":5566,"files":5567,"plus":819,"minus":2136},"b8cbc10","extract lvOS snap-zone math into a pure, unit-tested helper",[5568,5569,5570],{"path":288,"add":64,"del":2136},{"path":4058,"add":1498,"del":15},{"path":4061,"add":203,"del":15},{"hash":5572,"date":5345,"subject":5573,"files":5574,"plus":448,"minus":64},"4ccbf43","add \u002Flife, \u002Fdesktop and missing posts to the sitemap with lastmod",[5575],{"path":2377,"add":448,"del":64},{"hash":5577,"date":5345,"subject":5578,"files":5579,"plus":120,"minus":15},"59a7b04","add a Play Game of Life action to the command palette",[5580,5581],{"path":17,"add":164,"del":15},{"path":438,"add":164,"del":15},{"hash":5583,"date":5345,"subject":5584,"files":5585,"plus":5596,"minus":106},"52dd313","add a full-page playable Game of Life reachable by clicking the hero",[5586,5587,5589,5591,5592,5593,5594],{"path":1749,"add":29,"del":25},{"path":643,"add":5588,"del":15},340,{"path":5590,"add":61,"del":15},"app\u002Futils\u002FgameOfLife.ts",{"path":843,"add":82,"del":40},{"path":438,"add":545,"del":15},{"path":149,"add":11,"del":11},{"path":5595,"add":147,"del":15},"tests\u002FgameOfLife.test.ts",521,{"hash":5598,"date":5345,"subject":5599,"files":5600,"plus":731,"minus":40},"62284cd","add a >_ prompt mark to the navbar brand and an svg favicon",[5601,5602,5603,5604,5605],{"path":4295,"add":40,"del":11},{"path":248,"add":448,"del":11},{"path":843,"add":43,"del":15},{"path":149,"add":40,"del":15},{"path":5176,"add":28,"del":15},{"hash":5607,"date":5345,"subject":5608,"files":5609,"plus":1498,"minus":223},"c3eae73","extend link styling to footer\u002Fcontent links and animate see-all arrows",[5610,5611,5612],{"path":418,"add":200,"del":64},{"path":2353,"add":72,"del":72},{"path":843,"add":255,"del":15},{"hash":5614,"date":5345,"subject":5615,"files":5616,"plus":29,"minus":15},"36777ba","style prose links with an offset dotted underline that fades to solid on hover",[5617,5618],{"path":418,"add":150,"del":15},{"path":438,"add":164,"del":15},{"hash":5620,"date":5345,"subject":5621,"files":5622,"plus":3504,"minus":43},"627d0aa","make the status bar interactive: presence cycle, LF\u002FCRLF and language mode",[5623,5624],{"path":880,"add":2683,"del":43},{"path":843,"add":302,"del":15},{"hash":5626,"date":5345,"subject":5627,"files":5628,"plus":82,"minus":40},"48fd80d","use the visitor's identity name in the terminal and contact title bars",[5629,5630,5631],{"path":261,"add":39,"del":11},{"path":363,"add":40,"del":11},{"path":937,"add":39,"del":15},{"hash":5633,"date":5345,"subject":5634,"files":5635,"plus":531,"minus":75},"d05f66c","add a notification center with bell + unread badge to the lvOS taskbar",[5636,5637,5638,5639,5640],{"path":4295,"add":40,"del":11},{"path":284,"add":164,"del":40},{"path":271,"add":3580,"del":40},{"path":4399,"add":461,"del":43},{"path":447,"add":163,"del":15},{"hash":5642,"date":5345,"subject":5643,"files":5644,"plus":1481,"minus":200},"9bb96da","allow lvOS windows to resize from any edge or corner",[5645,5646,5647],{"path":284,"add":1496,"del":444},{"path":288,"add":461,"del":72},{"path":447,"add":302,"del":15},{"hash":5649,"date":5345,"subject":5650,"files":5651,"plus":2559,"minus":175},"72f0a1a","support nested directories and cd in the terminal filesystem",[5652,5653,5654,5655,5656,5657,5658],{"path":115,"add":28,"del":11},{"path":4190,"add":24,"del":448},{"path":179,"add":731,"del":15},{"path":4096,"add":572,"del":75},{"path":127,"add":40,"del":15},{"path":937,"add":302,"del":15},{"path":181,"add":750,"del":11},{"hash":5660,"date":5345,"subject":5661,"files":5662,"plus":61,"minus":1141},"9fddd0f","refresh \u002Fnow and \u002Fuses with current, richer content",[5663,5664,5665],{"path":505,"add":46,"del":43},{"path":479,"add":255,"del":164},{"path":438,"add":82,"del":15},{"hash":5667,"date":5345,"subject":5668,"files":5669,"plus":1491,"minus":15},"8bf31cf","add related-by-tag posts to the end of each blog article",[5670,5671],{"path":564,"add":204,"del":15},{"path":438,"add":164,"del":15},{"hash":5673,"date":5345,"subject":5674,"files":5675,"plus":2505,"minus":171},"294c468","add quadrant (corner) window snapping to lvOS with matching previews",[5676,5677],{"path":288,"add":61,"del":171},{"path":447,"add":255,"del":15},{"hash":5679,"date":5345,"subject":5680,"files":5681,"plus":791,"minus":182},"aa0bfac","add a persistent home filesystem to the terminal (mkdir, touch, echo>, cat, rm)",[5682,5683,5684,5685,5686,5687,5688],{"path":115,"add":82,"del":15},{"path":4190,"add":120,"del":64},{"path":179,"add":29,"del":15},{"path":4096,"add":1496,"del":164},{"path":127,"add":39,"del":15},{"path":937,"add":444,"del":15},{"path":181,"add":278,"del":15},{"hash":5690,"date":5345,"subject":5691,"files":5692,"plus":1177,"minus":263},"9818f04","redesign the What I Do cards as terminal-window panels",[5693,5694],{"path":2353,"add":2777,"del":263},{"path":438,"add":64,"del":15},{"hash":5696,"date":5345,"subject":5697,"files":5698,"plus":3234,"minus":3234},"a173aba","replace the hero constellation with an interactive game of life",[5699,5701,5703,5704,5705],{"path":5700,"add":15,"del":5463},"app\u002Fcomponents\u002FHeroConstellation.vue",{"path":1749,"add":5702,"del":15},199,{"path":2353,"add":11,"del":11},{"path":978,"add":11,"del":11},{"path":438,"add":43,"del":40},{"hash":5707,"date":5345,"subject":5708,"files":5709,"plus":11,"minus":1519},"8ec5945","Revert \"add hover-lift and press micro-interactions to project cards and links\"",[5710,5711,5712],{"path":951,"add":11,"del":182},{"path":2353,"add":15,"del":75},{"path":438,"add":15,"del":223},{"hash":5714,"date":5345,"subject":5715,"files":5716,"plus":1498,"minus":15},"04cf620","add claude file",[5717],{"path":470,"add":1498,"del":15},{"hash":5719,"date":5345,"subject":5720,"files":5721,"plus":15,"minus":15},"aafa00e","update resume",[5722],{"path":919,"add":15,"del":15},{"hash":5724,"date":5345,"subject":5725,"files":5726,"plus":64,"minus":82},"b2c2e66","improve navbar design",[5727],{"path":248,"add":64,"del":82},{"hash":5729,"date":5345,"subject":5730,"files":5731,"plus":204,"minus":15},"34b88fb","add blog post: giving my fake OS its own route",[5732],{"path":5733,"add":204,"del":15},"content\u002Fblog\u002Fan-os-on-its-own-route.md",{"hash":5735,"date":5345,"subject":5736,"files":5737,"plus":1519,"minus":11},"b8c68ef","add hover-lift and press micro-interactions to project cards and links",[5738,5739,5740],{"path":951,"add":182,"del":11},{"path":2353,"add":75,"del":15},{"path":438,"add":223,"del":15},{"hash":5742,"date":5345,"subject":5743,"files":5744,"plus":750,"minus":164},"bfc4e9e","match loading skeletons to final content and fade lists in to avoid pop\u002Fshift",[5745,5746],{"path":651,"add":444,"del":28},{"path":2353,"add":444,"del":72},{"hash":5748,"date":5345,"subject":5749,"files":5750,"plus":735,"minus":39},"74f387c","add a fullscreen toggle to the lvOS taskbar",[5751,5752,5753],{"path":4295,"add":39,"del":11},{"path":271,"add":25,"del":40},{"path":447,"add":223,"del":15},{"hash":5755,"date":5345,"subject":5756,"files":5757,"plus":75,"minus":15},"8d4110a","fix boot screen: reset Bulma pre styling so the logo has no pale panel",[5758,5759],{"path":771,"add":72,"del":15},{"path":447,"add":82,"del":15},{"hash":5761,"date":5345,"subject":5762,"files":5763,"plus":120,"minus":15},"8039790","route .vue formatting through eslint to stop format-on-save attribute churn",[5764,5766],{"path":5765,"add":39,"del":15},".vscode\u002Fextensions.json",{"path":5767,"add":182,"del":15},".vscode\u002Fsettings.json",{"hash":5769,"date":5345,"subject":5770,"files":5771,"plus":809,"minus":107},"4dc0857","extract lvOS toast and wallpaper logic into composables",[5772,5773,5774,5775],{"path":284,"add":72,"del":147},{"path":271,"add":11,"del":40},{"path":4399,"add":302,"del":15},{"path":3141,"add":107,"del":15},{"hash":5777,"date":5345,"subject":5778,"files":5779,"plus":352,"minus":43},"862202b","add taskbar hover previews and Aero-peek window highlighting to lvOS",[5780,5781,5782],{"path":284,"add":444,"del":11},{"path":271,"add":735,"del":64},{"path":447,"add":164,"del":15},{"hash":5784,"date":5345,"subject":5785,"files":5786,"plus":5794,"minus":5788},"019cf2d","split WebDesktop taskbar, icons and context menu into child components",[5787,5789,5790,5791,5793],{"path":284,"add":444,"del":5788},336,{"path":2876,"add":140,"del":15},{"path":746,"add":216,"del":15},{"path":271,"add":5792,"del":15},279,{"path":447,"add":175,"del":15},446,{"hash":5796,"date":5345,"subject":5797,"files":5798,"plus":205,"minus":3616,"truncated":72},"ac7aeda","make lvOS its own \u002Fdesktop page and drop the overlay + ?desktop launch",[5799,5800,5801,5802,5803,5804,5805,5806,5807,5808,5809,5810],{"path":418,"add":43,"del":15},{"path":1852,"add":11,"del":40},{"path":363,"add":72,"del":72},{"path":284,"add":182,"del":200},{"path":852,"add":40,"del":40},{"path":17,"add":40,"del":28},{"path":1669,"add":11,"del":40},{"path":115,"add":11,"del":40},{"path":1671,"add":40,"del":164},{"path":1673,"add":43,"del":15},{"path":641,"add":46,"del":15},{"path":5811,"add":15,"del":163},"app\u002Fplugins\u002Fdesktop-launch.client.ts",{"hash":5813,"date":5345,"subject":5814,"files":5815,"plus":4193,"minus":43},"7021b9e","enrich the 404 shell: nearest-page hint, quick links, tab-completion and history",[5816,5817,5819,5820],{"path":482,"add":947,"del":43},{"path":5818,"add":900,"del":15},"app\u002Futils\u002FnearestRoute.ts",{"path":438,"add":46,"del":15},{"path":5821,"add":2136,"del":15},"tests\u002FnearestRoute.test.ts",{"hash":5823,"date":5345,"subject":5824,"files":5825,"plus":2993,"minus":28},"7d6b812","upgrade command palette: match highlighting, frequency ranking, CRT + copy-link actions",[5826,5827,5828,5829],{"path":334,"add":46,"del":39},{"path":17,"add":830,"del":40},{"path":438,"add":263,"del":15},{"path":5830,"add":203,"del":15},"tests\u002Fpalette.test.ts",{"hash":5832,"date":5345,"subject":5833,"files":5834,"plus":425,"minus":72},"b5ff2bf","polish the hero: floating constellation and staggered intro",[5835],{"path":2353,"add":425,"del":72},{"hash":5837,"date":5345,"subject":5838,"files":5839,"plus":545,"minus":791},"065d3b5","replace the flow-field background with a subtle drifting dot-grid",[5840,5843],{"path":5841,"add":197,"del":5842},"app\u002Fcomponents\u002FFlowField.vue",183,{"path":978,"add":11,"del":11},{"hash":5845,"date":5345,"subject":5846,"files":5847,"plus":828,"minus":3535},"5d8d51d","replace the three.js hero with a cursor-reactive constellation and drop three.js",[5848,5849,5852,5853,5854,5855,5856],{"path":5700,"add":5463,"del":15},{"path":5850,"add":15,"del":5851},"app\u002Fcomponents\u002FHeroScene.vue",205,{"path":2353,"add":11,"del":40},{"path":978,"add":11,"del":11},{"path":438,"add":263,"del":15},{"path":60,"add":15,"del":2505},{"path":63,"add":15,"del":40},{"hash":5858,"date":5345,"subject":5859,"files":5860,"plus":1675,"minus":15},"aeadb60","add blog post: a window manager in a div",[5861,5863],{"path":5862,"add":2359,"del":15},"content\u002Fblog\u002Fa-window-manager-in-a-div.md",{"path":438,"add":164,"del":15},{"hash":5865,"date":5345,"subject":5866,"files":5867,"plus":5871,"minus":11},"3e4bb5c","add a persistent sticky-notes app to lvOS",[5868,5869,5870],{"path":284,"add":72,"del":11},{"path":1329,"add":32,"del":15},{"path":447,"add":120,"del":15},210,{"hash":5873,"date":5345,"subject":5874,"files":5875,"plus":3566,"minus":64},"12e48fc","add Aero-style snap preview and Alt+Tab window switching to lvOS",[5876,5877,5878],{"path":284,"add":203,"del":11},{"path":288,"add":750,"del":28},{"path":447,"add":731,"del":15},{"hash":5880,"date":5345,"subject":5881,"files":5882,"plus":4282,"minus":15},"c6fd97d","add unit tests for completion, ascii toys and identity sanitizing",[5883,5884,5886],{"path":3553,"add":61,"del":15},{"path":5885,"add":200,"del":15},"tests\u002Fidentity.test.ts",{"path":5887,"add":735,"del":15},"tests\u002FterminalToys.test.ts",{"hash":5889,"date":5345,"subject":5890,"files":5891,"plus":171,"minus":46},"6cea52b","share the minimark AST types and type the blog reading-time walk",[5892,5893,5894],{"path":564,"add":163,"del":163},{"path":5084,"add":28,"del":39},{"path":438,"add":39,"del":11},{"hash":5896,"date":5345,"subject":5897,"files":5898,"plus":809,"minus":107},"2b0125f","extract terminal completion and history persistence into pure modules",[5899,5900,5901],{"path":115,"add":43,"del":107},{"path":3551,"add":425,"del":15},{"path":5040,"add":731,"del":15},{"hash":5903,"date":5345,"subject":5904,"files":5905,"plus":289,"minus":164},"1ab1608","cycle through multiple candidates on repeated Tab in the terminal",[5906,5907,5908],{"path":222,"add":545,"del":40},{"path":115,"add":182,"del":43},{"path":937,"add":182,"del":15},{"hash":5910,"date":5345,"subject":5911,"files":5912,"plus":2391,"minus":15},"fb70d80","add cal, which and uname terminal commands",[5913,5914,5915,5916],{"path":1575,"add":506,"del":15},{"path":4096,"add":1154,"del":15},{"path":937,"add":163,"del":15},{"path":1578,"add":24,"del":15},{"hash":5918,"date":5345,"subject":5919,"files":5920,"plus":1496,"minus":72},"81f4a64","give route changes a terminal-style top-down repaint transition",[5921],{"path":418,"add":1496,"del":72},{"hash":5923,"date":5345,"subject":5924,"files":5925,"plus":1498,"minus":72},"2cd9236","add a reading-progress rail along the navbar bottom edge",[5926,5927],{"path":248,"add":2136,"del":72},{"path":843,"add":75,"del":15},{"hash":5929,"date":5345,"subject":5930,"files":5931,"plus":1119,"minus":72},"2ea2fc1","make the mobile menu an accessible dialog (role, focus trap, escape to close)",[5932,5933],{"path":248,"add":61,"del":72},{"path":843,"add":75,"del":15},{"hash":5935,"date":5345,"subject":5936,"files":5937,"plus":735,"minus":39},"2754410","fix mobile menu scroll lock so the close button stays reachable",[5938,5939],{"path":248,"add":731,"del":39},{"path":843,"add":120,"del":15},{"hash":5941,"date":5345,"subject":5942,"files":5943,"plus":444,"minus":82},"31931b9","sync $USER\u002F$HOME and pwd with the visitor identity and update e2e specs",[5944,5945,5946,5947],{"path":115,"add":43,"del":40},{"path":4096,"add":11,"del":11},{"path":447,"add":40,"del":40},{"path":937,"add":163,"del":39},{"hash":5949,"date":5345,"subject":5950,"files":5951,"plus":492,"minus":175},"74c624d","consolidate cv to a single real pdf download that works in dev and prod",[5952,5953,5954,5955,5956],{"path":347,"add":40,"del":39},{"path":2865,"add":163,"del":82},{"path":656,"add":223,"del":1141},{"path":919,"add":15,"del":15},{"path":199,"add":72,"del":11},{"hash":5958,"date":5345,"subject":5959,"files":5960,"plus":1109,"minus":82},"90d109f","give visitors an editable random identity used in the prompt, whoami and live cursors",[5961,5962,5963,5964,5965,5966,5967,5968,5969],{"path":2221,"add":28,"del":40},{"path":222,"add":40,"del":11},{"path":852,"add":187,"del":11},{"path":3393,"add":289,"del":15},{"path":115,"add":28,"del":15},{"path":4190,"add":1141,"del":40},{"path":4096,"add":40,"del":40},{"path":127,"add":72,"del":15},{"path":129,"add":28,"del":15},{"hash":5971,"date":5345,"subject":5972,"files":5973,"plus":15,"minus":64},"93d48c6","remove snake from the lvos desktop (it is a terminal game)",[5974],{"path":284,"add":15,"del":64},{"hash":5976,"date":5345,"subject":5977,"files":5978,"plus":43,"minus":43},"e47d9b5","correct role to head of development at nosana (co-founder only for effect.ai)",[5979,5980,5981],{"path":508,"add":72,"del":72},{"path":528,"add":40,"del":40},{"path":484,"add":11,"del":11},{"hash":5983,"date":5345,"subject":5984,"files":5985,"plus":187,"minus":185},"4fcab50","fix sticky navbar, make mobile menu a full-page overlay, and label branch as main",[5986,5987,5988,5989],{"path":54,"add":11,"del":11},{"path":418,"add":39,"del":11},{"path":248,"add":185,"del":572},{"path":880,"add":11,"del":11},{"hash":5991,"date":5345,"subject":5992,"files":5993,"plus":11,"minus":11},"05a1129","reset cursorsWs default to empty (was a local dev value)",[5994],{"path":149,"add":11,"del":11},{"hash":5996,"date":5345,"subject":5997,"files":5998,"plus":164,"minus":11},"e55d368","update verify skill with resume-pdf build step and e2e mime note",[5999],{"path":6000,"add":164,"del":11},".claude\u002Fskills\u002Fverify\u002FSKILL.md",{"hash":6002,"date":5345,"subject":6003,"files":6004,"plus":20,"minus":15},"7d5bb93","extend e2e coverage for navbar, shortcuts, desktop launchers, pwd, tree and desktop terminal",[6005,6006,6007,6008],{"path":447,"add":223,"del":15},{"path":843,"add":1477,"del":15},{"path":937,"add":731,"del":15},{"path":3867,"add":11,"del":15},{"hash":6010,"date":5345,"subject":6011,"files":6012,"plus":3355,"minus":28},"88c679f","generate a downloadable pdf resume at build and link it from cv and palette",[6013,6014,6015,6016,6017,6018,6019],{"path":54,"add":39,"del":11},{"path":347,"add":72,"del":15},{"path":418,"add":40,"del":15},{"path":17,"add":75,"del":15},{"path":656,"add":223,"del":72},{"path":63,"add":40,"del":15},{"path":199,"add":14,"del":15},{"hash":6021,"date":5345,"subject":6022,"files":6023,"plus":2978,"minus":15},"1a0eb4d","add a keyboard shortcuts cheatsheet overlay triggered by ?",[6024,6025],{"path":2066,"add":2432,"del":15},{"path":1671,"add":11,"del":15},{"hash":6027,"date":5345,"subject":6028,"files":6029,"plus":6034,"minus":492},"0002665","add sticky navbar divider, full-screen mobile menu, palette search and more desktop launchers",[6030,6031,6032,6033],{"path":1852,"add":39,"del":15},{"path":4295,"add":40,"del":11},{"path":248,"add":3234,"del":175},{"path":5811,"add":163,"del":15},226,{"hash":6036,"date":5345,"subject":6037,"files":6038,"plus":1519,"minus":15},"f4a73f0","add tree command rendering the site as a directory tree",[6039],{"path":4190,"add":1519,"del":15},{"hash":6041,"date":5345,"subject":6042,"files":6043,"plus":6051,"minus":975},"21b7161","add pwd, cwd-aware prompt, ctrl+r search, and run the terminal as an lvos window",[6044,6045,6046,6047,6048,6049,6050],{"path":222,"add":3535,"del":15},{"path":363,"add":82,"del":3260},{"path":284,"add":175,"del":28},{"path":4129,"add":175,"del":15},{"path":115,"add":164,"del":11},{"path":4096,"add":72,"del":15},{"path":127,"add":40,"del":15},354,{"hash":6053,"date":6054,"subject":6055,"files":6056,"plus":182,"minus":15},"34e4e92","2026-07-07","point verify skill at the new e2e suite",[6057],{"path":6000,"add":182,"del":15},{"hash":6059,"date":6054,"subject":6060,"files":6061,"plus":6071,"minus":15},"eb2c5f8","add playwright e2e suite for terminal, lvos and pages, wired into ci",[6062,6063,6064,6065,6066,6067,6068,6069,6070],{"path":54,"add":28,"del":15},{"path":347,"add":28,"del":15},{"path":447,"add":203,"del":15},{"path":438,"add":150,"del":15},{"path":937,"add":29,"del":15},{"path":60,"add":480,"del":15},{"path":63,"add":40,"del":15},{"path":463,"add":545,"del":15},{"path":3867,"add":29,"del":15},322,{"hash":6073,"date":6054,"subject":6074,"files":6075,"plus":3267,"minus":28},"d064a9c","generate per-page og images at build time and add json-ld structured data",[6076,6077,6078,6079,6080,6081,6082,6083],{"path":347,"add":39,"del":15},{"path":639,"add":448,"del":15},{"path":564,"add":278,"del":11},{"path":2353,"add":182,"del":15},{"path":566,"add":461,"del":11},{"path":149,"add":39,"del":39},{"path":63,"add":40,"del":15},{"path":631,"add":3884,"del":15},{"hash":6085,"date":6054,"subject":6086,"files":6087,"plus":186,"minus":150},"0c6d8d4","extract pure terminal pipeline helpers and add unit tests for pipes, aliases and env",[6088,6089,6090,6092],{"path":115,"add":82,"del":150},{"path":2443,"add":2683,"del":15},{"path":6091,"add":444,"del":15},"tests\u002FterminalMarkdown.test.ts",{"path":3168,"add":21,"del":15},{"hash":6094,"date":6054,"subject":6095,"files":6096,"plus":4197,"minus":11},"2c7e573","add an idle screensaver with warp starfield and bouncing clock to lvos",[6097,6098],{"path":284,"add":46,"del":11},{"path":235,"add":5112,"del":15},{"hash":6100,"date":6054,"subject":6101,"files":6102,"plus":6104,"minus":82},"66db282","add lvos toast notifications and a desktop right-click context menu",[6103],{"path":284,"add":6104,"del":82},144,{"hash":6106,"date":6054,"subject":6107,"files":6108,"plus":6113,"minus":11},"83e5f58","add calculator and clock+timer apps to lvos",[6109,6110,6111],{"path":284,"add":43,"del":11},{"path":1450,"add":4839,"del":15},{"path":6112,"add":5463,"del":15},"app\u002Fcomponents\u002Fdesktop\u002FDesktopClock.vue",378,{"hash":6115,"date":6054,"subject":6116,"files":6117,"plus":3775,"minus":40},"a834c31","add top, ping and curl terminal toys",[6118,6119],{"path":4278,"add":2505,"del":40},{"path":978,"add":132,"del":15},{"hash":6121,"date":6054,"subject":6122,"files":6123,"plus":3912,"minus":164},"ba70221","add related projects, image lightbox and package.json card to project detail",[6124],{"path":566,"add":3912,"del":164},{"hash":6126,"date":6054,"subject":6127,"files":6128,"plus":2622,"minus":46},"54d5ae3","command palette v2: recent actions, blog posts and accent switches",[6129,6130],{"path":334,"add":61,"del":182},{"path":17,"add":150,"del":40},{"hash":6132,"date":6054,"subject":6133,"files":6134,"plus":2485,"minus":15},"544c39f","add runtime accent color picker via colorscheme command and settings",[6135,6136,6137,6138,6140,6141],{"path":852,"add":185,"del":15},{"path":2413,"add":809,"del":15},{"path":115,"add":64,"del":15},{"path":6139,"add":72,"del":15},"app\u002Fplugins\u002Faccent.client.ts",{"path":4096,"add":1141,"del":15},{"path":127,"add":28,"del":15},{"hash":6143,"date":6054,"subject":6144,"files":6145,"plus":2965,"minus":107},"e266122","extract reusable TuiFrame component and dedupe the corner-bracket frames",[6146,6147,6149,6150],{"path":261,"add":11,"del":1141},{"path":6148,"add":3616,"del":15},"app\u002Fcomponents\u002FTuiFrame.vue",{"path":482,"add":11,"del":448},{"path":656,"add":11,"del":448},{"hash":6152,"date":6054,"subject":6153,"files":6154,"plus":64,"minus":11},"fb2970c","note lazy-mount and error-page verification gotchas in verify skill",[6155],{"path":6000,"add":64,"del":11},{"hash":6157,"date":6054,"subject":6158,"files":6159,"plus":72,"minus":40},"e8ce7a6","fix lvos boot screen skipped when the desktop is lazy-mounted",[6160],{"path":284,"add":72,"del":40},{"hash":6162,"date":6054,"subject":6163,"files":6164,"plus":2683,"minus":164},"eb1f844","accessibility pass: focus rings, skip link, aria roles\u002Flabels and reduced-motion safety net",[6165,6166,6167,6168,6169,6170],{"path":418,"add":506,"del":15},{"path":334,"add":40,"del":11},{"path":229,"add":11,"del":11},{"path":363,"add":11,"del":15},{"path":284,"add":200,"del":64},{"path":1671,"add":40,"del":11},{"hash":6172,"date":6054,"subject":6173,"files":6174,"plus":197,"minus":75},"e5b56d5","lazy-load the three.js hero and lvos apps into on-demand chunks",[6175,6176,6177],{"path":284,"add":263,"del":223},{"path":1671,"add":75,"del":11},{"path":2353,"add":40,"del":11},{"hash":6179,"date":6054,"subject":6180,"files":6181,"plus":2583,"minus":289},"266280b","extract shared chiptune engine and add a multi-mode lvos visualizer app",[6182,6183,6184,6185],{"path":284,"add":72,"del":11},{"path":2175,"add":263,"del":140},{"path":3645,"add":3646,"del":15},{"path":2681,"add":289,"del":15},{"hash":6187,"date":6054,"subject":6188,"files":6189,"plus":6192,"minus":72},"a732c6a","add lvos bios boot sequence, wallpaper switcher and taskbar calendar",[6190,6191],{"path":284,"add":4197,"del":72},{"path":771,"add":3355,"del":15},272,{"hash":6194,"date":6054,"subject":6195,"files":6196,"plus":4070,"minus":1141},"b4539e8","restyle now as a ps process monitor and frame the cv sheet with tui accents",[6197,6198],{"path":656,"add":197,"del":39},{"path":513,"add":3860,"del":163},{"hash":6200,"date":6054,"subject":6201,"files":6202,"plus":758,"minus":11},"32923a5","add a crosshair cursor overlay with live coordinate readout on the hero",[6203,6204],{"path":254,"add":546,"del":15},{"path":2353,"add":448,"del":11},{"hash":6206,"date":6054,"subject":6207,"files":6208,"plus":1107,"minus":223},"e83f677","render the about skills section as an npm ls dependency tree",[6209],{"path":484,"add":1107,"del":223},{"hash":6211,"date":6054,"subject":6212,"files":6213,"plus":6215,"minus":263},"30f006c","turn the 404 page into an interactive recovery shell",[6214],{"path":482,"add":6215,"del":263},186,{"hash":6217,"date":6054,"subject":6218,"files":6219,"plus":3643,"minus":39},"fb9ea09","add man pages, aliases, env vars and variable expansion to the terminal",[6220,6221,6222,6223],{"path":115,"add":278,"del":11},{"path":4190,"add":40,"del":15},{"path":4096,"add":3504,"del":11},{"path":127,"add":43,"del":11},{"hash":6225,"date":6054,"subject":6226,"files":6227,"plus":2667,"minus":187},"eef3bbb","extract shared high-score, ticker and quit-key helpers into a game kit",[6228,6229],{"path":2215,"add":830,"del":15},{"path":978,"add":278,"del":187},{"hash":6231,"date":6054,"subject":6232,"files":6233,"plus":6241,"minus":572},"9183030","add vitest unit tests for terminal markdown and games, wire into ci",[6234,6235,6237,6238,6239,6240],{"path":54,"add":11,"del":15},{"path":60,"add":6236,"del":572},331,{"path":63,"add":40,"del":15},{"path":4494,"add":3355,"del":15},{"path":6091,"add":216,"del":15},{"path":4536,"add":182,"del":15},520,{"hash":6243,"date":6054,"subject":6244,"files":6245,"plus":735,"minus":72},"1b9df70","align contact wizard with tui design: corner frame, step counter, quick confirm",[6246],{"path":261,"add":735,"del":72},{"hash":6248,"date":6054,"subject":6249,"files":6250,"plus":21,"minus":197},"fec1895","render the about timeline as a git log graph with commit hashes",[6251],{"path":484,"add":21,"del":197},{"hash":6253,"date":6054,"subject":6254,"files":6255,"plus":3882,"minus":545},"6583f58","add reading progress, sticky toc and code copy buttons to blog posts",[6256],{"path":564,"add":3882,"del":545},{"hash":6258,"date":6054,"subject":6259,"files":6260,"plus":2359,"minus":163},"2c69526","restyle project filters as command-line flags with counts",[6261],{"path":1877,"add":2359,"del":163},{"hash":6263,"date":6054,"subject":6264,"files":6265,"plus":6273,"minus":40},"e938ac9","add vim editor, settings and paint apps to lvos",[6266,6267,6268,6269,6270,6271],{"path":4295,"add":40,"del":11},{"path":284,"add":223,"del":11},{"path":170,"add":11,"del":15},{"path":1094,"add":125,"del":15},{"path":852,"add":2389,"del":15},{"path":1167,"add":6272,"del":15},261,549,{"hash":6275,"date":6054,"subject":6276,"files":6277,"plus":6280,"minus":667},"59077d7","extract lvos window manager composable with resize, maximize, edge snapping and session layout",[6278,6279],{"path":284,"add":1266,"del":667},{"path":288,"add":3296,"del":15},289,{"hash":6282,"date":6054,"subject":6283,"files":6284,"plus":6287,"minus":11},"e198f97","add tetris and 2048 to the terminal game engine",[6285,6286],{"path":4278,"add":182,"del":11},{"path":978,"add":3185,"del":15},300,{"hash":6289,"date":6054,"subject":6290,"files":6291,"plus":2642,"minus":444},"1fdbd21","add terminal pipes, argument tab completion, persistent history and ctrl+l",[6292,6293,6294,6295,6296],{"path":363,"add":64,"del":11},{"path":115,"add":1675,"del":223},{"path":4190,"add":1141,"del":223},{"path":4096,"add":39,"del":11},{"path":127,"add":40,"del":11},{"hash":6298,"date":6054,"subject":6299,"files":6300,"plus":3643,"minus":4070,"truncated":75},"2acbda8","introduce lv hsl design tokens and replace repeated bulma hsla incantations",[6301,6302,6303,6304,6305,6306,6307,6308,6309,6310,6311,6312],{"path":418,"add":263,"del":39},{"path":248,"add":11,"del":72},{"path":880,"add":40,"del":28},{"path":769,"add":39,"del":39},{"path":2865,"add":28,"del":28},{"path":334,"add":39,"del":39},{"path":261,"add":164,"del":164},{"path":742,"add":164,"del":164},{"path":4385,"add":11,"del":11},{"path":951,"add":72,"del":72},{"path":4679,"add":11,"del":11},{"path":363,"add":163,"del":163},{"hash":6314,"date":6054,"subject":6315,"files":6316,"plus":6322,"minus":518},"1d3f043","split terminal command registry into per-domain modules behind a context api",[6317,6318,6319,6320,6321],{"path":115,"add":106,"del":518},{"path":4190,"add":3882,"del":15},{"path":4278,"add":2622,"del":15},{"path":4096,"add":2485,"del":15},{"path":127,"add":216,"del":15},567,{"hash":6324,"date":6054,"subject":6325,"files":6326,"plus":2136,"minus":15},"2f937f7","add project verify skill with headless browser recipe",[6327],{"path":6000,"add":2136,"del":15},{"hash":6329,"date":6054,"subject":6330,"files":6331,"plus":2440,"minus":39},"d7dfe7a","add lvos blog reader app so posts open in the desktop instead of navigating",[6332,6333,6334],{"path":284,"add":302,"del":40},{"path":3523,"add":2368,"del":15},{"path":170,"add":40,"del":11},{"hash":6336,"date":6054,"subject":6337,"files":6338,"plus":6341,"minus":64},"68b0aae","render blog posts as markdown inside the terminal via blog and cat commands",[6339,6340],{"path":115,"add":1579,"del":64},{"path":5084,"add":968,"del":15},180,{"hash":6343,"date":6054,"subject":6344,"files":6345,"plus":1085,"minus":492},"143937a","add icons and package-list styling with hover health checks to uses page",[6346,6347,6348],{"path":4295,"add":163,"del":11},{"path":479,"add":46,"del":182},{"path":488,"add":3355,"del":163},{"hash":6350,"date":6054,"subject":6351,"files":6352,"plus":5842,"minus":3616},"96b122b","redesign project cards with corner brackets, scanline sweep and glitch hover",[6353],{"path":951,"add":5842,"del":3616},{"hash":6355,"date":6054,"subject":6356,"files":6357,"plus":6369,"minus":546},"c500715","replace bulma buttons with terminal-style cmd buttons and drop magnetic hover",[6358,6359,6360,6362,6363,6364,6365,6366,6367],{"path":2865,"add":5342,"del":15},{"path":4965,"add":2136,"del":11},{"path":6361,"add":15,"del":106},"app\u002Fcomposables\u002FuseMagnetic.ts",{"path":482,"add":11,"del":11},{"path":484,"add":28,"del":28},{"path":656,"add":72,"del":72},{"path":2353,"add":223,"del":75},{"path":566,"add":164,"del":171},{"path":6368,"add":15,"del":28},"app\u002Fplugins\u002Fdirectives.ts",243,{"hash":6371,"date":6054,"subject":6372,"files":6373,"plus":290,"minus":39},"a95271f","give the navbar brand a tilde-expansion typing effect with git segment",[6374],{"path":248,"add":290,"del":39},{"hash":6376,"date":6054,"subject":6377,"files":6378,"plus":2440,"minus":40},"6141c10","add config-gated live visitor cursors with bundled websocket relay",[6379,6380,6381,6382,6383,6384],{"path":10,"add":223,"del":15},{"path":2221,"add":3775,"del":15},{"path":1671,"add":11,"del":15},{"path":60,"add":40,"del":11},{"path":63,"add":40,"del":11},{"path":129,"add":289,"del":15},{"hash":6386,"date":6054,"subject":6387,"files":6388,"plus":289,"minus":15},"83f4369","add config-gated goatcounter analytics with terminal command events",[6389,6390,6391,6393],{"path":4520,"add":200,"del":15},{"path":115,"add":72,"del":15},{"path":6392,"add":1141,"del":15},"app\u002Fplugins\u002Fanalytics.client.ts",{"path":149,"add":164,"del":15},{"hash":6395,"date":6054,"subject":6396,"files":6397,"plus":6406,"minus":1119},"7fa5543","add pwa support with offline caching and install manifest",[6398,6399,6401,6402,6404],{"path":149,"add":175,"del":11},{"path":60,"add":6400,"del":289},3483,{"path":63,"add":11,"del":15},{"path":6403,"add":15,"del":15},"public\u002Fpwa-192.png",{"path":6405,"add":15,"del":15},"public\u002Fpwa-512.png",3512,{"hash":6408,"date":6054,"subject":6409,"files":6410,"plus":6416,"minus":39},"7127cfb","add minesweeper, media player, file explorer and browser apps to lvos",[6411,6412,6413,6414,6415],{"path":284,"add":509,"del":39},{"path":474,"add":14,"del":15},{"path":170,"add":3775,"del":15},{"path":2175,"add":2391,"del":15},{"path":2187,"add":975,"del":15},543,{"hash":6418,"date":6054,"subject":6419,"files":6420,"plus":203,"minus":72},"0915d8a","enable view transitions and add magnetic hover to primary buttons",[6421,6422,6423,6424,6425],{"path":6361,"add":106,"del":15},{"path":484,"add":40,"del":40},{"path":2353,"add":40,"del":40},{"path":6368,"add":28,"del":15},{"path":149,"add":28,"del":15},{"hash":6427,"date":6054,"subject":6428,"files":6429,"plus":46,"minus":163},"646130f","self-host project thumbnails and fix lazy images never loading while hidden",[6430,6431,6432,6434,6436,6438,6440,6441,6443,6445,6446],{"path":951,"add":43,"del":11},{"path":528,"add":223,"del":75},{"path":6433,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fautomated-web-design.png",{"path":6435,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fdetect-malicious-websites.png",{"path":6437,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Feffect-network.png",{"path":6439,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fhangman.png",{"path":1806,"add":15,"del":15},{"path":6442,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fkids-age.png",{"path":6444,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fnosana.png",{"path":1812,"add":15,"del":15},{"path":6447,"add":15,"del":15},"public\u002Fimg\u002Fprojects\u002Fself-coding-website.png",{"hash":6449,"date":6054,"subject":6450,"files":6451,"plus":3117,"minus":64},"ec23a7a","add \u002Fnow page with now terminal command",[6452,6453,6454,6455,6456,6457],{"path":17,"add":11,"del":15},{"path":115,"add":75,"del":11},{"path":505,"add":492,"del":15},{"path":484,"add":43,"del":72},{"path":513,"add":1496,"del":15},{"path":2377,"add":11,"del":11},{"hash":6459,"date":6054,"subject":6460,"files":6461,"plus":2777,"minus":39},"0a9a953","add rss feed, reading time and prev\u002Fnext post navigation",[6462,6463,6464,6465],{"path":564,"add":107,"del":40},{"path":651,"add":11,"del":15},{"path":149,"add":11,"del":11},{"path":633,"add":572,"del":15},{"hash":6467,"date":6054,"subject":6468,"files":6469,"plus":2368,"minus":15},"6bdcb75","add konami code party mode with confetti",[6470,6471,6472,6473],{"path":418,"add":46,"del":15},{"path":4385,"add":1085,"del":15},{"path":115,"add":263,"del":15},{"path":1671,"add":11,"del":15},{"hash":6475,"date":6054,"subject":6476,"files":6477,"plus":709,"minus":185},"1e3edc0","turn contact page into interactive contact.sh terminal wizard",[6478,6479],{"path":261,"add":3766,"del":15},{"path":27,"add":175,"del":185},{"hash":6481,"date":6054,"subject":6482,"files":6483,"plus":425,"minus":28},"e9a8184","add reboot command to replay the boot sequence",[6484,6485],{"path":769,"add":255,"del":28},{"path":115,"add":263,"del":15},{"hash":6487,"date":6054,"subject":6488,"files":6489,"plus":3670,"minus":15},"1382825","add latest writing section to homepage",[6490],{"path":2353,"add":3670,"del":15},{"hash":6492,"date":6054,"subject":6493,"files":6494,"plus":163,"minus":40},"18a1c94","lock nav link width during scramble to prevent layout shift",[6495],{"path":248,"add":163,"del":40},{"hash":6497,"date":6054,"subject":6498,"files":6499,"plus":40,"minus":28},"a28e1bd","remove external site mentions from readme, lvos and uses page",[6500,6501,6502],{"path":10,"add":11,"del":11},{"path":284,"add":11,"del":39},{"path":488,"add":15,"del":11},{"hash":6504,"date":6054,"subject":6505,"files":6506,"plus":6508,"minus":2569},"c7eadb2","replace navbar with minimal mono links and scramble hover effect",[6507],{"path":248,"add":6508,"del":2569},142,{"hash":6510,"date":6054,"subject":6511,"files":6512,"plus":3267,"minus":2368},"c9eaca0","replace particle network background with interactive flow field",[6513,6514,6515,6516,6518,6519],{"path":418,"add":11,"del":11},{"path":769,"add":11,"del":11},{"path":5841,"add":2570,"del":15},{"path":6517,"add":15,"del":3646},"app\u002Fcomponents\u002FParticleNetwork.vue",{"path":528,"add":11,"del":11},{"path":1671,"add":11,"del":11},{"hash":6521,"date":6054,"subject":6522,"files":6523,"plus":43,"minus":39},"503f409","update readme with blog, lvos desktop and ide-style features",[6524],{"path":10,"add":43,"del":39},{"hash":6526,"date":6054,"subject":6527,"files":6528,"plus":6536,"minus":28},"98d4fc7","add lvos desktop easter egg with draggable windows and taskbar",[6529,6530,6532,6533,6534,6535],{"path":363,"add":46,"del":39},{"path":284,"add":6531,"del":15},457,{"path":17,"add":163,"del":15},{"path":1669,"add":40,"del":11},{"path":115,"add":46,"del":11},{"path":1671,"add":11,"del":15},507,{"hash":6538,"date":6054,"subject":6539,"files":6540,"plus":3474,"minus":163},"ac1b311","make hero icosahedron interactive with drag rotation and click pulse",[6541],{"path":5850,"add":3474,"del":163},{"hash":6543,"date":6054,"subject":6544,"files":6545,"plus":6369,"minus":40},"b6e29bf","add ide-style status bar, comment nav and laurens.ts code window",[6546,6547,6548,6549,6550,6551],{"path":248,"add":182,"del":40},{"path":880,"add":3643,"del":15},{"path":1686,"add":204,"del":15},{"path":1671,"add":39,"del":15},{"path":484,"add":75,"del":15},{"path":2353,"add":448,"del":15},{"hash":6553,"date":6054,"subject":6554,"files":6555,"plus":6570,"minus":5463},"4e3eb3c","add blog section powered by nuxt content with two posts",[6556,6557,6558,6559,6560,6561,6562,6563,6565,6566,6568,6569],{"path":248,"add":11,"del":15},{"path":17,"add":11,"del":15},{"path":115,"add":28,"del":11},{"path":564,"add":1579,"del":15},{"path":651,"add":1481,"del":15},{"path":2487,"add":182,"del":15},{"path":390,"add":61,"del":15},{"path":6564,"add":216,"del":15},"content\u002Fblog\u002Fsnake-in-the-terminal.md",{"path":149,"add":1141,"del":11},{"path":60,"add":6567,"del":3267},3652,{"path":63,"add":40,"del":15},{"path":2377,"add":82,"del":40},3958,{"hash":6572,"date":6054,"subject":6573,"files":6574,"plus":29,"minus":40},"34ba6af","add browser console easter egg and hidden secrets command",[6575,6576,6577],{"path":10,"add":448,"del":40},{"path":115,"add":1141,"del":15},{"path":1632,"add":492,"del":15},{"hash":6579,"date":6054,"subject":6580,"files":6581,"plus":3267,"minus":15},"4f608d0","add cowsay, figlet, fortune and sl train terminal toys",[6582,6583,6584,6585],{"path":4679,"add":150,"del":15},{"path":115,"add":197,"del":15},{"path":1671,"add":11,"del":15},{"path":6586,"add":3580,"del":15},"app\u002Futils\u002FterminalToys.ts",{"hash":6588,"date":6054,"subject":6589,"files":6590,"plus":2570,"minus":15},"1ab012d","add matrix rain and crt mode terminal easter eggs",[6591,6592,6593,6594,6595],{"path":418,"add":203,"del":15},{"path":229,"add":819,"del":15},{"path":1669,"add":182,"del":15},{"path":115,"add":302,"del":15},{"path":1671,"add":11,"del":15},{"hash":6597,"date":6054,"subject":6598,"files":6599,"plus":6603,"minus":448},"de11680","add playable snake and hangman games to the terminal",[6600,6601,6602],{"path":363,"add":278,"del":75},{"path":115,"add":1519,"del":40},{"path":978,"add":3650,"del":15},274,{"hash":6605,"date":6054,"subject":6606,"files":6607,"plus":3355,"minus":15},"c3b2ddc","add first-visit boot sequence splash",[6608,6609],{"path":769,"add":819,"del":15},{"path":1671,"add":11,"del":15},{"hash":6611,"date":6054,"subject":6612,"files":6613,"plus":6618,"minus":15},"01ade10","add three.js wireframe icosahedron to hero with mouse parallax",[6614,6615,6616,6617],{"path":5850,"add":5152,"del":15},{"path":2353,"add":164,"del":15},{"path":60,"add":2505,"del":15},{"path":63,"add":40,"del":15},228,{"hash":6620,"date":6054,"subject":6621,"files":6622,"plus":118,"minus":425},"310169f","add scroll reveal animations and page transitions",[6623,6624,6626,6628,6629,6630],{"path":418,"add":106,"del":15},{"path":6625,"add":46,"del":15},"app\u002Fcomponents\u002FRevealBlock.vue",{"path":6627,"add":492,"del":15},"app\u002Fcomposables\u002FuseScrollReveal.ts",{"path":484,"add":1141,"del":75},{"path":2353,"add":425,"del":302},{"path":149,"add":11,"del":15},{"hash":6632,"date":6054,"subject":6633,"files":6634,"plus":6639,"minus":15},"c847fda","add ctrl+k command palette with fuzzy search",[6635,6637,6638],{"path":334,"add":6636,"del":15},265,{"path":17,"add":20,"del":15},{"path":1671,"add":11,"del":15},370,{"hash":6641,"date":6054,"subject":6642,"files":6643,"plus":3912,"minus":11},"9a8a255","add live github stats with star badges and github terminal command",[6644,6645,6646,6647,6648],{"path":949,"add":29,"del":15},{"path":951,"add":1141,"del":11},{"path":953,"add":140,"del":15},{"path":115,"add":1141,"del":15},{"path":484,"add":39,"del":15},{"hash":6650,"date":6054,"subject":6651,"files":6652,"plus":828,"minus":39},"47a0754","add printable \u002Fcv page with cv terminal command and print theming",[6653,6654,6655,6656,6657],{"path":418,"add":164,"del":15},{"path":115,"add":28,"del":11},{"path":484,"add":43,"del":11},{"path":656,"add":186,"del":15},{"path":2377,"add":11,"del":11},{"hash":6659,"date":6054,"subject":6660,"files":6661,"plus":2391,"minus":40},"ec191fb","add \u002Fuses gear page with uses terminal command",[6662,6663,6664,6665,6666],{"path":248,"add":11,"del":15},{"path":115,"add":302,"del":11},{"path":479,"add":150,"del":15},{"path":488,"add":29,"del":15},{"path":2377,"add":11,"del":11},{"hash":6668,"date":6054,"subject":6669,"files":6670,"plus":6603,"minus":163},"2621f67","add project detail pages with stories, cat command and sitemap entries",[6671,6672,6673,6674,6675,6677],{"path":951,"add":1141,"del":39},{"path":115,"add":545,"del":15},{"path":528,"add":809,"del":164},{"path":566,"add":489,"del":15},{"path":6676,"add":15,"del":15},"app\u002Fpages\u002F{projects.vue => projects\u002Findex.vue}",{"path":2377,"add":72,"del":11},{"hash":6679,"date":6054,"subject":6680,"files":6681,"plus":6695,"minus":6696},"0a20242","add eslint + typecheck ci, og meta\u002Fimage, sitemap and robots.txt",[6682,6683,6685,6686,6689,6690,6692,6694],{"path":54,"add":40,"del":15},{"path":6684,"add":164,"del":15},"eslint.config.mjs",{"path":149,"add":255,"del":40},{"path":60,"add":6687,"del":6688},4593,1475,{"path":63,"add":64,"del":11},{"path":6691,"add":15,"del":15},"public\u002Fog.png",{"path":6693,"add":72,"del":15},"public\u002Frobots.txt",{"path":2377,"add":163,"del":15},4647,1478,{"hash":6698,"date":6054,"subject":6699,"files":6700,"plus":6713,"minus":6714,"truncated":147},"544847b","rebuild site with nuxt 4, typescript, bulma 1 and interactive terminal mode",[6701,6702,6703,6704,6705,6706,6707,6708,6709,6710,6711,6712],{"path":54,"add":82,"del":182},{"path":347,"add":448,"del":1177},{"path":10,"add":120,"del":75},{"path":416,"add":28,"del":15},{"path":418,"add":3292,"del":15},{"path":1852,"add":29,"del":15},{"path":4295,"add":900,"del":15},{"path":248,"add":4070,"del":15},{"path":742,"add":3860,"del":15},{"path":6517,"add":3646,"del":15},{"path":951,"add":186,"del":15},{"path":1877,"add":2683,"del":15},10952,29503,{"hash":6716,"date":6717,"subject":6718,"files":6719,"plus":163,"minus":39},"2e74b07","2022-10-04","add nosana as project",[6720],{"path":6721,"add":163,"del":39},"components\u002FProjects.vue",{"hash":6723,"date":6724,"subject":6725,"files":6726,"plus":255,"minus":15},"d5073cf","2022-10-03","add contact page",[6727],{"path":6728,"add":255,"del":15},"pages\u002Fcontact.vue",{"hash":6730,"date":6731,"subject":6732,"files":6733,"plus":6739,"minus":6740},"e65b007","2022-09-28","update to node 16",[6734,6735,6738],{"path":54,"add":11,"del":11},{"path":60,"add":6736,"del":6737},19444,4275,{"path":63,"add":40,"del":40},19447,4278,{"hash":6742,"date":6743,"subject":6744,"files":6745,"plus":2965,"minus":444},"90d9031","2022-03-31","add projects",[6746,6748,6750,6751,6753],{"path":6747,"add":163,"del":15},"assets\u002Fscss\u002Fglobal.scss",{"path":6749,"add":11,"del":11},"assets\u002Fscss\u002Fvariables.scss",{"path":6721,"add":24,"del":461},{"path":6752,"add":15,"del":15},"pages\u002F{blockchain.vue => about.vue}",{"path":6754,"add":171,"del":15},"pages\u002Fprojects.vue",{"hash":6756,"date":6757,"subject":6758,"files":6759,"plus":731,"minus":43},"831a5dd","2022-03-11","change thumbnail on hover",[6760],{"path":6721,"add":731,"del":43},{"hash":6762,"date":6757,"subject":6763,"files":6764,"plus":3670,"minus":205},"88581bd","make all project cards same height",[6765,6766],{"path":6721,"add":290,"del":15},{"path":6767,"add":28,"del":205},"pages\u002Findex.vue",{"hash":6769,"date":6757,"subject":6770,"files":6771,"plus":1141,"minus":15},"47b7273","update packages",[6772],{"path":60,"add":1141,"del":15},{"hash":6774,"date":6775,"subject":6776,"files":6777,"plus":15,"minus":15},"c59e5d0","2021-03-11","changed grid image?",[6778],{"path":6779,"add":15,"del":15},"assets\u002Fimg\u002Fgrid.png",{"hash":6781,"date":6782,"subject":6783,"files":6784,"plus":3504,"minus":46},"8732f2d","2021-03-01","add cards for projects + fa",[6785,6787,6788,6789],{"path":6786,"add":11,"del":15},"nuxt.config.js",{"path":60,"add":28,"del":15},{"path":63,"add":11,"del":15},{"path":6767,"add":1177,"del":46},{"hash":6791,"date":6792,"subject":6793,"files":6794,"plus":175,"minus":163},"0b7a1b3","2021-02-28","add some text on homepage + grid + font change",[6795,6796,6798,6799,6801],{"path":6779,"add":15,"del":15},{"path":6797,"add":39,"del":15},"assets\u002Fscss\u002Fdark-mode.scss",{"path":6749,"add":40,"del":40},{"path":6800,"add":82,"del":11},"layouts\u002Fdefault.vue",{"path":6767,"add":182,"del":223},{"hash":6803,"date":6804,"subject":6805,"files":6806,"plus":6814,"minus":5851},"0aaf34e","2021-02-11","add blockchain page",[6807,6809,6810,6811,6813],{"path":6808,"add":11,"del":28},"assets\u002Fimg\u002Fblockchain.svg",{"path":6749,"add":39,"del":39},{"path":6800,"add":40,"del":40},{"path":6812,"add":2770,"del":15},"pages\u002Fblockchain.vue",{"path":6767,"add":11,"del":186},241,{"hash":6816,"date":6817,"subject":6818,"files":6819,"plus":6826,"minus":75},"e1a3472","2021-02-09","add inline blockchain svg animation",[6820,6822,6823,6824,6825],{"path":6808,"add":6821,"del":15},3910,{"path":6786,"add":39,"del":40},{"path":60,"add":3355,"del":15},{"path":63,"add":11,"del":15},{"path":6767,"add":5851,"del":223},4229,{"hash":6828,"date":6829,"subject":6830,"files":6831,"plus":11,"minus":509},"b52d5ab","2021-01-28","pick a font (for now)",[6832],{"path":6749,"add":11,"del":509},{"hash":6834,"date":6835,"subject":6836,"files":6837,"plus":106,"minus":40},"d3d57e3","2021-01-27","try different fonts",[6838,6840,6841],{"path":6839,"add":15,"del":15},".github\u002Fworkflows\u002F{main.yml => deploy_gh-pages.yml}",{"path":6749,"add":572,"del":40},{"path":6767,"add":28,"del":15},{"hash":6843,"date":6844,"subject":6845,"files":6846,"plus":40,"minus":28},"c47fae3","2021-01-26","update accent color",[6847,6848],{"path":6749,"add":40,"del":40},{"path":6849,"add":15,"del":39},"components\u002FNavBar.vue",{"hash":6851,"date":6844,"subject":6852,"files":6853,"plus":11,"minus":15},"dafde34","add CNAME in dist root",[6854],{"path":6855,"add":11,"del":15},"static\u002FCNAME",{"hash":6857,"date":6844,"subject":6858,"files":6859,"plus":15,"minus":11},"98b39cf","remove lint from ci for now",[6860],{"path":6861,"add":15,"del":11},".github\u002Fworkflows\u002Fmain.yml",{"hash":6863,"date":6844,"subject":6864,"files":6865,"plus":6886,"minus":6887,"truncated":82},"3d86ce8","remove vuetify and use bulma for base",[6866,6867,6869,6870,6871,6872,6875,6877,6880,6881,6883,6885],{"path":6861,"add":28,"del":72},{"path":6868,"add":15,"del":164},"assets\u002Fscss\u002Fcommon.scss",{"path":6797,"add":120,"del":15},{"path":6747,"add":444,"del":15},{"path":6749,"add":302,"del":3587},{"path":6873,"add":6874,"del":15},"components\u002FDarkmodeSwitcher.vue",242,{"path":6876,"add":492,"del":15},"components\u002FFooter.vue",{"path":6878,"add":15,"del":6879},"components\u002FModal.vue",143,{"path":6849,"add":4197,"del":15},{"path":6882,"add":15,"del":667},"components\u002FSnackbar.vue",{"path":6884,"add":15,"del":2683},"layouts\u002FHeader.vue",{"path":6800,"add":278,"del":122},3428,2523,{"hash":6889,"date":6890,"subject":6891,"files":6892,"plus":6909,"minus":6910,"truncated":164},"ddcde72","2020-09-10","barebone layout + modal\u002Fsnackbar plugins",[6893,6895,6896,6897,6898,6900,6902,6903,6904,6906,6907,6908],{"path":6894,"add":15,"del":120},".eslintrc.js",{"path":6861,"add":15,"del":11},{"path":6868,"add":164,"del":15},{"path":6749,"add":118,"del":15},{"path":6899,"add":15,"del":72},"assets\u002Fvariables.scss",{"path":6901,"add":15,"del":2777},"components\u002FLogo.vue",{"path":6878,"add":6879,"del":15},{"path":6882,"add":667,"del":15},{"path":6905,"add":15,"del":461},"components\u002FVuetifyLogo.vue",{"path":6884,"add":2683,"del":15},{"path":6800,"add":1119,"del":1866},{"path":6786,"add":444,"del":448},617,1855,{"hash":6912,"date":6890,"subject":6913,"files":6914,"plus":15,"minus":11},"12f75f6","Delete CNAME",[6915],{"path":6916,"add":15,"del":11},"CNAME",{"hash":6918,"date":6890,"subject":6919,"files":6920,"plus":11,"minus":11},"25580ce","trigger github action workflow on push to master branch",[6921],{"path":6861,"add":11,"del":11},{"hash":6923,"date":6890,"subject":6924,"files":6925,"plus":6943,"minus":15,"truncated":163},"c3067fc","add empty nuxt (+vuetify)",[6926,6928,6929,6930,6931,6933,6934,6935,6937,6938,6940,6941],{"path":6927,"add":163,"del":15},".editorconfig",{"path":6894,"add":120,"del":15},{"path":347,"add":21,"del":15},{"path":10,"add":302,"del":15},{"path":6932,"add":43,"del":15},"assets\u002FREADME.md",{"path":6899,"add":72,"del":15},{"path":6901,"add":2777,"del":15},{"path":6936,"add":43,"del":15},"components\u002FREADME.md",{"path":6905,"add":461,"del":15},{"path":6939,"add":43,"del":15},"layouts\u002FREADME.md",{"path":6800,"add":3884,"del":15},{"path":6942,"add":107,"del":15},"layouts\u002Ferror.vue",12560,{"hash":6945,"date":6890,"subject":6946,"files":6947,"plus":40,"minus":15},"0badf59","add gitignore",[6948],{"path":347,"add":40,"del":15},{"hash":6950,"date":6890,"subject":6951,"files":6952,"plus":25,"minus":15},"ed9bac5","Create main.yml",[6953],{"path":6861,"add":25,"del":15},{"hash":6955,"date":6890,"subject":6956,"files":6957,"plus":11,"minus":15},"0d04cc3","Create CNAME",[6958],{"path":6916,"add":11,"del":15},{"hash":6960,"date":6890,"subject":6961,"files":6962,"plus":11,"minus":15},"95382fb","add README",[6963],{"path":10,"add":11,"del":15},1786200551494]