<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: X-ray Tango</title>
    <description>The latest articles on Forem by X-ray Tango (@xraytangooscar).</description>
    <link>https://forem.com/xraytangooscar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F231015%2Fc415f002-d91e-4818-af25-ec12fd440f6d.png</url>
      <title>Forem: X-ray Tango</title>
      <link>https://forem.com/xraytangooscar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/xraytangooscar"/>
    <language>en</language>
    <item>
      <title>I hacked time in Chrome for a demo video</title>
      <dc:creator>X-ray Tango</dc:creator>
      <pubDate>Sat, 08 Nov 2025 12:30:52 +0000</pubDate>
      <link>https://forem.com/xraytangooscar/i-hacked-time-in-chrome-for-a-demo-video-lg7</link>
      <guid>https://forem.com/xraytangooscar/i-hacked-time-in-chrome-for-a-demo-video-lg7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How I made Chrome believe a full workday passed in seconds to record a Timesheetr demo video.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted to demo how easy it is to add entries to Timesheetr, use the keyboard shortcuts, and basically show how it would be used throughout the day to track timesheets.  &lt;/p&gt;

&lt;p&gt;Problem: I couldn't just &lt;strong&gt;wait&lt;/strong&gt; for the whole day to pass and screen-capture the process over 8 hours. So I had to make Chrome think that time had changed in between my timesheet entries.&lt;/p&gt;

&lt;p&gt;See, when you add an entry in Timesheetr, it automatically ends the previous one at the time of the new entry and calculates its duration.&lt;br&gt;&lt;br&gt;
My demo would have been terrible if every entry had the same timestamp.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnnjas5pji1mi3tpxl1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnnjas5pji1mi3tpxl1s.png" alt="Bad demo data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, using a few lines of JavaScript in Chrome’s DevTools, I &lt;strong&gt;overrode the browser’s &lt;code&gt;Date&lt;/code&gt; object&lt;/strong&gt; to make my app think the day was flying by.&lt;br&gt;&lt;br&gt;
I didn’t know that was even possible — but it worked!&lt;/p&gt;
&lt;h3&gt;
  
  
  First, a simple test
&lt;/h3&gt;

&lt;p&gt;The idea was to add a random number of minutes every five seconds.&lt;br&gt;
I ran this in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fakeNow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2025-11-02T10:00:00Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OriginalDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;OriginalDate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OriginalDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakeNow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OriginalDate&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fakeNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minutesToAdd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;fakeNow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakeNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;minutesToAdd&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`⏰ Fake time advanced by &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;minutesToAdd&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; min →`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fakeNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✅ Fake time simulation started&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked beautifully. My app really thought time was passing.&lt;/p&gt;

&lt;p&gt;But for some entries, five seconds was too short , and the random intervals didn’t always make sense.&lt;br&gt;
I kept tweaking the code to make it feel more natural.&lt;/p&gt;
&lt;h3&gt;
  
  
  Fast forward to the result
&lt;/h3&gt;

&lt;p&gt;I ended up writing a small “demo script” to plan exactly what I’d type and when.&lt;br&gt;
Then I hard-coded the specific times for each entry to make it fit better.&lt;br&gt;
Finally, I added a short &lt;strong&gt;beep&lt;/strong&gt; each time the fake clock advanced — so I’d know if I was early or late (typing too fast or too slow).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Beep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AudioContext&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webkitAudioContext&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;osc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createOscillator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createGain&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;osc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;osc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sine&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;osc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;880&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;osc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;osc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;07:51&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;09:30&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;09:43&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;10:53&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;11:37&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;13:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;13:23&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;17:30&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OriginalDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fakeNow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OriginalDate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;fakeNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;OriginalDate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OriginalDate&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OriginalDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fakeNow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fakeNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;fakeNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;⏰ Time jump →&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fakeNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleTimeString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nc"&gt;Beep&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sec&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sec&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;jump&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✅ Time hacking started. Watch your console!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;


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

&lt;/div&gt;



&lt;p&gt;And that’s it — my fake day unfolded perfectly for the demo video.&lt;br&gt;
Take a look at the result:&lt;br&gt;


&lt;iframe class="tweet-embed" id="tweet-1987129678612639789-61" src="https://platform.twitter.com/embed/Tweet.html?id=1987129678612639789"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1987129678612639789-61');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1987129678612639789&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;It’s certainly not the cleanest piece of code, and probably not something to reuse as-is, but it’s a neat trick for time-sensitive demos or tests.&lt;/p&gt;

