about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--elm/cyoa/elm-stuff/0.19.1/d.datbin1554 -> 1594 bytes
-rw-r--r--js/seed/src/app.js40
-rw-r--r--js/seed/src/dev.js74
-rw-r--r--js/seed/src/view.js17
-rw-r--r--js/seed/style.css4
5 files changed, 124 insertions, 11 deletions
diff --git a/elm/cyoa/elm-stuff/0.19.1/d.dat b/elm/cyoa/elm-stuff/0.19.1/d.dat
index 7223730..7587e5b 100644
--- a/elm/cyoa/elm-stuff/0.19.1/d.dat
+++ b/elm/cyoa/elm-stuff/0.19.1/d.dat
Binary files differdiff --git a/js/seed/src/app.js b/js/seed/src/app.js
index 5fd577c..34b4579 100644
--- a/js/seed/src/app.js
+++ b/js/seed/src/app.js
@@ -5,9 +5,11 @@ import { initialState, cloneState } from './state.js';
 import { update } from './update.js';
 import { view } from './view.js';
 import { fetchPokemon } from './api.js';
+import { initDevMode } from './dev.js';
 
 const root = document.getElementById('app');
 let state = cloneState(initialState);
+let dev;
 
 /**
  * Entrypoint for the app.
@@ -90,6 +92,7 @@ function dispatch(action) {
   const prevState = state;
   state = update(state, action);
   if (devMode) {
+    dev.pushState(state);
     console.groupCollapsed(`Action: ${action.type}`);
     console.log('Payload:', action.payload);
     console.log('Prev state:', prevState);
@@ -111,7 +114,7 @@ function handleInput(e) {
 /**
  * Handles form submission, triggers async fetch, and dispatches state updates.
  *
- * Why handle async here? Keeps update/view pure and side-effect free, following functional programming best practices.
+ * Why handle async here? Keeps update/view pure and centralizes side-effect.
  */
 async function handleSubmit(e) {
   e.preventDefault();
@@ -126,4 +129,37 @@ async function handleSubmit(e) {
 }
 
 // Initial render
-doRender(); 
\ No newline at end of file
+doRender();
+
+// After devMode is set
+if (devMode) {
+  dev = initDevMode({
+    getState: () => state,
+    setState: s => { state = s; },
+    render: doRender
+  });
+}
+
+function updateHistoryInfo() {
+  if (!devMode || !dev) return;
+  dev.update();
+}
+
+function setHistoryPointer(idx) {
+  const info = dev.getHistoryInfo();
+  if (idx < 1 || idx > info.length) return;
+  const newState = dev.setPointer(idx - 1);
+  if (newState) {
+    state = newState;
+    doRender();
+    updateHistoryInfo();
+  }
+}
+
+function handleSliderChange(e) {
+  setHistoryPointer(Number(e.target.value));
+}
+
+function handleStepperChange(e) {
+  setHistoryPointer(Number(e.target.value));
+} 
\ No newline at end of file
diff --git a/js/seed/src/dev.js b/js/seed/src/dev.js
new file mode 100644
index 0000000..ee1a6e7
--- /dev/null
+++ b/js/seed/src/dev.js
@@ -0,0 +1,74 @@
+// devMode.js
+// Minimal, single-file dev mode with scriptable console API
+
+/**
+ * Initialize dev mode: exposes a scriptable API for stepping through state history.
+ * @param {object} opts
+ * @param {function} opts.getState - returns current app state
+ * @param {function} opts.setState - sets app state
+ * @param {function} opts.render - triggers app re-render
+ */
+export function initDevMode({ getState, setState, render }) {
+  let history = [];
+  let pointer = -1;
+  let firstLoad = true;
+
+  function pushState(state) {
+    if (pointer < history.length - 1) history = history.slice(0, pointer + 1);
+    history.push(clone(state));
+    pointer = history.length - 1;
+    logInstructions();
+  }
+  function goTo(idx) {
+    if (idx < 0 || idx >= history.length) return;
+    pointer = idx;
+    setState(clone(history[pointer]));
+    render();
+    logInstructions();
+  }
+  function next() {
+    if (pointer < history.length - 1) goTo(pointer + 1);
+  }
+  function prev() {
+    if (pointer > 0) goTo(pointer - 1);
+  }
+  function get() {
+    return history[pointer];
+  }
+  function clone(obj) {
+    return JSON.parse(JSON.stringify(obj));
+  }
+  function table(obj) {
+    console.table(dev.history);
+  }
+  function logInstructions() {
+    if (firstLoad) {
+      console.log('[DevMode] State history debugger');
+      console.log('Usage:');
+      console.log('- dev.next()   // step forward');
+      console.log('- dev.prev()   // step backward');
+      console.log('- dev.goTo(n)  // jump to state n (1-based)');
+      console.log('- dev.get()    // get current state');
+      console.log('- dev.table()  // display history as a table');
+      console.log('- dev.history  // array of all states');
+      console.log('- dev.pointer  // current pointer (0-based)');
+      firstLoad = false;
+    }
+  }
+
+  // Expose API globally for console use
+  window.dev = {
+    next,
+    prev,
+    goTo,
+    get,
+    table,
+    get pointer() { return pointer; },
+    get history() { return history.slice(); },
+  };
+
+  // Initial state
+  pushState(getState());
+
+  return { pushState };
+} 
\ No newline at end of file
diff --git a/js/seed/src/view.js b/js/seed/src/view.js
index cdf1d08..5feef6e 100644
--- a/js/seed/src/view.js
+++ b/js/seed/src/view.js
@@ -22,26 +22,29 @@
  */
 export function view(state) {
   return `
-    <main role="main">
+    <h1>PokéDex</h1>
+    <container>
       <form id="search-form" autocomplete="off">
-        <label for="pokemon-query">Pokémon Name</label>
+        <label for="pokemon-query">Pokémon Name (or number)</label>
         <input id="pokemon-query" type="text" value="${escape(state.query)}" placeholder="e.g. pikachu" aria-label="Pokémon Name" required />
         <button type="submit" ${state.loading ? 'disabled' : ''}>${state.loading ? 'Loading...' : 'Search'}</button>
       </form>
       ${state.error ? `<div class="error" role="alert" tabindex="-1">${escape(state.error)}</div>` : ''}
       ${state.pokemon ? renderResult(state.pokemon) : ''}
-    </main>
+    </container>
   `;
 }
 
 function renderResult(pokemon) {
   return `
     <div class="result">
+      <h2>${capitalize(pokemon.name)} (#${pokemon.id})</h2>
       <img class="pokemon-sprite" src="${pokemon.sprites.front_default}" alt="${escape(pokemon.name)} sprite" />
-      <div><strong>${capitalize(pokemon.name)}</strong> (#${pokemon.id})</div>
-      <div>Type: ${pokemon.types.map(t => escape(t.type.name)).join(', ')}</div>
-      <div>Height: ${pokemon.height / 10} m</div>
-      <div>Weight: ${pokemon.weight / 10} kg</div>
+      <ul>
+        <li>Type: <b>${pokemon.types.map(t => capitalize(escape(t.type.name))).join(', ')}</b></li>
+        <li>Height: <b>${pokemon.height / 10} m</b></li>
+        <li>Weight: <b>${pokemon.weight / 10} kg</b></li>
+      </ul>
     </div>
   `;
 }
diff --git a/js/seed/style.css b/js/seed/style.css
index 1d70c7f..da35a7a 100644
--- a/js/seed/style.css
+++ b/js/seed/style.css
@@ -67,13 +67,13 @@ button:disabled {
   border: 1.5px solid var(--color-result-border);
   border-radius: 0.3em;
   background: var(--color-result-bg);
-  text-align: center;
 }
 .pokemon-sprite {
   width: 120px;
   height: 120px;
   object-fit: contain;
-  margin-bottom: 0.5em;
+  margin: 0 auto;
+  display: block;
 }
 .error {
   color: var(--color-error);