DEV Community

Cover image for Juris and AI collaboration
ArtyProg
ArtyProg

Posted on • Edited on

Juris and AI collaboration

What is nice about Juris, it is that it's just Javascript. Juris is all based on Javascript objects.
So AI have no problems to handle the Juris code.

I asked Claude to enhance my TrafficLights example.

The result is pretty nice, and very legible. Nothing tricky.
Beware 'bss' have nothing to do with Juris, it is just a simple and handy CssInJS library I used when developping with Mithril my first beloved library .
Sorry Mithril, I am in Juris now 😊.

TrafficLights enhanced by Claude:

import b from 'bss'

// ================================
// 📋 CONFIGURATION - All settings in one place
// ================================
// This is where you can easily change colors, sizes, and timing without digging through code

const CONFIG = {
  // 🎨 Colors used throughout the app
  colors: {
    background: "#252526",      // Dark background
    border: "white",            // White borders
    container: "black",         // Traffic light box background
    text: {
      light: "white",           // White text
      dark: "black"             // Black text
    },
    lights: {
      red: "red",               // Red traffic light
      orange: "orange",         // Orange traffic light
      green: "green"            // Green traffic light
    },
    ui: {
      primary: "#333",          // Main button color
      primaryHover: "#555",     // Button hover color
      danger: "crimson",        // Stop button color
      disabled: "gray"          // Disabled button color
    }
  },

  // 📏 Spacing - consistent gaps and margins
  spacing: {
    xs: "0.25rem",    // Extra small: 4px
    sm: "0.5rem",     // Small: 8px
    md: "1rem",       // Medium: 16px
    lg: "1.25rem",    // Large: 20px
    xl: "2rem",       // Extra large: 32px
    xxl: "6rem"       // Extra extra large: 96px
  },

  // 📐 Sizes - dimensions for various elements
  sizes: {
    lightSize: "55px",                    // How big each light is
    buttonWidth: "3.5rem",                // Width of control buttons
    containerWidth: "45px",               // Width of traffic light box
    containerHeight: "270px",             // Height of traffic light box
    orientationButtonWidth: "140px"       // Width of orientation toggle button
  },

  // ⏰ Timing - how fast things happen
  timing: {
    lightDuration: 500,          // How long each light stays on (milliseconds)
    transitionDelay: 200,        // Pause between lights
    cycleDelay: 300,             // Pause between complete cycles
    cssTransition: "all 0.63s ease"  // CSS animation speed
  }
};

// 🏁 Starting state - what the app looks like when it first loads
const INITIAL_STATE = {
  red: "hidden",        // Red light starts off
  orange: "hidden",     // Orange light starts off
  green: "hidden",      // Green light starts off
  orientation: "column", // Lights arranged vertically
  cycling: false        // Auto-cycling is off
};

// 🔄 The order lights flash in auto-cycle mode
const LIGHT_SEQUENCE = ["red", "orange", "green"];

// ================================
// 🛠️ HELPER FUNCTIONS
// ================================

// Simple delay function - makes code wait for a specified time
const delay = (milliseconds) => new Promise(resolve => setTimeout(resolve, milliseconds));

// Sets up the basic styles for the page
const createBaseStyles = () => {
  // Style the page background
  b.css({
    body: {
      bc: CONFIG.colors.background  // bc = background-color
    }
  });

  // Style all buttons to look consistent
  b.css({
    button: {
      w: CONFIG.sizes.buttonWidth,      // w = width
      ta: 'center',                     // ta = text-align
      border: 'none',                   // Remove default border
      p: CONFIG.spacing.sm,             // p = padding
      br: '3px',                        // br = border-radius (rounded corners)
      cursor: 'pointer',                // Show hand cursor on hover
      transition: CONFIG.timing.cssTransition  // Smooth animations
    }
  });
};

// ================================
// 🎨 STYLE GENERATORS - Functions that create CSS styles
// ================================

// Creates styles for individual lights (red, orange, green)
const createLightStyles = (color, isVisible) => {
  // Base styles that all lights share
  const baseStyle = `
    w ${CONFIG.sizes.lightSize}
    h ${CONFIG.sizes.lightSize}
    br 50%
    bc ${color}
    m 6px
    transition ${CONFIG.timing.cssTransition}`;

  // Different styles based on whether light is on or off
  const stateStyle = isVisible 
    ? `opacity 1; box-shadow: 0px 0px 40px 10px ${color}`  // Bright with glow
    : 'opacity 0.3';  // Dim

  return b`${baseStyle} ${stateStyle}`;
};