&lt;p&gt;If you ever need to fake time in a web app — this hack is gold.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>console</category>
      <category>javascript</category>
      <category>demo</category>
    </item>
    <item>
      <title>Building Liberty Drives: A Road Trip Planner Born from Curiosity (and a Bit of Chaos)</title>
      <dc:creator>X-ray Tango</dc:creator>
      <pubDate>Sun, 26 Oct 2025 15:05:53 +0000</pubDate>
      <link>https://forem.com/xraytangooscar/building-liberty-drives-a-road-trip-planner-born-from-curiosity-and-a-bit-of-chaos-27c</link>
      <guid>https://forem.com/xraytangooscar/building-liberty-drives-a-road-trip-planner-born-from-curiosity-and-a-bit-of-chaos-27c</guid>
      <description>&lt;p&gt;The best vacations I’ve ever taken were road trips. I did some in the USA and some in New Zealand. One thing you don’t want to do on a road trip is over-plan and leave no room for discovery and wandering. But you also don’t want to just drive with no plan. At least I don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enters: AI
&lt;/h2&gt;

&lt;p&gt;With AI and its world knowledge, I thought there was something interesting to do. So I tried tweaking an OpenAI custom GPT, but the results — in a chat — were hard to read and to come back to. So I decided to build a website where anyone can generate their own custom itinerary, with all the tweaks and requirements from my initial request. It provides a good balance between driving and discovering, accounting for constraints like vehicle type (motorcycle, EV, campervan), accessibility requirements, and more.&lt;/p&gt;

&lt;p&gt;Of course, most of it can be obtained with a well-crafted prompt, but what I find interesting isn’t the actual itinerary result — it’s the images (taken for each stop from Unsplash), the pro tips and practical info, and the fact that you can bookmark a trip and download it as a PDF for offline reading later.&lt;/p&gt;

&lt;p&gt;Last but not least, it’s a lot of fun to see what road trips others are exploring and the hidden gems the AI brings to light.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;The tech is intentionally simple — I wanted it fast, light, and easy to maintain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: SvelteKit + TypeScript&lt;/li&gt;
&lt;li&gt;Backend: Cloudflare Pages + D1 (SQLite)&lt;/li&gt;
&lt;li&gt;Styling: TailwindCSS&lt;/li&gt;
&lt;li&gt;Images: Unsplash API for every stop&lt;/li&gt;
&lt;li&gt;Maps: Mapbox API for route previews&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything runs on the edge through Cloudflare, so it’s global by default and basically maintenance-free.&lt;br&gt;
No servers. No scaling headaches. Just deploy and go.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenges Along the Way
&lt;/h2&gt;

&lt;p&gt;No project ever goes smoothly from start to finish, and Liberty Drives was no exception.&lt;br&gt;
Half the time I was learning things on the fly, and the other half I was wondering why they suddenly stopped working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unsplash API — Getting Out of the Sandbox
&lt;/h3&gt;

&lt;p&gt;One of the first hurdles was getting approved to use the Unsplash API outside of its sandbox mode.&lt;br&gt;
I wanted every itinerary to show real images for each stop, not just gray boxes or random placeholders.&lt;/p&gt;

&lt;p&gt;Unsplash’s review process makes sense — they want responsible usage and proper attribution — but it meant making sure every image loaded correctly, displayed credit text, and cached properly.&lt;br&gt;
I ended up re-working how I fetched, stored, and displayed images just to meet their API compliance.&lt;br&gt;
It took a few iterations, but seeing those photos come alive on the page made it completely worth it. And honestly the team at Unsplash was very nice throughout.&lt;/p&gt;

&lt;h3&gt;
  
  
  SvelteKit Meets Wrangler
&lt;/h3&gt;

&lt;p&gt;Then came the Cloudflare side of things.&lt;br&gt;
SvelteKit runs beautifully in theory, but getting it to play nice with Wrangler and Cloudflare Pages Functions wasn’t always straightforward.&lt;/p&gt;

&lt;p&gt;At one point, I spent hours chasing what I thought was a deployment bug — console logs, restarts, rebuilds — only to find out it was just a caching issue.&lt;br&gt;
Classic.&lt;br&gt;
Nothing humbles you like debugging your own impatience.&lt;/p&gt;

&lt;p&gt;It also took me a while to understand where to put my backend code: Cloudflare's Function folder VS SvelteKit's src/routes/api folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foypu3h98it0z0l03vsz0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foypu3h98it0z0l03vsz0.png" alt="SvelteKit and Wrangler" width="644" height="894"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still, once it clicked, the result was worth it: zero servers, instant deploys, and edge rendering everywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning Tailwind
&lt;/h3&gt;

&lt;p&gt;I also learned Tailwind CSS for this project.&lt;br&gt;
There was definitely a learning curve at first — all those class names look like hieroglyphs when you start.&lt;br&gt;
But once it “clicked,” I realized I never wanted to go back to writing vanilla CSS again.&lt;/p&gt;

