<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Vite on blog.iankulin.com</title><link>https://blog.iankulin.com/tags/vite/</link><description>Recent content in Vite on blog.iankulin.com</description><generator>Hugo</generator><language>en-AU</language><lastBuildDate>Fri, 26 Jan 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iankulin.com/tags/vite/index.xml" rel="self" type="application/rss+xml"/><item><title>Getting Your Vite React App to Work on Github Pages</title><link>https://blog.iankulin.com/getting-your-vite-react-app-to-work-on-github-pages/</link><pubDate>Fri, 26 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/getting-your-vite-react-app-to-work-on-github-pages/</guid><description>&lt;img src="https://blog.iankulin.com/images/combined.png" width="512" alt=""&gt;
&lt;p&gt;One of the many cool things about GitHub is &lt;a href="https://pages.github.com"&gt;GitHub Pages&lt;/a&gt; - the free web hosting Microsoft gives you while they vacuum up &lt;a href="https://docs.github.com/en/copilot/overview-of-github-copilot/about-github-copilot-individual"&gt;your code for CoPilot&lt;/a&gt; training. Each repository you keep there can have pages at &lt;code&gt;&amp;lt;your-github-username&amp;gt;.github.io/&amp;lt;repo-name&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="github"&gt;GitHub&lt;/h3&gt;
&lt;p&gt;To enable this, you need to go into the settings for the repository - look down the left for &amp;ldquo;Pages&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-1.58.05-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to have it based on a complicated GitHub action (where your build step happens on GitHub when you push your code), but the easiest thing is just to have it deployed from a branch. To do this you choose which branch (usually main) and whereabouts in the main branch your HTML is. The choices are in the root of your project, or in the &lt;code&gt;/docs&lt;/code&gt; directory. I&amp;rsquo;ve chosen the &lt;code&gt;/docs&lt;/code&gt; directory in the screenshot above, since my messy React project is in the root.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all the GitHub set up we need. Now whenever I push my project to the &lt;code&gt;main&lt;/code&gt; branch on GitHub, whatever is in the &lt;code&gt;/docs&lt;/code&gt; directory will be uploaded to my GitHub page for this repo.&lt;/p&gt;
&lt;h3 id="vitereact"&gt;Vite/React&lt;/h3&gt;
&lt;p&gt;Now we need to make a couple of changes to our project to get this to work. The first is to tell Vite the &amp;ldquo;base directory&amp;rdquo; for the project which needs to be the repo name you&amp;rsquo;ve used on GutHub.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.04.50-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.04.50-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is written into the &lt;code&gt;index.html&lt;/code&gt; that is built as part of this process. If it&amp;rsquo;s not there, then any browser accessing your &lt;code&gt;index.html&lt;/code&gt; on gh-pages won&amp;rsquo;t be able to find your JavaScript, and the user will be left looking at a blank white page instead of your amazing app.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.11.06-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.11.06-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My process from this point, is to build the project with &lt;code&gt;npm run build&lt;/code&gt;. By default, this creates a &lt;code&gt;/dist&lt;/code&gt; directory in your project (which is already added to &lt;code&gt;.gitignore&lt;/code&gt;) and puts the project artifacts (the HTML, JavaScript, CSS and any images) into it. I then manually copy the artifacts over to the &lt;code&gt;/docs&lt;/code&gt; directory of the project and push it up to GitHub to be published - which takes two or three minutes.&lt;/p&gt;
&lt;p&gt;I like this manual step of copying the files over so that publishing is an intentful action on my part, and also, for solo projects I generally just work out of the main branch rather than on feature branches that then get PR&amp;rsquo;d into main. If you did want the process to be more CI/CD flavoured, you can just make another change the &lt;code&gt;vite.config.ts&lt;/code&gt; file to have your builds go straight to the &lt;code&gt;/docs&lt;/code&gt; folder.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; defineConfig &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; from &lt;span style="color:#a3be8c"&gt;&amp;#39;vite&amp;#39;&lt;/span&gt;import react from &lt;span style="color:#a3be8c"&gt;&amp;#39;@vitejs/plugin-react&amp;#39;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; https&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt;vitejs&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;dev&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;config&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default defineConfig&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; base&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;/mosh-expense/&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; plugins&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;react&lt;span style="color:#eceff4"&gt;()],&lt;/span&gt; build&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; outDir&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;docs&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once all that&amp;rsquo;s working, and you&amp;rsquo;ve pushed your changes and waited a minute or two, your project should be live to the world on &lt;code&gt;github.io&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.45.26-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.45.26-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want users browsing your repo to find the live version, it&amp;rsquo;s worth editing your repository about settings to point to it.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.47.30-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.47.30-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;</description></item><item><title>React - a To Do Example</title><link>https://blog.iankulin.com/react-a-to-do-example/</link><pubDate>Mon, 08 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/react-a-to-do-example/</guid><description>&lt;p&gt;Since I&amp;rsquo;m on a roll making different versions of the To Do app, this might be a good time to talk about &lt;a href="https://react.dev/"&gt;React&lt;/a&gt;. React is one of the giants of front end libraries. It&amp;rsquo;s based on a few big ideas - and to work effectively in React you need to wrap your head around these.&lt;/p&gt;
&lt;h3 id="overview"&gt;Overview&lt;/h3&gt;
&lt;p&gt;Components - when you are developing in React, the starting point of your build is to decompose the user interface in to logical pieces. These components (comprising a mixture of HTML and Javascript) will be the building blocks of your app. In a good composable architecture components are reusable, and that is true for React (there are several sources of components you can pull in). For example, if you created some sort of special slider for your app, it is possible to reuse that quite easily.&lt;/p&gt;
&lt;p&gt;Declarative - this was one of the big barriers when I was learning SwiftUI. The UI is just described. It&amp;rsquo;s not a big step when you&amp;rsquo;re coming from HTML - all that is is a description of the user interface. The next step of this is that React deals with using the state of your data model to update the UI. This means that these state-UI connections are made very explicit (which I like) and protected. For example if there&amp;rsquo;s a counter on a web page, you can&amp;rsquo;t change the HTML of the page to increment the counter, and in fact, you can&amp;rsquo;t directly change the counter. These things are wrapped up so React can manage them, and you have to play by React&amp;rsquo;s rules.&lt;/p&gt;
&lt;p&gt;Virtual DOM - since each component knows what state it depends upon, and changing that state causes the component to be redrawn, it quickly gets expensive with parts of the page being reloaded. To improve performance, and reduce often unneeded flashes of content loading, React keeps a copy of the DOM and makes changes to it rather than the real DOM. Since this is instrumented, React can easily see what changes &lt;em&gt;really&lt;/em&gt; need to be percolated to the browser DOM and can manage how that happens in the most efficient way. Sometimes I think I know what I want to happen for something to be efficient but in React, it&amp;rsquo;s often not in your control, and you just need to trust the system.&lt;/p&gt;
&lt;h3 id="tooling"&gt;Tooling&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re leaving my comfort zone of straightforward development environments, along with the major benefit of working in Javascript. The complexity of the React system requires a build step to produce the production artifacts. Whether you use the standard &lt;a href="https://create-react-app.dev/"&gt;Create-React&lt;/a&gt;, or &lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt; (as I did for this project) you get a system for bundling the code, mapping it (since for debugging you need a way to translate the source line that&amp;rsquo;s running back to the human readable source), and a development server to run the app while you&amp;rsquo;re working on it.&lt;/p&gt;
&lt;p&gt;These things inevitably add to complexity and errors, and are the reason that big projects start to need tools like development containers to remove a pain point. Like anything, you get better with experience, but especially at the start there&amp;rsquo;s a developer cost involved. React is incredibly popular, so most people&amp;rsquo;s calculus on this is that the extra complexity in project management is made up for with improved developer experience.&lt;/p&gt;
&lt;p&gt;In any case, I got started by typing &lt;code&gt;npm create vite@latest .&lt;/code&gt; and then choosing React and Javascript. This created a starter project, and spun it up in the development server. In the &lt;code&gt;package.json&lt;/code&gt; file it gives you, there is a build command that can be run with &lt;code&gt;npm run build&lt;/code&gt; to create the output files. I had issues with the development server that serves up these frontend files - since I also needed to run a real server (the unchanged REST API server for Todo Items data from the earlier project) on a different port. Then it complained - thinking it was part of a cross-site scripting hack. To overcome it, I just built the production code for each round of testing - with such a little project this wasn&amp;rsquo;t a hardship. But when I did have an error to track, it pointed me to a line number that turned out to be about 20 pages of minified code.&lt;/p&gt;
&lt;h3 id="components"&gt;Components&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-22-at-7.47.24-am.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Just a little reminder of how this app looks so we can think about how we&amp;rsquo;re going to break it down. There is a list of todo items, each one has a button to delete it (which we do when it has been completed) then at the bottom there&amp;rsquo;s a little form to add a new item (by clicking the button or hitting enter) to the bottom of the list.&lt;/p&gt;
&lt;p&gt;I ended up with three components - the App (every React app has one), the TodoList, and the AddTodoForm. Note that I could easily have had a forth component - the TodoItem. This is a bit of matter of taste - I probably would have if I wanted to do something fancier like editing in place - but for the current UX the cost of extracting out another component wasn&amp;rsquo;t worth the benefit.&lt;/p&gt;
&lt;h3 id="anatomy-of-a-component"&gt;Anatomy of a component&lt;/h3&gt;
&lt;p&gt;I claimed earlier that a component was it&amp;rsquo;s HTML and Javascript wrapped up together which, while a massive simpliciation, is a good place to start thinking about it. Every component is just a function that returns a bunch of (templated) HTML. We&amp;rsquo;ll start off by developing our AddTodoForm. At it&amp;rsquo;s simplest, it could be something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;function AddTodoForm&lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;form&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;input type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; name&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo_item&amp;#34;&lt;/span&gt; id&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo&amp;#34;&lt;/span&gt; required &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;Add&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;form&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default AddTodoForm&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this little form component can&amp;rsquo;t really talk to the world, where as we need it to add a todo item. First, let&amp;rsquo;s track any changes to the text field.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; useState &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; from &lt;span style="color:#a3be8c"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;function AddTodoForm&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;props&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;]&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; useState&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;form onSubmit&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;handleSubmit&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;input type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; name&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo_item&amp;#34;&lt;/span&gt; id&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo&amp;#34;&lt;/span&gt; value&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; onChange&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;e &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;e&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;target&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;)}&lt;/span&gt; required &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;Add&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;form&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default AddTodoForm&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is an example of the very explicit management of state I was talking about earlier.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;const [value, setValue] = useState('');&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;useState() is a React hook for managing state. This line gives us a getter (value) and setter (setValue) for this variable, and set&amp;rsquo;s its initial state to &amp;lsquo;&amp;rsquo;. If the value changes the component will be redrawn. React will know that the value has changed as this is built into the setValue() function where we never need see or worry about it. If you foolishly decided to side-step React and assign directly to &lt;code&gt;value&lt;/code&gt;, I guess there&amp;rsquo;d be a runtime error, or even worse, no error and the management of the DOM state wuld fall into some type of chaos.&lt;/p&gt;
&lt;p&gt;What we&amp;rsquo;re doing with this value (which I&amp;rsquo;m now realising is very badly named) is using it to collect the text input from out form. It&amp;rsquo;s constantly updated as the user types.&lt;/p&gt;
&lt;p&gt;onChange={e =&amp;gt; setValue(e.target.value)}&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s our value sorted, but of course we need to add it to our data model. This model is being managed at the App level so there&amp;rsquo;s a bit of juggling needed to get it out to there. This &amp;lsquo;plumbing&amp;rsquo; cost is the downside of these types or framework, but it&amp;rsquo;s not really complex and quickly becomes routine.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll start at the top, here&amp;rsquo;s the relevant code from App.jsx - our App component.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;todos&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; setTodos&lt;span style="color:#eceff4"&gt;]&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; useState&lt;span style="color:#eceff4"&gt;([]);&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; addTodo &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;newTodo&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; fetch&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;http://localhost:3000/todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; method&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; headers&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;},&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; JSON&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;stringify&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;newTodo&lt;span style="color:#eceff4"&gt;),&lt;/span&gt; &lt;span style="color:#eceff4"&gt;})&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;then&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;response &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; response&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;())&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;then&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;data &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; Update the todos state with the new todo setTodos&lt;span style="color:#eceff4"&gt;([&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;...&lt;/span&gt;todos&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; data&lt;span style="color:#eceff4"&gt;]);&lt;/span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt; &lt;span style="color:#eceff4"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This useState hook should be starting to look familiar. We read the todos from &lt;code&gt;todos&lt;/code&gt;, and write to them with &lt;code&gt;setTodos()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;addTodo&lt;/code&gt; does the actual work of saving it to the database (via our REST API) then creates a new &lt;code&gt;todos&lt;/code&gt; array by adding our new one to the end. But we need to pass this down into our AddTodoForm. Here&amp;rsquo;s the main part of the App that returns out HTML, in this case that&amp;rsquo;s the list of todos and our form:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; return ( &amp;lt;main&amp;gt; &amp;lt;h1&amp;gt;To do&amp;lt;/h1&amp;gt; &amp;lt;TodoList todos={todos} onDeleteTodo={deleteTodo}/&amp;gt; &amp;lt;AddTodoForm onAddTodo={addTodo} /&amp;gt; &amp;lt;/main&amp;gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can see here that the todos array and a function &lt;em&gt;deleteTodo&lt;/em&gt;() are passed into the the TodoList component, and that our &lt;em&gt;addTodo()&lt;/em&gt; function is passed to the AddTodoForm.&lt;/p&gt;
&lt;p&gt;In React, things like this that are passed into a component are passed as a single object variable called &amp;lsquo;props&amp;rsquo; - short for properties. It seems crazy to me to be bundling them like this in a language in 2023 rather than passing them explicitly as separate variables. This lack or clarity about what&amp;rsquo;s being passed into a component is doubtless one of the reasons TypeScript is such a common combo with React. It&amp;rsquo;s certainly the first time I&amp;rsquo;ve felt the need of it.&lt;/p&gt;
&lt;p&gt;There is a lighter partial solution to adding types to props so that the linter can call out any issues - this is the PropTypes library - once it&amp;rsquo;s installed, we import it with &lt;code&gt;import PropTypes from 'prop-types';&lt;/code&gt; then we can add a definition for the props at the bottom of the file. For our AddTodoForm, this would look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AddTodoForm.propTypes = { onAddTodo: PropTypes.func.isRequired,};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the linter will prevent us from using anything other than props.onAddToDo, and it will flag if it&amp;rsquo;s being used as anything other than a function.&lt;/p&gt;
&lt;p&gt;Anyway, the props are passed in and we can extract the function from it to use.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; useState &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; from &lt;span style="color:#a3be8c"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;function AddTodoForm&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;props&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;]&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; useState&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; handleSubmit &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;event&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; event&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;preventDefault&lt;span style="color:#eceff4"&gt;();&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;!&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;onAddTodo&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; todo_item&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; value &lt;span style="color:#eceff4"&gt;});&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; &lt;span style="color:#eceff4"&gt;};&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;form onSubmit&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;handleSubmit&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;input type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; name&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo_item&amp;#34;&lt;/span&gt; id&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo&amp;#34;&lt;/span&gt; value&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; onChange&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;e &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;e&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;target&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;)}&lt;/span&gt; required &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;Add&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;form&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default AddTodoForm&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The arrangement of elements in this file will start to become familiar - there&amp;rsquo;s often some state at the top with the useState() hook, then a few handler functions, then our final return of the &amp;lsquo;HTML&amp;rsquo;.&lt;/p&gt;
&lt;p&gt;Our TodoList is a bit simpler in that it doesn&amp;rsquo;t have any handlers, but a better illustration of using PropTypes since it has access to the global state of the todos.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import PropTypes from &lt;span style="color:#a3be8c"&gt;&amp;#39;prop-types&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;function TodoList&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;props&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;ul&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;todos&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;map&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todo &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;li key&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;todo&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;todo&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{()&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;onDeleteTodo&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todo&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;)}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; Done &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;li&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;))}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;ul&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;)}&lt;/span&gt;TodoList&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;propTypes &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; todos&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; PropTypes&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;array&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isRequired&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; onDeleteTodo&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; PropTypes&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;func&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isRequired&lt;span style="color:#eceff4"&gt;};&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default TodoList&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;You would not sensibly reach for React for a project this size - the complexity of the tooling, and the fact that we&amp;rsquo;re now shipping 150K of Javascript to do something we were nicely achieving in 2K of vanilla, or 15K with htmx makes me deeply uncomfortable. Nevertheless, hundreds of thousands of developers can&amp;rsquo;t be wrong - React&amp;rsquo;s component model is a powerful one for building modern single page applications, especially when it allows you to pull in components from public or corporate collections.&lt;/p&gt;
&lt;p&gt;I plan on doing some more React - partly because its just such big part of the webdev world, and partly because I&amp;rsquo;d like to get some experience with TypeScript, so I&amp;rsquo;m fishing around for a medium size project to play with both of these technologies.&lt;/p&gt;
&lt;p&gt;(&lt;a href="https://github.com/IanKulin/todo/tree/react"&gt;source&lt;/a&gt;)&lt;/p&gt;</description></item><item><title>Openlayers &amp; Vite</title><link>https://blog.iankulin.com/openlayers-vite/</link><pubDate>Thu, 26 Jan 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/openlayers-vite/</guid><description>&lt;p&gt;In Randy Pausch&amp;rsquo;s &lt;a href="https://www.youtube.com/watch?v=ji5_MqicxSo"&gt;last lecture&lt;/a&gt; he talks about the benefit of brick walls in our lives - they tell us how much we really want something. Software development is full of these brick walls - things we want to do, but there&amp;rsquo;s a barrier to achieving it. Will we persevere and accomplish the thing, give up, or some other compromise.&lt;/p&gt;
&lt;p&gt;In heroic tales, the protagonist overcomes all obstacles to achieve the goal. In life and especially in software development, that&amp;rsquo;s not always the smart thing to do - to stubbornly invest in an outcome, often disproportionately to the benefit. Here&amp;rsquo;s my brick walls from today.&lt;/p&gt;
&lt;p&gt;I had made a web page showing text of the updated lat/long of the ISS. It met the requirements, but was not very exciting. The obvious thing to do with this information would be to show it on a map.&lt;/p&gt;
&lt;h4 id="moving-map"&gt;Moving map&lt;/h4&gt;
&lt;p&gt;Obviously there are lots of options for this, none of them were as simple as I would have liked, and I wanted a free one. &lt;a href="https://openlayers.org/"&gt;OpenLayers&lt;/a&gt; sounded like it would do the job, and the download to showing a map in the demo app time was about five minutes - we&amp;rsquo;re off to a good start.&lt;/p&gt;
&lt;h4 id="wall-1---complicated-library"&gt;Wall 1 - complicated library&lt;/h4&gt;
&lt;p&gt;I&amp;rsquo;m not joking when I say OpenLayers is comprehensive. It appears to do everything you could possibly want. The flipside of this is complexity. I managed to get the map scrolling so the ISS position was the centre of the map, and discovered how to add a dot as a feature to represent the ISS, but then when I wanted to get that dot to move I was increasingly out of my depth.&lt;/p&gt;
&lt;p&gt;Of course, there&amp;rsquo;s nothing magical here - it&amp;rsquo;s a gigantic, well documented API, and there&amp;rsquo;s a smattering on answers on StackOverflow. I&amp;rsquo;m smart enough to get my head around it, find some repos using it and dissect them and so on. The question is one of cost/benefit. I&amp;rsquo;m essentially building a toy for my own amusement - would the time be better spent on moving on to the next part of my course?&lt;/p&gt;
&lt;h4 id="wall-2---overlapping-images"&gt;Wall 2 - overlapping images&lt;/h4&gt;
&lt;p&gt;I have a better idea anyway - since the ISS should be at the centre of the map, I can just stick a picture of the ISS there. I assume this is possible with CSS?&lt;/p&gt;
&lt;p&gt;This turns out to be a low wall. The CSS for my image is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;img {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; position: absolute;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; top: 50%;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; left: 50%;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The container (that holds the map and this image of the ISS) has &lt;code&gt;position:relative&lt;/code&gt; this all works on the first try, and my ISS png is positioned perfectly in the centre of the map at it&amp;rsquo;s correct location, and it changes correctly as the window is resized.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-01-17-at-6.25.04-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;h4 id="wall-3---vite"&gt;Wall 3 - Vite&lt;/h4&gt;
&lt;p&gt;The process of creating the demo app from the Getting Started instructions for OpenLayers involves something called &lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt;. Apparently it&amp;rsquo;s a &amp;ldquo;Next Gen Frontend Tooling&amp;rdquo; which does not really tell me much. I&amp;rsquo;ve heard it mentioned in passing a couple of times, and know it&amp;rsquo;s pronounced veet. A bit like when I was using React, there&amp;rsquo;s a node build step that fills a directory with the distributed files - I&amp;rsquo;m guessing this is something to do with pulling in just the parts of the giant library I&amp;rsquo;m using - but there&amp;rsquo;s 260K of JavaScript and a 1.6MB .map in there, so I feel some handcrafting or a CDN should be involved.&lt;/p&gt;
&lt;p&gt;If the .map file is in fact the map, that&amp;rsquo;s actually pretty amazing for a map of the entire world that I can zoom down to individual street level - so it&amp;rsquo;s probably not, but either way it&amp;rsquo;s massive overkill for what this application needs. All I really need for this is a 1000x500 px image of a map of the world. Still here we are.&lt;/p&gt;
&lt;p&gt;So I don&amp;rsquo;t really understand Vite, but I can follow the instructions enough to get the live server running for development, and to build the distribution files.&lt;/p&gt;
&lt;h4 id="wall-4---github-pages"&gt;Wall 4 - GitHub Pages&lt;/h4&gt;
&lt;p&gt;I love GitHub pages, and have a couple of apps up on it &lt;a href="https://iankulin.github.io/calc/"&gt;here&lt;/a&gt; and &lt;a href="https://iankulin.github.io/todo001/"&gt;here&lt;/a&gt;. It works by specifying a branch (which can include main) in the repo to expose, or by starting a repo with your GitHub username - for example, mine would be &lt;a href="https://iankulin.github.io/"&gt;iankulin.github.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I had my original (non-map) version of the iss location working on Pages as a subdirectory of the main iankulin page. For the map version, I publish it directly from the docs directory of the main branch of it&amp;rsquo;s own repo. About this time, I realised that my original version was mysteriously not working. I would have been okay with that, but neither was the new version, which as usual &amp;lsquo;wOrKEd On mY mAChiNe&amp;rsquo;.&lt;/p&gt;
&lt;p&gt;I can see now what the problem was with my original version. I had copied the source files of the non-map version to the /iss folder of my main github.io page, so it&amp;rsquo;s address had been &lt;a href="https://iankulin.github.io/iss/"&gt;https://iankulin.github.io/iss/&lt;/a&gt;. Meanwhile, I&amp;rsquo;d named the new map-version repo &amp;lsquo;iss&amp;rsquo; and published it from that repo - so its link was, you guessed it &lt;a href="https://iankulin.github.io/iss/"&gt;https://iankulin.github.io/iss/&lt;/a&gt; After a bit of shuffling around, the old one is now working correctly at &lt;a href="https://iankulin.github.io/cwd/iss/"&gt;https://iankulin.github.io/cwd/iss/&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="wall-5---vite-again"&gt;Wall 5 - Vite again?&lt;/h4&gt;
&lt;p&gt;Meanwhile, the new version, with the moving map was clearly loading the HTML, but no sign of the ISS image, the .css or .js&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m in unfamiliar territory here with Vite, but since I started with the bundled my-app app, I&amp;rsquo;m sort of expecting everything to just work, unless it&amp;rsquo;s something to do this the GitHub Pages hosting. I do go down a rabbit hole about how they are designed for Jekyll. But a simpler explanation might be the links to those resources in the HTML. They all had a forward slash in front of them, like&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/assets/iss.d6272ccf.png&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;where as I would have been expecting&lt;/p&gt;
&lt;p&gt;&lt;code&gt;assets/iss.d6272ccf.png&lt;/code&gt; or even &lt;code&gt;./assets/iss.d6272ccf.png&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;(a side note - I have no idea why *ix systems use &lt;code&gt;./&lt;/code&gt; to mean the current directory. It would be like prefixing everyone&amp;rsquo;s name with &lt;em&gt;parent&amp;rsquo;s child&lt;/em&gt; when you&amp;rsquo;re talking to them).&lt;/p&gt;
&lt;img src="https://blog.iankulin.com/images/soanyway.jpg" width="74" alt=""&gt;
&lt;p&gt;So anyway, I just started manually editing the built code which doesn&amp;rsquo;t seem like great practice:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-01-18-at-10.16.35-am.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;It didn&amp;rsquo;t immediately solve my problem, but I&amp;rsquo;m not sure what the update rate for Pages is, and when I looked the next morning, it was working.&lt;/p&gt;
&lt;h4 id="wall-6---the-last-90"&gt;Wall 6 - The last 90%&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Ninety%E2%80%93ninety_rule"&gt;Tom Cargill&lt;/a&gt; (Bell Labs in the 80&amp;rsquo;s) is credited with noticing that the first 90% of the code accounts for the first 90% of the development time and the remaining 10% accounts for the remaining 90%. It certainly feels right that solving the problems and getting the MVP/prototype up and working - aka the fun bit, is not even the half way point in a project.&lt;/p&gt;
&lt;p&gt;Even in this very simple app, with the shortcuts I&amp;rsquo;ve made, there&amp;rsquo;s still significant work to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I need to disable the user&amp;rsquo;s ability to scroll around the map - since it breaks the illusion that the ISS picture is map of the map (or correctly render it as a map layer).&lt;/li&gt;
&lt;li&gt;There needs to be a delay or something so the first render of the map is based on a fetched ISS position.&lt;/li&gt;
&lt;li&gt;It ISS picture is not quite in the centre - it&amp;rsquo;s not noticeable on a laptop, but on mobile it&amp;rsquo;s very evident.&lt;/li&gt;
&lt;li&gt;Actually, the whole mobile experience needs a bit of work. That lat/long display does not fit as written. Better to pop the elements into a grid so we can stack them when the screen gets narrow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Will I do these things? Is the trip worth the gas? I need to balance progressing in the course with the things I can learn by finishing everything off carefully. In this case, since I&amp;rsquo;m way, way past what the course exercise was, I&amp;rsquo;ll leave it as is. Eventually I&amp;rsquo;ll work back through my GitHub with a recruiter&amp;rsquo;s eye, and things like this will need fixed to production standards or made private.&lt;/p&gt;</description></item></channel></rss>