// Collection of button styles for different purposes
const buttonStyles = {
  // Styles for light control buttons (Red, Orange, Green)
  lightControl: (backgroundColor, textColor) => b`bc ${backgroundColor}; c ${textColor}`,

  // Styles for the "Start Cycle" button
  cycleStart: (isCycling) => b`
    bc ${isCycling ? CONFIG.colors.ui.disabled : CONFIG.colors.lights.green}
    c ${CONFIG.colors.text.light}
    ${isCycling ? 'opacity 0.6; cursor not-allowed' : ''}`,

  // Styles for the "Stop Cycle" button
  cycleStop: (isCycling) => b`
    bc ${isCycling ? CONFIG.colors.ui.danger : CONFIG.colors.ui.disabled}
    c ${CONFIG.colors.text.light}
    ${!isCycling ? 'opacity 0.6; cursor not-allowed' : ''}`,

  // Styles for the orientation toggle button
  orientation: () => b`
    w ${CONFIG.sizes.orientationButtonWidth} !important
    bc ${CONFIG.colors.ui.primary}
    c ${CONFIG.colors.text.light}
    `.$hover({
      bc: 'red'  // Changes to red when you hover over it
    })
};

// ================================
// 🧠 BUSINESS LOGIC - The "brain" of the application
// ================================

// This is the main controller that handles all the traffic light logic
const TrafficLightController = (context) => {
  let cyclingTimeoutId = null;  // Keeps track of the auto-cycle timer

  // Shortcuts to make the code cleaner
  const setState = context.setState;
  const getState = context.getState;

  // Helper functions for working with lights
  const setLightState = (color, state) => setState(color, state);
  const getLightState = (color) => getState(color);
  const isCycling = () => getState("cycling");

  // Turn off all lights at once
  const turnOffAllLights = () => {
    LIGHT_SEQUENCE.forEach(color => setLightState(color, "hidden"));
  };

  // Toggle a single light on/off (only works when not auto-cycling)
  const toggleLight = (color) => {
    if (isCycling()) return;  // Don't allow manual control during auto-cycle
    const currentState = getLightState(color);
    setLightState(color, currentState === "visible" ? "hidden" : "visible");
  };

  // Start the automatic cycling of lights
  const startCycling = () => {
    if (isCycling()) return;  // Don't start if already cycling
    setState("cycling", true);
    cycleLights();
  };

  // Stop the automatic cycling
  const stopCycling = () => {
    setState("cycling", false);
    if (cyclingTimeoutId) {
      clearTimeout(cyclingTimeoutId);  // Stop the timer
      cyclingTimeoutId = null;
    }
    turnOffAllLights();  // Turn off all lights when stopping
  };

  // The main cycling logic - makes lights flash in sequence
  const cycleLights = async () => {
    // Clear any existing timer to prevent conflicts
    if (cyclingTimeoutId) {
      clearTimeout(cyclingTimeoutId);
    }

    try {
      // Start with all lights off
      turnOffAllLights();
      await delay(CONFIG.timing.transitionDelay);

      // Go through each light in sequence
      for (const color of LIGHT_SEQUENCE) {
        if (!isCycling()) return;  // Stop if cycling was turned off

        setLightState(color, "visible");           // Turn light on
        await delay(CONFIG.timing.lightDuration);  // Wait
        setLightState(color, "hidden");            // Turn light off
        await delay(CONFIG.timing.transitionDelay); // Brief pause
      }

      // If still cycling, schedule the next cycle
      if (isCycling()) {
        cyclingTimeoutId = setTimeout(() => cycleLights(), CONFIG.timing.cycleDelay);
      }
    } catch (error) {
      console.error("Error during light cycling:", error);
      stopCycling();  // Stop cycling if something goes wrong
    }
  };

  // Switch between vertical and horizontal light arrangement
  const toggleOrientation = () => {
    const current = getState("orientation");
    setState("orientation", current === "column" ? "row" : "column");
  };

  // Return all the functions that other parts of the app can use
  return {
    toggleLight,
    startCycling,
    stopCycling,
    toggleOrientation,
    isCycling,
    getLightState,
    getState
  };
};

// ================================
// 🎭 UI COMPONENTS - The visual parts of the app
// ================================

// Individual light component (red, orange, or green circle)
const Light = (props, context) => ({
  div: {
    className: () => {
      const isVisible = context.getState(props.name) === 'visible';
      return createLightStyles(props.color, isVisible);
    }
  }
});

// Container that holds all three lights
const LightContainer = (props, context) => ({
  div: {
    className: b`display flex; jc center`,  // Center the lights
    children: [{
      div: {
        className: () => b`
          display flex
          flex-direction ${context.getState("orientation")}
          gap ${CONFIG.spacing.lg}
          mt ${CONFIG.spacing.md}`,
        children: [
          { Light: { color: CONFIG.colors.lights.red, name: 'red' } },
          { Light: { color: CONFIG.colors.lights.orange, name: 'orange' } },
          { Light: { color: CONFIG.colors.lights.green, name: 'green' } },
        ]
      }
    }]
  } 
});