&lt;p&gt;Being able to style everything inline, with predictable utility classes, made building the interface so much faster.&lt;br&gt;
It felt like the perfect companion to SvelteKit’s component approach — simple, contained, and clean.&lt;/p&gt;

&lt;h3&gt;
  
  
  The OG Image Struggle
&lt;/h3&gt;

&lt;p&gt;One thing I still haven’t cracked properly is generating OG images for the itineraries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywievzrwuuotwv5om6g4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywievzrwuuotwv5om6g4.png" alt="OG Image generated for each itinerary" width="800" height="420"&gt;&lt;/a&gt;&lt;br&gt;
Right now, I’m using the static map image as a base, overlaying some text and the logo, and saving the whole thing as an SVG. That's done automatically at the creation of the itinerary. But from there, I have to manually download the SVG, convert it to PNG, compress it, and upload it — which works, but it’s clunky and definitely not sustainable if the site grows.&lt;/p&gt;

&lt;p&gt;I’ve looked at automating that last stretch with headless rendering or Cloudflare Workers, or Cloudflare Images, but haven’t landed on a clean solution yet.&lt;br&gt;
So for now, it’s a bit of a handmade process — which, honestly, feels pretty fitting for a project about road trips.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It’s At Now
&lt;/h2&gt;

&lt;p&gt;Right now, Liberty Drives can already do some pretty fun stuff. You tell it how long you’ve got, roughly where you want to go, and what kind of trip you’re in the mood for — and it builds a plan that feels human, not robotic.&lt;/p&gt;

&lt;p&gt;It adapts to simple things like what you’re driving (a bike, an EV, or a campervan) and whether you care more about comfort, accessibility, or adventure. It’s not just spitting out data; it’s shaping something that actually makes sense once you’re out there on the road.&lt;/p&gt;

&lt;p&gt;Each trip comes alive with real photos from Unsplash, a few travel tips, and the kind of small, practical notes you’d normally only get from someone who’s been there. You can save your trip, keep it for later, or download it to take with you when you go offline.&lt;/p&gt;

&lt;p&gt;And maybe the best part? Seeing what other people are planning — the wild routes, unexpected regions, and hidden places the AI keeps surfacing. It’s like getting a glimpse into everyone else’s version of “freedom on the road.”&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;There’s still a lot I want to build on top of this. Right now, the AI does a good job of coming up with complete trips, but I’d love to give people more control over the result. Things like being able to say, “Add a stop in this town,” or “Include a detour for a hike or a winery.”&lt;/p&gt;

&lt;p&gt;I also want to make it easier to start from an existing itinerary — take something that’s already good and tweak it into your own version. The goal isn’t to make AI replace human planning, but to make it a creative partner in the process.&lt;/p&gt;

&lt;p&gt;Another idea on the list is creating famous routes — like Route 66 or the Road to Civil Rights — but with all the modern tweaks you’d need today: accessibility, vehicle type, time limits, even budget filters.&lt;/p&gt;

&lt;p&gt;And beyond features, I’d love to turn it into more of a community space — where people can rate itineraries, leave comments and suggestions, and help uncover even more hidden gems along the road.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;At the end of the day, I’m just building something I wish existed. There’s no grand plan or secret growth strategy — most of the time I’m just shooting blind in total darkness, hoping it’ll click for someone else too.&lt;/p&gt;

&lt;p&gt;If you’re a fellow builder and want to see how it’s going, it’s live at &lt;a href="https://libertydrives.com" rel="noopener noreferrer"&gt;LibertyDrives.com&lt;/a&gt;. Please let me know if you have any feedback or comments.&lt;/p&gt;

&lt;p&gt;Sometimes, the best projects aren’t about getting somewhere, they’re about finding the road that finds you.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>roadtrips</category>
      <category>sveltekit</category>
    </item>
    <item>
      <title>I Got Tired of Timesheets, So I Built My Own Tool</title>
      <dc:creator>X-ray Tango</dc:creator>
      <pubDate>Sun, 07 Sep 2025 13:59:09 +0000</pubDate>
      <link>https://forem.com/xraytangooscar/i-got-tired-of-timesheets-so-i-built-my-own-tool-4ege</link>
      <guid>https://forem.com/xraytangooscar/i-got-tired-of-timesheets-so-i-built-my-own-tool-4ege</guid>
      <description>&lt;p&gt;Every Friday, I used to lose time trying to remember what I actually worked on. My employer’s ERP made timesheets feel like unpaid homework — slow forms, clunky UX, way too many clicks.&lt;/p&gt;