// Control panel with all the buttons
const ControlPanel = (props, context) => {
  const controller = TrafficLightController(context);

  return { 
    div: {
      className: b`
        display flex
        jc center
        gap ${CONFIG.spacing.sm}
        padding-bottom ${CONFIG.spacing.xl}
        mt 6rem`, 
      children: [
        // Individual light control buttons
        { button: {
            className: buttonStyles.lightControl(CONFIG.colors.lights.red, CONFIG.colors.text.light),
            text: "Red",
            onclick: () => controller.toggleLight("red")
        }},
        { button: {
            className: buttonStyles.lightControl(CONFIG.colors.lights.orange, CONFIG.colors.text.dark),
            text: "Orange", 
            onclick: () => controller.toggleLight("orange")
        }},
        { button: {
            className: buttonStyles.lightControl(CONFIG.colors.lights.green, CONFIG.colors.text.light),
            text: "Green",
            onclick: () => controller.toggleLight("green")
        }},

        // Auto-cycle control buttons
        { button: {
            className: () => buttonStyles.cycleStart(controller.isCycling()),
            text: () => controller.isCycling() ? "Cycling..." : "Start Cycle",
            onclick: () => controller.startCycling()
        }},
        { button: {
            className: () => buttonStyles.cycleStop(controller.isCycling()),
            text: "Stop Cycle",
            onclick: () => controller.stopCycling()
        }}        
      ]
    }
  };
};

// The main traffic light box that contains everything
const TrafficLightBox = (props, context) => ({ 
  div: {
    className: b`
      br 5px
      border 1px solid ${CONFIG.colors.border}
      p ${CONFIG.spacing.md}
      bc ${CONFIG.colors.container}
      w ${CONFIG.sizes.containerWidth}
      h ${CONFIG.sizes.containerHeight}
      m 0 auto
      mt ${CONFIG.spacing.xxl}`, 
    children: [
      { LightContainer: {} },
      { ControlPanel: {} },
    ]
  }
});

// Button to switch between vertical and horizontal layout
const OrientationToggle = (props, context) => {
  const controller = TrafficLightController(context);

  return {
    button: {
      className: buttonStyles.orientation(),
      text: () => {
        const orientation = controller.getState("orientation");
        return orientation === "row" ? "Arrange Vertical" : "Arrange Horizontal";
      },
      onclick: () => controller.toggleOrientation()
    }
  };
};

// Container for the orientation toggle button
const OrientationContainer = () => ({
  div: {
    className: b`
      w ${CONFIG.sizes.orientationButtonWidth}
      m 0 auto
      mt ${CONFIG.spacing.md}`,
    children: [{ OrientationToggle: {} }]
  }
});

// ================================
// 🔧 HEADLESS COMPONENTS - Logic without visual elements
// ================================

// This component provides the cycling API but doesn't render anything
const TrafficLightsManager = (props, context) => {
  const controller = TrafficLightController(context);

  return {
    api: {
      cycleLights: controller.startCycling,
      stopCycling: controller.stopCycling
    }
  };
};

// ================================
// 🚀 APPLICATION SETUP
// ================================

// Function that sets up and creates the entire application
const initializeApp = () => {
  createBaseStyles();  // Apply the base CSS styles

  return new Juris({
    // Starting state of the application
    states: INITIAL_STATE,

    // All the visual components
    components: {
      Light,
      LightContainer,
      ControlPanel,
      TrafficLightBox,
      OrientationToggle,
      OrientationContainer
    },

    // Non-visual components that provide functionality
    headlessComponents: {
      TrafficLightsManager: { 
        fn: TrafficLightsManager, 
        options: { autoInit: true } 
      }
    },

    // What gets displayed on the page
    layout: [
      { TrafficLightBox: {} }, 
      { OrientationContainer: {} }
    ]
  });
};

// ================================
// 🎬 START THE APP
// ================================

const app = initializeApp();
app.render();

// 📚 HOW TO CUSTOMIZE:
// 1. Change colors: Edit the CONFIG.colors section
// 2. Adjust timing: Modify CONFIG.timing values
// 3. Resize elements: Update CONFIG.sizes
// 4. Add new lights: Add to LIGHT_SEQUENCE and update components
// 5. Change layout: Modify the layout array in initializeApp
Enter fullscreen mode Exit fullscreen mode

Rating from ChatGPT

Category Score Comments
Structure & Modularity ✅ 10/10 Excellent separation of concerns (config, logic, UI). Easy to navigate and extend.
Readability & Comments ✅ 10/10 Well-documented with friendly, helpful inline comments. Emojis + clear headers = super readable.
Styling System (bss) ✅ 9/10 Consistent and compact, though a small learning curve if you're unfamiliar with bss.
Logic & Flow ✅ 9/10 Async cycleLights is clean. Good use of promises and delays. Minor edge cases could be expanded.
Reusability / Scalability ⚠️ 7.5/10 Mostly hardcoded for 3 lights. Would benefit from making lights dynamic via config.
Accessibility / UX ⚠️ 6.5/10 Could improve on keyboard access, color contrast, and ARIA roles for screen readers.
Framework Usage ✅ 9/10 Assuming familiarity with Juris, it's cleanly integrated. If not, maybe a bit opaque to newcomers.

Top comments (0)