&lt;p&gt;Funny enough, back when I freelanced part-time, pen and paper was faster than the ERP. Just jot down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;09:00 Project A  
10:30 Meeting  
11:00 Project B  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple. But as a full-time consultant, the list at the end of the week got &lt;em&gt;long&lt;/em&gt;. Translating that back into the ERP became painful again. That’s when I thought: &lt;em&gt;I should automate this.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Wanted
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Recording should be instant.&lt;/strong&gt; The system should capture start times automatically, then calculate the time spent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reporting should be painless.&lt;/strong&gt; Group entries by task, push them to the ERP via API, and never open the ERP’s UX again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I considered Excel, but it wouldn’t give me real automation. I checked a few existing tools, but they felt bloated and didn’t match my pen-and-paper logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;End a task by simply starting the next one.&lt;/li&gt;
&lt;li&gt;Automatically calculate the duration of the previous task.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built a web app.&lt;/p&gt;




&lt;h2&gt;
  
  
  Version 1: Already a Boost
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add entries with one button or a keyboard shortcut.&lt;/li&gt;
&lt;li&gt;Date/time auto-populates.&lt;/li&gt;
&lt;li&gt;Previous task’s duration auto-calculated.&lt;/li&gt;
&lt;li&gt;Select multiple entries → see the total minutes right away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’d just take that total and type it into the ERP. Productivity win.&lt;/p&gt;

&lt;p&gt;The stack remained super simple: no backend needed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend deployed to &lt;strong&gt;Cloudflare Pages&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Data stored in &lt;strong&gt;browser localStorage&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No signup, no emails, no accounts. Just open the page and log.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Integrating with the ERP
&lt;/h2&gt;

&lt;p&gt;Of course, I wanted more. My employer used &lt;strong&gt;Odoo&lt;/strong&gt;, and I wanted to push timesheets directly to it at the end of each day, with one button click. That meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pulling projects and tasks from Odoo via API&lt;/li&gt;
&lt;li&gt;Linking entries in my app&lt;/li&gt;
&lt;li&gt;Pushing them back to Odoo in bulk at the end of the day, with rounding options for time spent (minimum 15 minutes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then came the classic: &lt;strong&gt;CORS issues&lt;/strong&gt;.&lt;br&gt;
Frontend → Odoo backend? Nope.&lt;br&gt;
I tried using a Chrome extension and it worked, it was just not that practical.&lt;br&gt;
The fix: &lt;strong&gt;Cloudflare Workers&lt;/strong&gt;. They handled the proxy neatly, and suddenly I had a working integration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Current Tech Stack (for the curious)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; StencilJS (web components)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting:&lt;/strong&gt; Cloudflare Pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; Browser LocalStorage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ERP Integration:&lt;/strong&gt; Odoo API (Projects &amp;amp; Tasks)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy / CORS Fix:&lt;/strong&gt; Cloudflare Workers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lightweight, serverless, and cheap to run.&lt;/p&gt;




&lt;h2&gt;
  
  
  Small UX Wins
&lt;/h2&gt;

&lt;p&gt;Over time, I kept scratching little itches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added a &lt;strong&gt;“Break” button&lt;/strong&gt; (instead of typing it every time) → logs as non-working time.&lt;/li&gt;
&lt;li&gt;Polished the design, made it &lt;strong&gt;responsive&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Shortcuts for common actions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a quick screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbmgw5ltf53a7rji3p6fg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbmgw5ltf53a7rji3p6fg.png" alt=" " width="800" height="689"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I didn’t think mobile mattered at first, but once or twice I wanted to check/edit during my commute. Since the data lives in local storage, it wasn’t super useful. Maybe syncing across devices will come later... That would require a backend, a database, authentication etc.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Teamwork integration&lt;/strong&gt; (since that’s what I use now).&lt;/li&gt;
&lt;li&gt;Maybe &lt;strong&gt;calendar pre-fills&lt;/strong&gt; to auto-generate some entries.&lt;/li&gt;
&lt;li&gt;CSV / Excel exports.&lt;/li&gt;
&lt;li&gt;Integrate with Zapier/Make hooks.&lt;/li&gt;
&lt;li&gt;Sync across devices if users ask for it.&lt;/li&gt;
&lt;li&gt;And why not at some point have an LLM improve the description of timesheets? 😅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the core will stay: simple, fast, no sign-up, no data harvesting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wanna Try?
&lt;/h2&gt;

&lt;p&gt;If you’re a freelancer, consultant, or developer who hates timesheets, maybe my app can help:&lt;br&gt;
👉 &lt;a href="https://timesheeter.app" rel="noopener noreferrer"&gt;timesheeter.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’d love to hear feedback — especially from devs who know the pain and want to make this faster.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>freelance</category>
    </item>
  </channel>
</rss>
