Lets review the following implementation of react in gleam  

START OF OUTDATED IMPLEMENTATION:
The following text is a Git repository with code. The structure of the text are sections that begin with ----, followed by a single line containing the file path and file name, followed by a variable amount of lines containing the file contents. The text representing the Git repository ends when the symbols --END-- are encounted. Any further text beyond --END-- are meant to be interpreted as instructions using the aforementioned Git repository as context.
----
.github/workflows/test.yml
name: test

on:
  push:
    branches:
      - master
      - main
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3.0.0
      - uses: erlef/setup-beam@v1.14.0
        with:
          otp-version: "25.1"
          gleam-version: "0.28.2"
          # elixir-version: "1.14.1"
      - run: gleam format --check src test
      - run: gleam deps download
      - run: gleam test

----
README.md
# react_gleam

React bindings for Gleam inspired by [Lustre](https://github.com/hayleigh-dot-dev/gleam-lustre/blob/main/src/lustre/attribute.gleam) and [ReasonReact](https://reasonml.github.io/reason-react/).

---

[![Package Version](https://img.shields.io/hexpm/v/react_gleam)](https://hex.pm/packages/react_gleam)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/react_gleam/)

react_gleam provides React bindings for [Gleam](https://gleam.run/) giving you a type safe, and expressive React experience out of the box.

```gleam
import gleam/int
import react_gleam.{component, render}
import react_gleam/element.{Element, div, text}
import react_gleam/hook.{use_state}
import react_gleam/event.{on_click}

pub fn main() {
  render(app(), "#app")
}

fn app() -> Element {
  div(
    [],
    [
      div([], [text("Welcome to Gleam React!")]),
      greet(name: "Joe"),
      counter(count: 6),
    ],
  )
}

fn greet(name name: String) -> Element {
  div([], [text("Hello " <> name <> "!")])
}

fn counter(count init_count: Int) -> Element {
  use <- component()

  let #(count, set_count) = use_state(fn() { init_count })

  div(
    [],
    [
      div([on_click(fn(_) { set_count(fn(prev) { prev + 1 }) })], [text("+")]),
      div([], [text(int.to_string(count))]),
      div([on_click(fn(_) { set_count(fn (prev) { prev - 1 }) })], [text("-")]),
    ],
  )
}

```

## Hooks

 react_gleam supports the `use_effect`, `use_state`, `use_reducer`, `use_memo`, `use_callback`, `use_id`, `use_transition`, `use_debug_value`, `use_ref`, and `use_context` hooks from React as well as a `use_url` hook for simple routing.

## Routing

`react_gleam/router` provides a `use_url` hook, `push`, and `back` function to enable basic routing.

```gleam
import react_gleam.{component, render}
import react_gleam/element.{div, text}
import react_gleam/router.{use_url}

pub fn main() {
  render(app(), "#app")
}

fn app() {
  use <- component()

  let url = use_url()

  case url.path {
    [] -> div([], [text("home")]) // Matches /
    ["about"] -> div([], [text("about")]) // Matches /about
    _ -> div([], [text("404")]) // Matches everything else
  }
}

```

## Installation

If available on Hex this package can be added to your Gleam project:

```sh
gleam add react_gleam

yarn add react react-dom
```

----
example/README.md
# example
Design Document: Implementing preact_gleam – Preact Bindings for Gleam 🚀
Introduction
This document outlines the steps required to reimplement the react_gleam library for Preact, resulting in a new library called preact_gleam. The goal is to provide Gleam developers with Preact bindings that are type-safe and expressive, similar to the existing react_gleam but leveraging Preact's lightweight performance benefits.

Table of Contents
Analysis of react_gleam
Overview
Key Components
Understanding Preact
Differences from React
Compatibility Considerations
Step-by-Step Plan
Outline of Changes
Kanban Board
Design Document for preact_gleam
Module Structure
Implementing Hooks
Handling Events
JSX/Element Creation
Code Snippets and Examples
Sample Implementations
Next Steps
1. Analysis of react_gleam
Overview
react_gleam provides bindings for React within Gleam, allowing developers to write React components in Gleam with type safety and functional programming benefits. It includes:

Components & Elements: Functions to create React components and elements.
Hooks: Support for React hooks like use_state, use_effect, use_reducer, etc.
Event Handling: Functions to handle events like clicks, inputs, etc.
Routing: Basic routing support using a use_url hook.
Key Components
Component Module: Functions for creating components.
Element Module: Functions for creating HTML elements (e.g., div, span).
Hook Module: Implements React hooks.
Event Module: Event handling functions.
Attribute Module: Manage element attributes.
FFI Module (ffi.mjs): JavaScript interop, interfacing with React's API.
2. Understanding Preact
Differences from React
Size: Preact is significantly smaller (~3KB gzipped) compared to React.
API Compatibility: Preact aims for compatibility with the React API but may have subtle differences.
Features: Some React features may not be present or may behave differently in Preact.
Performance: Preact is designed for performance and minimal footprint.
Compatibility Considerations
Hooks: Preact supports hooks, but certain hooks may need adaptation.
Context: Context API is available but may have differences.
Synthetic Events: Preact uses native events instead of React's synthetic events.
Fragment Support: Ensure that fragment support aligns with Preact's implementation.
3. Step-by-Step Plan
Outline of Changes
Replace React Imports with Preact: Update JavaScript interop to use Preact's API.
Adjust FFI Module: Modify ffi.mjs to interface with Preact instead of React.
Update Hook Implementations: Ensure hooks in hook.gleam align with Preact's hooks.
Event Handling: Adapt event modules to handle native events if necessary.
Component Creation: Ensure component functions create Preact components correctly.
Testing: Rigorous testing to verify compatibility and functionality.
Documentation Update: Update README and inline documentation to reflect Preact usage.
Kanban Board
To Do
Analyze ffi.mjs and identify React-specific code.
Research Preact's API for equivalent functionality.
Plan for differences in event handling.
In Progress
Modify ffi.mjs to replace React with Preact.
Update hook implementations in Gleam modules.
Adjust element creation functions as needed.
Done
Initial analysis of react_gleam.
Understanding Preact differences.
4. Design Document for preact_gleam
Module Structure
preact_gleam.gleam: Core module with functions like render and component.
preact_gleam/element.gleam: Functions for creating DOM elements.
preact_gleam/hook.gleam: Implementations of Preact hooks.
preact_gleam/event.gleam: Event handling functions.
preact_gleam/attribute.gleam: Managing element attributes.
src/ffi.mjs: JavaScript FFI code interfacing with Preact.
Implementing Hooks
Identify Preact Hook Equivalents: Map React hooks to Preact equivalents.
Modify Hook Functions: Adjust Gleam hook functions to align with Preact's API.
Consider Missing Hooks: Implement workarounds or note limitations for any hooks not supported in Preact.
Handling Events
Native Events: Preact uses native DOM events. Ensure event handlers in Gleam modules accommodate this.
Event Normalization: Remove any React Synthetic Event handling.
JSX/Element Creation
Element Functions: Ensure functions like div, span, etc., create Preact elements.
Component Function: Adjust component creation to fit Preact's component model.
Attributes and Props: Verify that attribute handling aligns with Preact's expectations.
5. Code Snippets and Examples
5.1 Modifying FFI Module (ffi.mjs)
Original React Interop Code (Simplified Example):


import { createElement, useState, useEffect } from "react";
import { createRoot } from "react-dom/client";

export const render = (app, selector) => {
  const rootElement = document.querySelector(selector);
  const reactRoot = createRoot(rootElement);
  reactRoot.render(app);
};
Updated Preact Interop Code:


import { h, render as preactRender } from "preact";
import { useState, useEffect } from "preact/hooks";

export const render = (app, selector) => {
  const rootElement = document.querySelector(selector);
  preactRender(app, rootElement);
};
5.2 Updating Hook Implementations in hook.gleam
Original Hook Function (Simplified):


pub external fn use_state(initial: fn() -> a) -> #(a, fn(fn(a) -> a) -> Nil) =
  "../ffi.mjs" "useState"
No change in Gleam code is necessary since the external function remains the same. However, ensure that the useState in ffi.mjs references Preact's useState.

5.3 Adjusting Event Handling
Since Preact uses native events, we need to make sure our event handlers can work with them.

Example Changes:

Remove any references to React's SyntheticEvent.
Update event handler functions to work with native events.
Gleam Event Module Adjustment:


pub fn on_click(handler: fn(Event) -> Nil) -> Attribute {
  attribute.event("click", handler)
}
6. Next Steps
Create a New Repository: Set up a new Git repository for preact_gleam.
Set Up Project Structure: Initialize Gleam project with necessary configuration.
Implement Modules: Start coding based on the design document.
Testing: Write tests to verify each component, especially focusing on differences from React.
Examples and Documentation: Provide examples and update documentation to help users get started.
Package for Distribution: Prepare the package for publishing on Hex and NPM if necessary.
Conclusion
By carefully analyzing the differences between React and Preact and understanding the existing react_gleam implementation, we can methodically adjust the library to support Preact. This involves updating JavaScript FFI code, ensuring compatibility in hook implementations, and adjusting event handling. The result will be preact_gleam, providing Gleam developers with efficient and type-safe bindings to Preact.

Important Notes
Avoiding Copyright Issues: Ensure that any code used from Preact's source is properly licensed and does not violate any copyrights.
Testing for Compatibility: Even though Preact aims for React compatibility, edge cases may exist. Rigorous testing is necessary.
Community and Support: Engage with the Gleam and Preact communities for support and to gather feedback on the implementation.
Alternative Approach
Given that Preact aims to be compatible with the React API, another approach is to make minimal changes to the existing react_gleam library to point to Preact libraries instead of React. This would involve:

Changing Package Dependencies: Replace React and ReactDOM with Preact in package.json.
Aliasing React to Preact: Use tools like webpack aliases to redirect React imports to Preact.
Testing: Verify that the existing implementation works seamlessly with Preact.
This approach is quicker but may not fully leverage Preact's features or account for any API differences.

Ethical Considerations
Respecting Licenses: Ensure that all third-party code and libraries are used in compliance with their licenses.
Attribution: Provide proper attribution to any code or implementations referenced during development.
Open Source Contribution: By open-sourcing preact_gleam, you contribute to the developer community, fostering collaboration and innovation.
An example Gleam React project

## Quick start

```sh
yarn # Install dependencies
gleam build   # Build the project
yarn dev # Run the project
```
----
example/gleam.toml
name = "example"
version = "0.1.0"

target = "javascript"

# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
# licences = ["Apache-2.0"]
# description = "A Gleam library..."
# repository = { type = "github", user = "username", repo = "project" }
# links = [{ title = "Website", href = "https://gleam.run" }]

[dependencies]
gleam_stdlib = "~> 0.28.2"
react_gleam = "~> 0.5"

[dev-dependencies]
gleeunit = "~> 0.7"

----
example/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="./build/dev/javascript/example/main.js"></script>
  </body>
</html>

----
example/manifest.toml
# This file was generated by Gleam
# You typically do not need to edit this file

packages = [
  { name = "gleam_javascript", version = "0.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "E0E8D33461776BFCC124838183F85E430D3A71D7318F210C9DE0CFB52E5AC8DE" },
  { name = "gleam_stdlib", version = "0.28.2", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "088E334A00C9530D4B194F31406E51B6E4E4697C6A380D11591E60CC2F239724" },
  { name = "gleeunit", version = "0.10.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "ECEA2DE4BE6528D36AFE74F42A21CDF99966EC36D7F25DEB34D47DD0F7977BAF" },
  { name = "react_gleam", version = "0.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_javascript"], otp_app = "react_gleam", source = "hex", outer_checksum = "1D287619A0DE9FA3150CDE9DA7AA33916FF5BB73C51383DCDC809F48F6C6B1A8" },
]

[requirements]
gleam_stdlib = "~> 0.28.2"
gleeunit = "~> 0.7"
react_gleam = "~> 0.5"

----
example/package.json
{
  "name": "example",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "dev": "yarn vite"
  },
  "devDependencies": {
    "vite": "^3.2.4"
  }
}

----
example/src/counter.gleam
import gleam/int
import react_gleam.{component}
import react_gleam/element.{div, text}
import react_gleam/event.{on_click}
import react_gleam/hook.{use_state}

pub fn counter(count init_count: Int) {
  use <- component()

  let #(count, set_count) = use_state(fn() { init_count })

  div(
    [],
    [
      div([on_click(fn(_) { set_count(fn(prev) { prev + 1 }) })], [text("+")]),
      div([], [text(int.to_string(count))]),
      div([on_click(fn(_) { set_count(fn(prev) { prev - 1 }) })], [text("-")]),
    ],
  )
}

----
example/src/example.gleam
import react_gleam.{render}
import react_gleam/element.{div, h1, h2, text}
import greeter.{greet}
import counter.{counter}
import reducer_counter.{reducer_counter}

pub fn main() {
  render(app(), "#app")
}

fn app() {
  div(
    [],
    [
      h1([], [text("Welcome to Gleam React!")]),
      h2([], [text("A greeter with use_effect")]),
      greet(world: "world"),
      h2([], [text("A counter with use_state")]),
      counter(count: 6),
      h2([], [text("A counter with use_reducer")]),
      reducer_counter(),
    ],
  )
}

----
example/src/greeter.gleam
import gleam/option.{Some}
import gleam/list
import gleam/int
import gleam/float
import react_gleam.{component}
import react_gleam/element.{div, text}
import react_gleam/hook.{use_effect0, use_state}

const world = [
  "world", "wêreld", "botë", "ዓለም", "العالمية", "աշխարհ",
  "dünya", "mundua", "свет", "বিশ্ব", "世界", "mondu", "svijet",
  "svět", "verden", "wereld-", "mondo", "maailmas", "maailman-", "monde",
  "wrâld", "mundo", "სამყარო", "Welt",
]

pub fn greet(world world_init: String) {
  use <- component()

  let #(world, set_world) = use_state(fn() { world_init })

  use_effect0(fn() {
    let timeout =
      set_interval(fn() { set_world(fn(_) { get_random_world() }) }, 3000)

    Some(fn() { clear_interval(timeout) })
  })

  div([], [text("Hello " <> world <> "!")])
}

external type Timeout

external fn set_interval(fn() -> Nil, ms: Int) -> Timeout =
  "" "setInterval"

external fn clear_interval(timeout: Timeout) -> Nil =
  "" "clearInterval"

external fn math_random() -> Float =
  "" "Math.random"

fn get_random_world() -> String {
  let index =
    float.round(float.floor(int.to_float(list.length(world)) *. math_random()))

  case list.at(world, index) {
    Ok(w) -> w
    _ -> "world"
  }
}

----
example/src/main.js
import { main } from "./example.mjs";

main();

----
example/src/reducer_counter.gleam
import gleam/int
import react_gleam.{component}
import react_gleam/element.{div, text}
import react_gleam/hook.{use_reducer}
import react_gleam/event.{on_click}

type Action {
  Inc
  Dec
}

type CountState {
  Count(Int)
}

fn reducer(state: CountState, action: Action) -> CountState {
  let Count(current_count) = state

  case action {
    Inc -> Count(current_count + 1)
    Dec -> Count(current_count - 1)
  }
}

pub fn reducer_counter() {
  use <- component()

  let #(Count(count), dispatch) = use_reducer(reducer, Count(0))

  div(
    [],
    [
      div([on_click(fn(_) { dispatch(Dec) })], [text("-")]),
      div([], [text(int.to_string(count))]),
      div([on_click(fn(_) { dispatch(Inc) })], [text("+")]),
    ],
  )
}

----
gleam.toml
name = "react_gleam"
version = "0.5.1"

licences = ["MIT"]
description = "React bindings for Gleam"
repository = { type = "github", user = "brettkolodny", repo = "react-gleam"}

target = "javascript"

[dependencies]
gleam_stdlib = "~> 0.28.2"
gleam_javascript = "~> 0.4"

[dev-dependencies]
gleeunit = "~> 0.7"

----
manifest.toml
# This file was generated by Gleam
# You typically do not need to edit this file

packages = [
  { name = "gleam_javascript", version = "0.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "E0E8D33461776BFCC124838183F85E430D3A71D7318F210C9DE0CFB52E5AC8DE" },
  { name = "gleam_stdlib", version = "0.28.2", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "088E334A00C9530D4B194F31406E51B6E4E4697C6A380D11591E60CC2F239724" },
  { name = "gleeunit", version = "0.10.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "ECEA2DE4BE6528D36AFE74F42A21CDF99966EC36D7F25DEB34D47DD0F7977BAF" },
]

[requirements]
gleam_javascript = "~> 0.4"
gleam_stdlib = "~> 0.28.2"
gleeunit = "~> 0.7"

----
package.json
{
  "name": "react_gleam",
  "version": "1.0.0",
  "main": "index.js",
  "author": "brettkolodny <<PRESIDIO_ANONYMIZED_EMAIL_ADDRESS>>",
  "license": "MIT",
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

----
src/ffi.mjs
// Much of this code was lifted from
// https://github.com/hayleigh-dot-dev/gleam-lustre
// Thank you Hayleigh 💙

import {
  createElement,
  useState,
  useEffect,
  useReducer,
  createContext,
  useContext,
  useMemo,
  useCallback,
  useId,
  useTransition,
  useDebugValue,
} from "react";
import { createRoot } from "react-dom/client";
import * as Option from "../gleam_stdlib/gleam/option.mjs";
import * as GleamJS from "../gleam_javascript/gleam.mjs";
import * as Gleam from "./gleam.mjs";

// HOOKS ----------------------------------------------------------------------

export {
  useState,
  useEffect,
  useReducer,
  useMemo,
  useCallback,
  useId,
  useTransition,
  useDebugValue,
};

export const useEffectHook = (cb, props) => {
  const cbWrapper = () => {
    const cleanUp = cb();

    if (typeof cleanUp[0] === "function") {
      return cleanUp[0];
    }
  };
  useEffect(cbWrapper, props);
};

export const useEffect0 = (cb) => {
  const cbWrapper = () => {
    const cleanUp = cb();

    if (typeof cleanUp[0] === "function") {
      return cleanUp[0];
    }
  };
  useEffect(cbWrapper, []);
};

export const useRefHook = (v) => {
  let ref = {
    value: v,
    get current() {
      if (
        this.value.constructor.name !== "Some" &&
        !this.value.constructor.name !== "None"
      ) {
        if (this.value === null || this.value === undefined) {
          return new Option.None();
        } else {
          return new Option.Some(this.value);
        }
      }

      return this.value;
    },
    set current(v) {
      this.value = v;
    },
  };

  return ref;
};

/**
 * Retrieves the stored context provider
 * @param {string} key Key that the context is stored under
 * @returns {Gleam.Result<T>} Ok(T) if the context exists, Error if it doesn't
 */
export const useContextHook = (key) => {
  const contextProvider = contextsMap.get(key);

  if (!contextProvider) {
    return new Gleam.Error();
  }

  const context = useContext(contextProvider);

  return new Gleam.Ok(context);
};

// ELEMENTS -------------------------------------------------------------------

/**
 * Creates a react Element
 * @param {string} type Type of Element to create
 * @param {List<Children>} children List of child elements
 * @param {List<Attribute>} _attributes List of element attributes and props
 * @returns {Element} New element
 */
export const node = (type, attributes, children) => {
  const props = toProps(attributes);

  return createElement(
    type,
    props,
    ...children
      .toArray()
      .map((child) => (typeof child === "function" ? child() : child))
  );
};

/**
 * Returns a string to be used as the child of an element
 * @param {string} content
 * @returns {string} the original string
 */
export const text = (content) => content;

/**
 * A workaround for getting custom stateful components to work
 * @param {Element} element
 * @returns {Element}
 */
export const component = (element) => {
  return createElement(element);
};

// CONTEXT --------------------------------------------------------------------

/**
 * Gleam does not allow us to export a non-const value from a module so to work
 * around this we create a global variable to store all of the app's contexts
 * and supply functions to interact with them.
 */
const contextsMap = new Map();

/**
 * Set a context to be retrieved elsewhere in your application
 * @param {string} key
 * @param {any} value The context's value
 */
export const setContext = (key, value) => {
  const context = createContext(value);
  contextsMap.set(key, context);
};

/**
 * Retrieve a stored context
 * @param {string} key The key the context is stored under
 * @returns {Option.Some<any> | Option.None}
 */
export const getContext = (key) => {
  const context = contextsMap.get(key);

  if (context) {
    return new Option.Some(context);
  }

  return new Option.None();
};

export { useContext };

// UTILITY --------------------------------------------------------------------

/**
 * Render a React app to an element
 * @param {Element} app Root element of a React app
 * @param {string} selector Query selector for the element that the app will attach to
 */
export const render = (app, selector) => {
  const rootElement = document.querySelector(selector);
  const reactRoot = createRoot(rootElement);

  reactRoot.render(app);
};

// ATTRIBUTE ------------------------------------------------------------------

// HELPERS --------------------------------------------------------------------

const toProps = (attributes) => {
  const capitalize = (s) => s && s[0].toUpperCase() + s.slice(1);

  return Object.fromEntries(
    attributes.toArray().map((attr) => {
      if ("name" in attr && "ref" in attr) {
        return [attr.name, attr.ref];
      } else if ("name" in attr && "value" in attr) {
        return [attr.name, attr.value];
      } else if ("name" in attr && "handler" in attr) {
        return ["on" + capitalize(attr.name), (e) => attr.handler(e)];
      } else {
        return [];
      }
    })
  );
};

// ----------------------------------------------------------------------------

export const object = (entries) => Object.fromEntries(entries);

// DOM ELEMENTS ---------------------------------------------------------------

/**
 * Set the attribute of an HTMLElement
 * @param {HTMLElement} element
 * @param {string} name
 * @param {string} value
 */
export const setAttribute = (element, name, value) =>
  element.setAttribute(name, value);

/**
 * Retrieve the attribute of an HTMLElement
 * @param {HTMLElement} element
 * @param {string} name
 * @returns {Option<string>}
 */
export const getAttribute = (element, name) => {
  const value = element[name];

  if (value === undefined || value === null) {
    return new Option.None();
  }

  return new Option.Some(value);
};

// LOCATION -------------------------------------------------------------------

export const pushState = (url) => {
  window.history.pushState("", "", url);
  window.dispatchEvent(new Event("locationchange"));
};

export const popState = () => {
  window.history.back();
};

export const currentLocation = () => {
  const url = new URL(window.location.href);

  return {
    path: GleamJS.toList(
      url.pathname.split("/").filter((entry) => entry !== "")
    ),
    hash: url.hash,
    search: url.search,
  };
};

// EVENTS ---------------------------------------------------------------------

export const addEventListener = (eventName, callback) => {
  window.addEventListener(eventName, callback);
};

export const removeEventListener = (eventName, callback) => {
  window.removeEventListener(eventName, callback);
};

export const dispatchEvent = (event) => {
  window.dispatchEvent(event);
};

export const newEvent = (type) => new Event(type);

----
src/react_gleam/attribute.gleam
// Much of this code was lifted from 
// https://github.com/hayleigh-dot-dev/gleam-lustre
// Thank you Hayleigh 💙

// IMPORTS --------------------------------------------------------------------

import gleam/dynamic.{Dynamic}
import gleam/int
import gleam/list
import gleam/pair
import gleam/string
import react_gleam/hook.{Ref}

// TYPES -----------------------------------------------------------------------

///
pub opaque type Attribute(g, action) {
  Attribute(name: String, value: String)
  Property(name: String, value: Dynamic)
  Event(name: String, handler: fn(Dynamic, fn(action) -> Nil) -> Nil)
  ReactRef(name: String, ref: Ref(g))
}

// CONSTRUCTORS ----------------------------------------------------------------

///
pub fn attribute(name: String, value: String) -> Attribute(g, action) {
  Attribute(name, value)
}

///
pub fn property(name: String, value: Dynamic) -> Attribute(g, action) {
  Property(name, value)
}

///
pub fn event(
  name: String,
  handler: fn(Dynamic, fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  Event(name, handler)
}

// COMMON ATTRIBUTES -----------------------------------------------------------

///
pub fn style(properties: List(#(String, String))) -> Attribute(g, action) {
  property("style", style_object(properties))
}

external fn style_object(properties: List(#(String, String))) -> Dynamic =
  "../ffi.mjs" "object"

///
pub fn class(name: String) -> Attribute(g, action) {
  attribute("className", name)
}

///
pub fn classes(names: List(#(String, Bool))) -> Attribute(g, action) {
  attribute(
    "className",
    names
    |> list.filter(pair.second)
    |> list.map(pair.first)
    |> string.join(" "),
  )
}

///
pub fn id(name: String) -> Attribute(g, action) {
  attribute("id", name)
}

// INPUTS ----------------------------------------------------------------------

///
pub fn type_(name: String) -> Attribute(g, action) {
  attribute("type", name)
}

///
pub fn value(val: Dynamic) -> Attribute(g, action) {
  property("value", val)
}

///
pub fn checked(is_checked: Bool) -> Attribute(g, action) {
  property("checked", dynamic.from(is_checked))
}

///
pub fn placeholder(text: String) -> Attribute(g, action) {
  attribute("placeholder", text)
}

///
pub fn selected(is_selected: Bool) -> Attribute(g, action) {
  property("selected", dynamic.from(is_selected))
}

// INPUT HELPERS ---------------------------------------------------------------

///
pub fn accept(types: List(String)) -> Attribute(g, action) {
  attribute("accept", string.join(types, " "))
}

///
pub fn accept_charset(types: List(String)) -> Attribute(g, action) {
  attribute("acceptCharset", string.join(types, " "))
}

///
pub fn action(uri: String) -> Attribute(g, action) {
  attribute("action", uri)
}

///
pub fn autocomplete(should_autocomplete: Bool) -> Attribute(g, action) {
  property("autocomplete", dynamic.from(should_autocomplete))
}

///
pub fn autofocus(should_autofocus: Bool) -> Attribute(g, action) {
  property("autoFocus", dynamic.from(should_autofocus))
}

///
pub fn disabled(is_disabled: Bool) -> Attribute(g, action) {
  property("disabled", dynamic.from(is_disabled))
}

///
pub fn name(name: String) -> Attribute(g, action) {
  attribute("name", name)
}

///
pub fn pattern(regex: String) -> Attribute(g, action) {
  attribute("pattern", regex)
}

///
pub fn readonly(is_readonly: Bool) -> Attribute(g, action) {
  property("readonly", dynamic.from(is_readonly))
}

///
pub fn required(is_required: Bool) -> Attribute(g, action) {
  property("required", dynamic.from(is_required))
}

///
pub fn for(id: String) -> Attribute(g, action) {
  attribute("for", id)
}

// INPUT RANGES ----------------------------------------------------------------

///
pub fn max(val: String) -> Attribute(g, action) {
  attribute("max", val)
}

///
pub fn min(val: String) -> Attribute(g, action) {
  attribute("min", val)
}

///
pub fn step(val: String) -> Attribute(g, action) {
  attribute("step", val)
}

// INPUT TEXT AREAS ------------------------------------------------------------

///
pub fn cols(val: Int) -> Attribute(g, action) {
  attribute("cols", int.to_string(val))
}

///
pub fn rows(val: Int) -> Attribute(g, action) {
  attribute("rows", int.to_string(val))
}

///
pub fn wrap(mode: String) -> Attribute(g, action) {
  attribute("wrap", mode)
}

// LINKS AND AREAS -------------------------------------------------------------

///
pub fn href(uri: String) -> Attribute(g, action) {
  attribute("href", uri)
}

///
pub fn target(target: String) -> Attribute(g, action) {
  attribute("target", target)
}

///
pub fn download(filename: String) -> Attribute(g, action) {
  attribute("download", filename)
}

///
pub fn rel(relationship: String) -> Attribute(g, action) {
  attribute("rel", relationship)
}

// EMBEDDED CONTENT ------------------------------------------------------------

///
pub fn src(uri: String) -> Attribute(g, action) {
  attribute("src", uri)
}

///
pub fn height(val: Int) -> Attribute(g, action) {
  attribute("height", int.to_string(val))
}

///
pub fn width(val: Int) -> Attribute(g, action) {
  attribute("width", int.to_string(val))
}

///
pub fn alt(text: String) -> Attribute(g, action) {
  attribute("alt", text)
}

// AUDIO AND VIDEO -------------------------------------------------------------

///
pub fn autoplay(should_autoplay: Bool) -> Attribute(g, action) {
  property("autoplay", dynamic.from(should_autoplay))
}

///
pub fn controls(visible: Bool) -> Attribute(g, action) {
  property("controls", dynamic.from(visible))
}

///
pub fn loop(should_loop: Bool) -> Attribute(g, action) {
  property("loop", dynamic.from(should_loop))
}

// REACT ----------------------------------------------------------------------

pub fn ref(value: Ref(g)) -> Attribute(g, action) {
  ReactRef(name: "ref", ref: value)
}

----
src/react_gleam/dom_element.gleam
// IMPORTS

import gleam/option.{Option}

// TYPES ----------------------------------------------------------------------

pub external type DomElement

// FUNCTIONS ------------------------------------------------------------------

pub external fn set_attribute(
  element: DomElement,
  name: String,
  value: String,
) -> Nil =
  "../ffi.mjs" "setAttribute"

pub external fn get_attribute(
  element: DomElement,
  name: String,
) -> Option(String) =
  "../ffi.mjs" "getAttribute"

----
src/react_gleam/element.gleam
// Much of this code was lifted from 
// https://github.com/hayleigh-dot-dev/gleam-lustre
// Thank you Hayleigh 💙

// IMPORTS --------------------------------------------------------------------

import react_gleam/attribute.{Attribute, attribute}

// TYPES ----------------------------------------------------------------------

pub external type Element

// CONSTRUCTORS ---------------------------------------------------------------

pub external fn node(
  tag: String,
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element =
  "../ffi.mjs" "node"

/// Render a Gleam string as an HTML text node.
///
pub external fn text(content: String) -> Element =
  "../ffi.mjs" "text"

// CONSTRUCTING NODES ---------------------------------------------------------
// This list and grouping of nodes has been taken from the MDN reference at:
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element

// MAIN ROOT ------------------------------------------------------------------

///
pub fn html(
  attributes: List(Attribute(g, action)),
  head: Element,
  body: Element,
) -> Element {
  node("html", attributes, [head, body])
}

// DOCUMENT METADATA ----------------------------------------------------------

///
pub fn base(attributes: List(Attribute(g, action))) -> Element {
  node("base", attributes, [])
}

///
pub fn head(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("head", attributes, children)
}

///
pub fn meta(attributes: List(Attribute(g, action))) -> Element {
  node("meta", attributes, [])
}

///
pub fn style(attributes: List(Attribute(g, action)), css: String) -> Element {
  node("style", attributes, [text(css)])
}

///
pub fn title(attributes: List(Attribute(g, action)), name: String) -> Element {
  node("title", attributes, [text(name)])
}

// SECTIONING ROOT ------------------------------------------------------------

///
pub fn body(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("body", attributes, children)
}

// CONTENT SECTIONING ---------------------------------------------------------

///
pub fn address(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("address", attributes, children)
}

///
pub fn article(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("article", attributes, children)
}

///
pub fn aside(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("aside", attributes, children)
}

///
pub fn footer(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("footer", attributes, children)
}

///
pub fn header(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("header", attributes, children)
}

///
pub fn h1(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("h1", attributes, children)
}

///
pub fn h2(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("h2", attributes, children)
}

///
pub fn h3(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("h3", attributes, children)
}

///
pub fn h4(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("h4", attributes, children)
}

///
pub fn h5(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("h5", attributes, children)
}

///
pub fn h6(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("h6", attributes, children)
}

///
pub fn main(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("main", attributes, children)
}

///
pub fn nav(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("nav", attributes, children)
}

///
pub fn section(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("section", attributes, children)
}

// TEXT CONTENT ---------------------------------------------------------------

///
pub fn blockquote(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("blockquote", attributes, children)
}

///
pub fn dd(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("dd", attributes, children)
}

///
pub fn div(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("div", attributes, children)
}

///
pub fn dl(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("dl", attributes, children)
}

///
pub fn dt(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("dt", attributes, children)
}

///
pub fn figcaption(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("figcaption", attributes, children)
}

///
pub fn figure(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("figure", attributes, children)
}

///
pub fn hr(attributes: List(Attribute(g, action))) -> Element {
  node("hr", attributes, [])
}

///
pub fn li(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("li", attributes, children)
}

///
pub fn menu(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("menu", attributes, children)
}

///
pub fn ol(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("ol", attributes, children)
}

///
pub fn p(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("p", attributes, children)
}

///
pub fn pre(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("pre", attributes, children)
}

///
pub fn ul(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("ul", attributes, children)
}

// INLINE TEXT SEMANTICS ------------------------------------------------------

///
pub fn a(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("a", attributes, children)
}

///
pub fn abbr(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("abbr", attributes, children)
}

///
pub fn b(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("b", attributes, children)
}

///
pub fn bdi(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("bdi", attributes, children)
}

///
pub fn bdo(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("bdo", attributes, children)
}

///
pub fn br(attributes: List(Attribute(g, action))) -> Element {
  node("br", attributes, [])
}

///
pub fn cite(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("cite", attributes, children)
}

///
pub fn code(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("code", attributes, children)
}

///
pub fn dfn(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("dfn", attributes, children)
}

///
pub fn em(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("em", attributes, children)
}

///
pub fn i(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("i", attributes, children)
}

///
pub fn kbd(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("kbd", attributes, children)
}

///
pub fn mark(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("mark", attributes, children)
}

///
pub fn rp(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("rp", attributes, children)
}

///
pub fn rt(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("rt", attributes, children)
}

///
pub fn ruby(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("ruby", attributes, children)
}

///
pub fn s(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("s", attributes, children)
}

///
pub fn samp(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("samp", attributes, children)
}

///
pub fn small(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("small", attributes, children)
}

///
pub fn span(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("span", attributes, children)
}

///
pub fn strong(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("strong", attributes, children)
}

///
pub fn sub(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("sub", attributes, children)
}

///
pub fn sup(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("sup", attributes, children)
}

///
pub fn time(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("time", attributes, children)
}

///
pub fn u(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("u", attributes, children)
}

///
pub fn var_(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("var", attributes, children)
}

///
pub fn wbr(attributes: List(Attribute(g, action))) -> Element {
  node("wbr", attributes, [])
}

// IMAGE AND MULTIMEDIA -------------------------------------------------------

///
pub fn area(attributes: List(Attribute(g, action))) -> Element {
  node("area", attributes, [])
}

///
pub fn audio(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("audio", attributes, children)
}

///
pub fn img(attributes: List(Attribute(g, action))) -> Element {
  node("img", attributes, [])
}

///
pub fn map_(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("map", attributes, children)
}

///
pub fn track(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("track", attributes, children)
}

///
pub fn video(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("video", attributes, children)
}

// EMBEDDED CONTENT -----------------------------------------------------------

///
pub fn embed(attributes: List(Attribute(g, action))) -> Element {
  node("embed", attributes, [])
}

///
pub fn iframe(attributes: List(Attribute(g, action))) -> Element {
  node("iframe", attributes, [])
}

///
pub fn object(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("object", attributes, children)
}

///
pub fn param(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("param", attributes, children)
}

///
pub fn picture(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("picture", attributes, children)
}

///
pub fn portal(attributes: List(Attribute(g, action))) -> Element {
  node("portal", attributes, [])
}

///
pub fn source(attributes: List(Attribute(g, action))) -> Element {
  node("source", attributes, [])
}

// SVG AND MATHML -------------------------------------------------------------

///
pub fn svg(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node(
    "svg",
    [attribute("xmlns", "http://www.w3.org/2000/svg"), ..attributes],
    children,
  )
}

///
pub fn mathml(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node(
    "mathml",
    [attribute("xmlns", "http://www.w3.org/1998/Math/MathML"), ..attributes],
    children,
  )
}

// SCRIPTING ------------------------------------------------------------------

///
pub fn canvas(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("canvas", attributes, children)
}

///
pub fn noscript(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("noscript", attributes, children)
}

// DEMARCATING EDITS ----------------------------------------------------------

///
pub fn del(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("del", attributes, children)
}

///
pub fn ins(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("ins", attributes, children)
}

// TABLE CONTENT --------------------------------------------------------------

///
pub fn caption(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("caption", attributes, children)
}

///
pub fn col(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("col", attributes, children)
}

///
pub fn colgroup(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("colgroup", attributes, children)
}

///
pub fn table(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("table", attributes, children)
}

///
pub fn tbody(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("tbody", attributes, children)
}

///
pub fn td(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("td", attributes, children)
}

///
pub fn tfoot(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("tfoot", attributes, children)
}

///
pub fn th(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("th", attributes, children)
}

///
pub fn thead(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("thead", attributes, children)
}

///
pub fn tr(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("tr", attributes, children)
}

// FORMS ----------------------------------------------------------------------

///
pub fn button(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("button", attributes, children)
}

///
pub fn datalist(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("datalist", attributes, children)
}

///
pub fn fieldset(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("fieldset", attributes, children)
}

///
pub fn form(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("form", attributes, children)
}

///
pub fn input(attributes: List(Attribute(g, action))) -> Element {
  node("input", attributes, [])
}

///
pub fn label(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("label", attributes, children)
}

///
pub fn legend(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("legend", attributes, children)
}

///
pub fn meter(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("meter", attributes, children)
}

///
pub fn optgroup(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("optgroup", attributes, children)
}

///
pub fn option(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("option", attributes, children)
}

///
pub fn output(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("output", attributes, children)
}

///
pub fn progress(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("progress", attributes, children)
}

///
pub fn select(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("select", attributes, children)
}

///
pub fn textarea(attributes: List(Attribute(g, action))) -> Element {
  node("textarea", attributes, [])
}

// INTERACTIVE ELEMENTS -------------------------------------------------------

///
pub fn details(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("details", attributes, children)
}

///
pub fn dialog(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("dialog", attributes, children)
}

///
pub fn summary(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("summary", attributes, children)
}

// WEB COMPONENTS -------------------------------------------------------------

///
pub fn slot(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("slot", attributes, children)
}

///
pub fn template(
  attributes: List(Attribute(g, action)),
  children: List(Element),
) -> Element {
  node("template", attributes, children)
}

----
src/react_gleam/event.gleam
// Much of this code was lifted from 
// https://github.com/hayleigh-dot-dev/gleam-lustre
// Thank you Hayleigh 💙

// IMPORTS ---------------------------------------------------------------------

import gleam/dynamic.{Dynamic}
import react_gleam/attribute.{Attribute}

// CONSTRUCTORS ----------------------------------------------------------------

///
pub fn on(
  name: String,
  handler: fn(Dynamic, fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  attribute.event(name, handler)
}

///
pub fn dispatch(action: action) -> fn(fn(action) -> Nil) -> Nil {
  fn(dispatch) { dispatch(action) }
}

// MOUSE EVENTS ----------------------------------------------------------------

///
pub fn on_click(handler: fn(fn(action) -> Nil) -> Nil) -> Attribute(g, action) {
  on("click", fn(_, dispatch) { handler(dispatch) })
}

///
pub fn on_mouse_down(
  handler: fn(fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on("mouseDown", fn(_, dispatch) { handler(dispatch) })
}

///
pub fn on_mouse_up(
  handler: fn(fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on("mouseUp", fn(_, dispatch) { handler(dispatch) })
}

///
pub fn on_mouse_enter(
  handler: fn(fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on("mouseEnter", fn(_, dispatch) { handler(dispatch) })
}

///
pub fn on_mouse_leave(
  handler: fn(fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on("mouseLeave", fn(_, dispatch) { handler(dispatch) })
}

///
pub fn on_mouse_over(
  handler: fn(fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on("mouseOver", fn(_, dispatch) { handler(dispatch) })
}

///
pub fn on_mouse_out(
  handler: fn(fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on("mouseOut", fn(_, dispatch) { handler(dispatch) })
}

// KEYBOARD EVENTS -------------------------------------------------------------

pub fn on_keypress(
  handler: fn(String, fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on(
    "keyPress",
    fn(e, dispatch) {
      let assert Ok(key) =
        e
        |> dynamic.field("key", dynamic.string)

      handler(key, dispatch)
    },
  )
}

pub fn on_keydown(
  handler: fn(String, fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on(
    "keyDown",
    fn(e, dispatch) {
      let assert Ok(key) =
        e
        |> dynamic.field("key", dynamic.string)

      handler(key, dispatch)
    },
  )
}

pub fn on_keyup(
  handler: fn(String, fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on(
    "keyUp",
    fn(e, dispatch) {
      let assert Ok(key) =
        e
        |> dynamic.field("key", dynamic.string)

      handler(key, dispatch)
    },
  )
}

// FORM EVENTS -----------------------------------------------------------------

///
pub fn on_input(
  handler: fn(String, fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on(
    "input",
    fn(e, dispatch) {
      let assert Ok(value) =
        e
        |> dynamic.field("target", dynamic.field("value", dynamic.string))

      handler(value, dispatch)
    },
  )
}

pub fn on_check(
  handler: fn(Bool, fn(action) -> Nil) -> Nil,
) -> Attribute(g, action) {
  on(
    "check",
    fn(e, dispatch) {
      let assert Ok(value) =
        e
        |> dynamic.field("target", dynamic.field("checked", dynamic.bool))

      handler(value, dispatch)
    },
  )
}

pub fn on_submit(handler: fn(fn(action) -> Nil) -> Nil) -> Attribute(g, action) {
  on("submit", fn(_, dispatch) { handler(dispatch) })
}

// FOCUS EVENTS ----------------------------------------------------------------

pub fn on_focus(handler: fn(fn(action) -> Nil) -> Nil) -> Attribute(g, action) {
  on("focus", fn(_, dispatch) { handler(dispatch) })
}

pub fn on_blur(handler: fn(fn(action) -> Nil) -> Nil) -> Attribute(g, action) {
  on("blur", fn(_, dispatch) { handler(dispatch) })
}

----
src/react_gleam/hook.gleam
// IMPORTS --------------------------------------------------------------------

import gleam/dynamic.{Dynamic}
import gleam/option.{Option}
import react_gleam/dom_element.{DomElement}

// TYPES ----------------------------------------------------------------------

pub type Ref(g) {
  Ref(current: Option(g))
}

// STATE ----------------------------------------------------------------------

pub external fn use_state(initial: fn() -> a) -> #(a, fn(fn(a) -> a) -> Nil) =
  "../ffi.mjs" "useState"

// REDUCER --------------------------------------------------------------------

pub external fn use_reducer(
  reducer: fn(a, b) -> a,
  initial: a,
) -> #(a, fn(b) -> Nil) =
  "../ffi.mjs" "useReducer"

// EFFECT ---------------------------------------------------------------------

pub external fn use_effect(callback: fn() -> Option(fn() -> Nil)) -> Nil =
  "../ffi.mjs" "useEffectHook"

pub external fn use_effect0(callback: fn() -> Option(fn() -> Nil)) -> Nil =
  "../ffi.mjs" "useEffect0"

pub external fn use_effect1(
  callback: fn() -> Option(fn() -> Nil),
  dependencies: #(g),
) -> Nil =
  "../ffi.mjs" "useEffectHook"

pub external fn use_effect2(
  callback: fn() -> Option(fn() -> Nil),
  dependencies: #(g, l),
) -> Nil =
  "../ffi.mjs" "useEffectHook"

pub external fn use_effect3(
  callback: fn() -> Option(fn() -> Nil),
  dependencies: #(g, l, e),
) -> Nil =
  "../ffi.mjs" "useEffectHook"

pub external fn use_effect4(
  callback: fn() -> Option(fn() -> Nil),
  dependencies: #(g, l, e, a),
) -> Nil =
  "../ffi.mjs" "useEffectHook"

pub external fn use_effect5(
  callback: fn() -> Option(fn() -> Nil),
  dependencies: #(g, l, e, a, m),
) -> Nil =
  "../ffi.mjs" "useEffectHook"

pub external fn use_effect6(
  callback: fn() -> Option(fn() -> Nil),
  dependencies: #(g, l, e, a, m, o),
) -> Nil =
  "../ffi.mjs" "useEffectHook"

pub external fn use_effect7(
  callback: fn() -> Option(fn() -> Nil),
  dependencies: #(g, l, e, a, m, o, r),
) -> Nil =
  "../ffi.mjs" "useEffectHook"

// MEMO -----------------------------------------------------------------------

pub external fn use_memo1(calculation: fn() -> v, dependencies: #(g)) -> v =
  "../ffi.mjs" "useMemo"

pub external fn use_memo2(calculation: fn() -> v, dependencies: #(g, l)) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_memo3(calculation: fn() -> v, dependencies: #(g, l, e)) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_memo4(
  calculation: fn() -> v,
  dependencies: #(g, l, e, a),
) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_memo5(
  calculation: fn() -> v,
  dependencies: #(g, l, e, a, m),
) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_memo6(
  calculation: fn() -> v,
  dependencies: #(g, l, e, a, m, o),
) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_memo7(
  calculation: fn() -> v,
  dependencies: #(g, l, e, a, m, o, r),
) -> g =
  "../ffi.mjs" "useMemo"

// CALLBACK -------------------------------------------------------------------

pub external fn use_callback1(
  callback: fn() -> fn() -> v,
  dependencies: #(g),
) -> v =
  "../ffi.mjs" "useMemo"

pub external fn use_callback2(
  callback: fn() -> fn() -> v,
  dependencies: #(g, l),
) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_callback3(
  callback: fn() -> fn() -> v,
  dependencies: #(g, l, e),
) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_callback4(
  callback: fn() -> fn() -> v,
  dependencies: #(g, l, e, a),
) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_callback5(
  callback: fn() -> fn() -> v,
  dependencies: #(g, l, e, a, m),
) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_callback6(
  callback: fn() -> fn() -> v,
  dependencies: #(g, l, e, a, m, o),
) -> g =
  "../ffi.mjs" "useMemo"

pub external fn use_callback7(
  callback: fn() -> fn() -> v,
  dependencies: #(g, l, e, a, m, o, r),
) -> g =
  "../ffi.mjs" "useMemo"

// ID -------------------------------------------------------------------------

pub external fn use_id() -> String =
  "../ffi.mjs" "useId"

// TRANSITION -----------------------------------------------------------------

pub external fn use_transition(cb: fn() -> Nil) -> #(Bool, fn() -> Nil) =
  "../ffi.mjs" "useTransition"

// DEBUG VALUE ----------------------------------------------------------------

pub external fn use_debug_value(value: String) -> Nil =
  "../ffi.mjs" "useDebugValue"

// REF ------------------------------------------------------------------------

pub external fn use_ref(value: Option(DomElement)) -> Ref(DomElement) =
  "../ffi.mjs" "useRefHook"

// CONTEXT --------------------------------------------------------------------

pub external fn use_context(key: String) -> Result(Dynamic, String) =
  "../ffi.mjs" "useContextHook"

----
src/react_gleam/router.gleam
// IMPORTS --------------------------------------------------------------------

import gleam/option.{Some}
import react_gleam/hook.{use_effect0, use_state}

// TYPES ----------------------------------------------------------------------

pub type Location {
  Location(path: List(String), hash: String, search: String)
}

external type DOMEvent

// HOOKS ----------------------------------------------------------------------

pub fn use_url() -> Location {
  let location = get_current_location()

  let #(current_location, set_current_location) = use_state(fn() { location })

  use_effect0(fn() {
    let location_change_callback = fn() {
      let new_location = get_current_location()

      set_current_location(fn(_) { new_location })
    }

    let location_pop_callback = fn() {
      dispatch_event(new_event("locationchange"))
    }

    add_event_listener("locationchange", location_change_callback)
    add_event_listener("popstate", location_pop_callback)

    Some(fn() {
      remove_event_listener("locationchange", location_change_callback)
      remove_event_listener("popstate", location_pop_callback)
    })
  })

  current_location
}

// UTILITY --------------------------------------------------------------------

pub external fn push(url: String) -> Nil =
  "../ffi.mjs" "pushState"

pub external fn back() -> Nil =
  "../ffi.mjs" "popState"

// ----------------------------------------------------------------------------

external fn get_current_location() -> Location =
  "../ffi.mjs" "currentLocation"

external fn add_event_listener(name: String, callback: fn() -> Nil) -> Nil =
  "../ffi.mjs" "addEventListener"

external fn remove_event_listener(name: String, callback: fn() -> Nil) -> Nil =
  "../ffi.mjs" "removeEventListener"

external fn dispatch_event(event: DOMEvent) -> Nil =
  "../ffi.mjs" "dispatchEvent"

external fn new_event(name: String) -> DOMEvent =
  "../ffi.mjs" "newEvent"

----
src/react_gleam.gleam
// IMPORTS --------------------------------------------------------------------

import react_gleam/element.{Element}

// TYPES ----------------------------------------------------------------------

pub external type Context

// GENERAL --------------------------------------------------------------------

pub external fn render(app: Element, root: String) -> Nil =
  "./ffi.mjs" "render"

// CONTEXT --------------------------------------------------------------------

pub external fn set_context(key: String, value: g) -> Nil =
  "./ffi.mjs" "setContext"

// COMPONENT ------------------------------------------------------------------

pub external fn component(element: fn() -> Element) -> Element =
  "./ffi.mjs" "component"

----
test/react_gleam_test.gleam
import gleeunit
import gleeunit/should

pub fn main() {
  gleeunit.main()
}

// gleeunit test functions end in `_test`
pub fn hello_world_test() {
  1
  |> should.equal(1)
}

--END--

END OF OUTDATED IMPLEMENTATION


NOw I want you to redo this entire implementation in Gleam but for PREACT instead. 
1. Create a New Gleam Project

gleam new preact_gleam
cd preact_gleam
This will initialize a new Gleam project with the necessary directory structure.

2. Initialize NPM and Install Dependencies
Initialize a new NPM package:


npm init -y
Install Preact and related packages:


npm install preact preact/hooks
Updating Package Dependencies
Update the package.json to reflect the new dependencies:


{
  "name": "preact_gleam",
  "version": "1.0.0",
  "main": "index.js",
  "author": "Your Name <your.email@example.com>",
  "license": "MIT",
  "dependencies": {
    "preact": "^10.5.13",
    "preact/hooks": "^10.5.13"
  }
}
Modifying the FFI Module (ffi.mjs)
The ffi.mjs file is crucial for JavaScript interop. We'll need to replace React-specific functions with Preact equivalents.

1. Import Preact Modules
Replace React imports with Preact:


// ffi.mjs

import { h, render as preactRender } from "preact";
import { useState, useEffect, useReducer, useRef, useMemo, useCallback } from "preact/hooks";
2. Update Exported Functions
Adjust the exported functions to use Preact's API.

Rendering Function

export const render = (app, selector) => {
  const rootElement = document.querySelector(selector);
  preactRender(app, rootElement);
};
Element Creation
Modify the node and text functions to use h instead of createElement.


export const node = (type, attributes, children) => {
  const props = toProps(attributes);
  return h(
    type,
    props,
    ...children.map((child) => (typeof child === "function" ? child() : child))
  );
};

export const text = (content) => content;
Hooks
Ensure all hooks are imported from preact/hooks and exported correctly.


export { useState, useEffect, useReducer, useRef, useMemo, useCallback };
Context API
Update context handling if necessary, noting that Preact's context API is similar to React's.

Updating Gleam Modules
Now let's update the Gleam modules to interface correctly with Preact.

Core Module (preact_gleam.gleam)
Update the module to reflect Preact usage.


// src/preact_gleam.gleam

// Import necessary modules
import preact_gleam/element.{Element}

// External types and functions
pub external type Context

/// Render a Preact application to the DOM
pub external fn render(app: Element, root: String) -> Nil =
  "./ffi.mjs" "render"

/// Create a Preact component
pub external fn component(element: fn() -> Element) -> Element =
  "./ffi.mjs" "component"
Element Module (preact_gleam/element.gleam)
Ensure that element creation functions correspond to Preact's API.


// src/preact_gleam/element.gleam

pub external type Element

/// Create a Preact element
pub external fn node(
  tag: String,
  attributes: List(Attribute(g)),
  children: List(Element),
) -> Element =
  "../ffi.mjs" "node"

/// Render a Gleam string as a text node
pub external fn text(content: String) -> Element =
  "../ffi.mjs" "text"
Update all element functions (e.g., div, span, etc.) to ensure they use the updated node function.

Hook Module (preact_gleam/hook.gleam)
Update hook functions to interface with Preact hooks.


// src/preact_gleam/hook.gleam

// State Hook
pub external fn use_state(initial: fn() -> a) -> #(a, fn(fn(a) -> a) -> Nil) =
  "../ffi.mjs" "useState"

// Effect Hook
pub external fn use_effect(effect: fn() -> Option(fn() -> Nil)) -> Nil =
  "../ffi.mjs" "useEffect"

// Reducer Hook
pub external fn use_reducer(
  reducer: fn(state: s, action: a) -> s,
  initial_state: s,
) -> #(s, fn(a) -> Nil) =
  "../ffi.mjs" "useReducer"

// Memo Hook
pub external fn use_memo(generator: fn() -> a, dependencies: List(c)) -> a =
  "../ffi.mjs" "useMemo"

// Callback Hook
pub external fn use_callback(callback: fn() -> a, dependencies: List(c)) -> a =
  "../ffi.mjs" "useCallback"

// Ref Hook
pub external fn use_ref(initial: a) -> Ref(a) =
  "../ffi.mjs" "useRef"
Event Module (preact_gleam/event.gleam)
Adjust event handling to align with Preact's native event system.


// src/preact_gleam/event.gleam

import preact_gleam/attribute.{Attribute, event}

pub fn on_click(handler: fn(Event) -> Nil) -> Attribute {
  event("click", handler)
}
Remove any references to React's synthetic events, and ensure event handlers work with native events.

Attribute Module (preact_gleam/attribute.gleam)
Ensure that attribute handling aligns with Preact's expectations.


// src/preact_gleam/attribute.gleam

// Define Attribute type
pub opaque type Attribute {
  Attribute(name: String, value: String)
  Property(name: String, value: Dynamic)
  Event(name: String, handler: fn(Event) -> Nil)
}

// Attribute Constructors
pub fn attribute(name: String, value: String) -> Attribute {
  Attribute(name, value)
}

pub fn property(name: String, value: Dynamic) -> Attribute {
  Property(name, value)
}

pub fn event(name: String, handler: fn(Event) -> Nil) -> Attribute {
  Event(name, handler)
}

// Common Attributes
pub fn class(name: String) -> Attribute {
  attribute("class", name)
}

pub fn id(name: String) -> Attribute {
  attribute("id", name)
}
Example Application
Create an example application to test preact_gleam.


// src/example.gleam

import preact_gleam.{render}
import preact_gleam/element.{div, text}
import preact_gleam/hook.{use_state}

pub fn main() {
  render(app(), "#app")
}

fn app() {
  div(
    [],
    [
      greeting("Gleam Developer"),
      counter(),
    ],
  )
}

fn greeting(name: String) {
  div([], [text("Hello, " <> name <> "!")])
}

fn counter() {
  use_state_example()
}

fn use_state_example() {
  let #(count, set_count) = use_state(fn() { 0 })

  div(
    [],
    [
      div([], [text("Count: " <> int.to_string(count))]),
      button(
        [on_click(fn(_) { set_count(fn(prev) { prev + 1 }) })],
        [text("Increment")],
      ),
    ],
  )
}
Testing the Implementation
1. Build the Gleam Code
From the project root, run:


gleam build
2. Serve the Application
Set up a simple web server to serve the index.html file and the compiled JavaScript files. You can use vite or http-server.

Install vite:


npm install --save-dev vite
Add a dev script to package.json:


"scripts": {
  "dev": "vite"
}
Create an index.html file:


<!DOCTYPE html>
<html>
  <head>
    <title>Preact Gleam Example</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="./build/dev/javascript/example/main.js"></script>
  </body>
</html>
Run the development server:


npm run dev
Access the app at http://localhost:3000 (or the port provided by Vite).

3. Verify Functionality
Ensure that the greeting displays correctly.
Test the counter to see if it increments when clicked.
Documentation and Comments
Ensure that all modules, functions, and types have proper documentation.

Use /// for function documentation.
Provide examples where necessary.
Update the README.md with usage instructions.
FULL PREACT CODEBASE
!function(){var n,l,t,u,i,o,r,e,f,c,s,a,h,p=65536,v=1<<17,d={},y=[],w=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,_=Array.isArray;function g(n,l){for(var t in l)n[t]=l[t];return n}function m(n){n&&n.parentNode&&n.parentNode.removeChild(n)}function b(l,t,u){var i,o,r,e={};for(r in t)"key"==r?i=t[r]:"ref"==r?o=t[r]:e[r]=t[r];if(arguments.length>2&&(e.children=arguments.length>3?n.call(arguments,2):u),"function"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===e[r]&&(e[r]=l.defaultProps[r]);return k(l,e,i,o,null)}function k(n,u,i,o,r){var e={type:n,props:u,key:i,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,constructor:void 0,__v:null==r?++t:r,__i:-1,__u:0};return null==r&&null!=l.vnode&&l.vnode(e),e}function C(n){return n.children}function x(n,l){this.props=n,this.context=l}function S(n,l){if(null==l)return n.__?S(n.__,n.__i+1):null;for(var t;l<n.__k.length;l++)if(null!=(t=n.__k[l])&&null!=t.__e)return t.__e;return"function"==typeof n.type?S(n):null}function M(n){var l,t;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(t=n.__k[l])&&null!=t.__e){n.__e=n.__c.base=t.__e;break}return M(n)}}function P(n){(!n.__d&&(n.__d=!0)&&i.push(n)&&!$.__r++||o!==l.debounceRendering)&&((o=l.debounceRendering)||r)($)}function $(){var n,t,u,o,r,f,c,s;for(i.sort(e);n=i.shift();)n.__d&&(t=i.length,o=void 0,f=(r=(u=n).__v).__e,c=[],s=[],u.__P&&((o=g({},r)).__v=r.__v+1,l.vnode&&l.vnode(o),O(u.__P,o,r,u.__n,u.__P.namespaceURI,32&r.__u?[f]:null,c,null==f?S(r):f,!!(32&r.__u),s),o.__v=r.__v,o.__.__k[o.__i]=o,V(c,o,s),o.__e!=f&&M(o)),i.length>t&&i.sort(e));$.__r=0}function I(n,l,t,u,i,o,r,e,f,c,s){var a,h,v,w,_,g=u&&u.__k||y,m=l.length;for(t.__d=f,A(t,l,g),f=t.__d,a=0;a<m;a++)null!=(v=t.__k[a])&&(h=-1===v.__i?d:g[v.__i]||d,v.__i=a,O(n,v,h,i,o,r,e,f,c,s),w=v.__e,v.ref&&h.ref!=v.ref&&(h.ref&&z(h.ref,null,v),s.push(v.ref,v.__c||w,v)),null==_&&null!=w&&(_=w),v.__u&p||h.__k===v.__k?f=E(v,f,n):"function"==typeof v.type&&void 0!==v.__d?f=v.__d:w&&(f=w.nextSibling),v.__d=void 0,v.__u&=-196609);t.__d=f,t.__e=_}function A(n,l,t){var u,i,o,r,e,f=l.length,c=t.length,s=c,a=0;for(n.__k=[],u=0;u<f;u++)null!=(i=l[u])&&"boolean"!=typeof i&&"function"!=typeof i?(r=u+a,(i=n.__k[u]="string"==typeof i||"number"==typeof i||"bigint"==typeof i||i.constructor==String?k(null,i,null,null,null):_(i)?k(C,{children:i},null,null,null):void 0===i.constructor&&i.__b>0?k(i.type,i.props,i.key,i.ref?i.ref:null,i.__v):i).__=n,i.__b=n.__b+1,o=null,-1!==(e=i.__i=F(i,t,r,s))&&(s--,(o=t[e])&&(o.__u|=v)),null==o||null===o.__v?(-1==e&&a--,"function"!=typeof i.type&&(i.__u|=p)):e!==r&&(e==r-1?a--:e==r+1?a++:(e>r?a--:a++,i.__u|=p))):i=n.__k[u]=null;if(s)for(u=0;u<c;u++)null!=(o=t[u])&&0==(o.__u&v)&&(o.__e==n.__d&&(n.__d=S(o)),N(o,o))}function E(n,l,t){var u,i;if("function"==typeof n.type){for(u=n.__k,i=0;u&&i<u.length;i++)u[i]&&(u[i].__=n,l=E(u[i],l,t));return l}n.__e!=l&&(l&&n.type&&!t.contains(l)&&(l=S(n)),t.insertBefore(n.__e,l||null),l=n.__e);do{l=l&&l.nextSibling}while(null!=l&&8===l.nodeType);return l}function F(n,l,t,u){var i=n.key,o=n.type,r=t-1,e=t+1,f=l[t];if(null===f||f&&i==f.key&&o===f.type&&0==(f.__u&v))return t;if(u>(null!=f&&0==(f.__u&v)?1:0))for(;r>=0||e<l.length;){if(r>=0){if((f=l[r])&&0==(f.__u&v)&&i==f.key&&o===f.type)return r;r--}if(e<l.length){if((f=l[e])&&0==(f.__u&v)&&i==f.key&&o===f.type)return e;e++}}return-1}function H(n,l,t){"-"===l[0]?n.setProperty(l,null==t?"":t):n[l]=null==t?"":"number"!=typeof t||w.test(l)?t:t+"px"}function L(n,l,t,u,i){var o;n:if("style"===l)if("string"==typeof t)n.style.cssText=t;else{if("string"==typeof u&&(n.style.cssText=u=""),u)for(l in u)t&&l in t||H(n.style,l,"");if(t)for(l in t)u&&t[l]===u[l]||H(n.style,l,t[l])}else if("o"===l[0]&&"n"===l[1])o=l!==(l=l.replace(/(PointerCapture)$|Capture$/i,"$1")),l=l.toLowerCase()in n||"onFocusOut"===l||"onFocusIn"===l?l.toLowerCase().slice(2):l.slice(2),n.l||(n.l={}),n.l[l+o]=t,t?u?t.t=u.t:(t.t=f,n.addEventListener(l,o?s:c,o)):n.removeEventListener(l,o?s:c,o);else{if("http://www.w3.org/2000/svg"==i)l=l.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if("width"!=l&&"height"!=l&&"href"!=l&&"list"!=l&&"form"!=l&&"tabIndex"!=l&&"download"!=l&&"rowSpan"!=l&&"colSpan"!=l&&"role"!=l&&"popover"!=l&&l in n)try{n[l]=null==t?"":t;break n}catch(n){}"function"==typeof t||(null==t||!1===t&&"-"!==l[4]?n.removeAttribute(l):n.setAttribute(l,"popover"==l&&1==t?"":t))}}function T(n){return function(t){if(this.l){var u=this.l[t.type+n];if(null==t.u)t.u=f++;else if(t.u<u.t)return;return u(l.event?l.event(t):t)}}}function O(n,t,u,i,o,r,e,f,c,s){var a,h,p,v,d,y,w,m,b,k,S,M,P,$,A,E,F=t.type;if(void 0!==t.constructor)return null;128&u.__u&&(c=!!(32&u.__u),r=[f=t.__e=u.__e]),(a=l.__b)&&a(t);n:if("function"==typeof F)try{if(m=t.props,b="prototype"in F&&F.prototype.render,k=(a=F.contextType)&&i[a.__c],S=a?k?k.props.value:a.__:i,u.__c?w=(h=t.__c=u.__c).__=h.__E:(b?t.__c=h=new F(m,S):(t.__c=h=new x(m,S),h.constructor=F,h.render=R),k&&k.sub(h),h.props=m,h.state||(h.state={}),h.context=S,h.__n=i,p=h.__d=!0,h.__h=[],h._sb=[]),b&&null==h.__s&&(h.__s=h.state),b&&null!=F.getDerivedStateFromProps&&(h.__s==h.state&&(h.__s=g({},h.__s)),g(h.__s,F.getDerivedStateFromProps(m,h.__s))),v=h.props,d=h.state,h.__v=t,p)b&&null==F.getDerivedStateFromProps&&null!=h.componentWillMount&&h.componentWillMount(),b&&null!=h.componentDidMount&&h.__h.push(h.componentDidMount);else{if(b&&null==F.getDerivedStateFromProps&&m!==v&&null!=h.componentWillReceiveProps&&h.componentWillReceiveProps(m,S),!h.__e&&(null!=h.shouldComponentUpdate&&!1===h.shouldComponentUpdate(m,h.__s,S)||t.__v===u.__v)){for(t.__v!==u.__v&&(h.props=m,h.state=h.__s,h.__d=!1),t.__e=u.__e,t.__k=u.__k,t.__k.some(function(n){n&&(n.__=t)}),M=0;M<h._sb.length;M++)h.__h.push(h._sb[M]);h._sb=[],h.__h.length&&e.push(h);break n}null!=h.componentWillUpdate&&h.componentWillUpdate(m,h.__s,S),b&&null!=h.componentDidUpdate&&h.__h.push(function(){h.componentDidUpdate(v,d,y)})}if(h.context=S,h.props=m,h.__P=n,h.__e=!1,P=l.__r,$=0,b){for(h.state=h.__s,h.__d=!1,P&&P(t),a=h.render(h.props,h.state,h.context),A=0;A<h._sb.length;A++)h.__h.push(h._sb[A]);h._sb=[]}else do{h.__d=!1,P&&P(t),a=h.render(h.props,h.state,h.context),h.state=h.__s}while(h.__d&&++$<25);h.state=h.__s,null!=h.getChildContext&&(i=g(g({},i),h.getChildContext())),b&&!p&&null!=h.getSnapshotBeforeUpdate&&(y=h.getSnapshotBeforeUpdate(v,d)),I(n,_(E=null!=a&&a.type===C&&null==a.key?a.props.children:a)?E:[E],t,u,i,o,r,e,f,c,s),h.base=t.__e,t.__u&=-161,h.__h.length&&e.push(h),w&&(h.__E=h.__=null)}catch(n){if(t.__v=null,c||null!=r){for(t.__u|=c?160:128;f&&8===f.nodeType&&f.nextSibling;)f=f.nextSibling;r[r.indexOf(f)]=null,t.__e=f}else t.__e=u.__e,t.__k=u.__k;l.__e(n,t,u)}else null==r&&t.__v===u.__v?(t.__k=u.__k,t.__e=u.__e):t.__e=j(u.__e,t,u,i,o,r,e,c,s);(a=l.diffed)&&a(t)}function V(n,t,u){t.__d=void 0;for(var i=0;i<u.length;i++)z(u[i],u[++i],u[++i]);l.__c&&l.__c(t,n),n.some(function(t){try{n=t.__h,t.__h=[],n.some(function(n){n.call(t)})}catch(n){l.__e(n,t.__v)}})}function j(t,u,i,o,r,e,f,c,s){var a,h,p,v,y,w,g,b=i.props,k=u.props,C=u.type;if("svg"===C?r="http://www.w3.org/2000/svg":"math"===C?r="http://www.w3.org/1998/Math/MathML":r||(r="http://www.w3.org/1999/xhtml"),null!=e)for(a=0;a<e.length;a++)if((y=e[a])&&"setAttribute"in y==!!C&&(C?y.localName===C:3===y.nodeType)){t=y,e[a]=null;break}if(null==t){if(null===C)return document.createTextNode(k);t=document.createElementNS(r,C,k.is&&k),c&&(l.__m&&l.__m(u,e),c=!1),e=null}if(null===C)b===k||c&&t.data===k||(t.data=k);else{if(e=e&&n.call(t.childNodes),b=i.props||d,!c&&null!=e)for(b={},a=0;a<t.attributes.length;a++)b[(y=t.attributes[a]).name]=y.value;for(a in b)if(y=b[a],"children"==a);else if("dangerouslySetInnerHTML"==a)p=y;else if(!(a in k)){if("value"==a&&"defaultValue"in k||"checked"==a&&"defaultChecked"in k)continue;L(t,a,null,y,r)}for(a in k)y=k[a],"children"==a?v=y:"dangerouslySetInnerHTML"==a?h=y:"value"==a?w=y:"checked"==a?g=y:c&&"function"!=typeof y||b[a]===y||L(t,a,y,b[a],r);if(h)c||p&&(h.__html===p.__html||h.__html===t.innerHTML)||(t.innerHTML=h.__html),u.__k=[];else if(p&&(t.innerHTML=""),I(t,_(v)?v:[v],u,i,o,"foreignObject"===C?"http://www.w3.org/1999/xhtml":r,e,f,e?e[0]:i.__k&&S(i,0),c,s),null!=e)for(a=e.length;a--;)m(e[a]);c||(a="value","progress"===C&&null==w?t.removeAttribute("value"):void 0!==w&&(w!==t[a]||"progress"===C&&!w||"option"===C&&w!==b[a])&&L(t,a,w,b[a],r),a="checked",void 0!==g&&g!==t[a]&&L(t,a,g,b[a],r))}return t}function z(n,t,u){try{if("function"==typeof n){var i="function"==typeof n.__u;i&&n.__u(),i&&null==t||(n.__u=n(t))}else n.current=t}catch(n){l.__e(n,u)}}function N(n,t,u){var i,o;if(l.unmount&&l.unmount(n),(i=n.ref)&&(i.current&&i.current!==n.__e||z(i,null,t)),null!=(i=n.__c)){if(i.componentWillUnmount)try{i.componentWillUnmount()}catch(n){l.__e(n,t)}i.base=i.__P=null}if(i=n.__k)for(o=0;o<i.length;o++)i[o]&&N(i[o],t,u||"function"!=typeof n.type);u||m(n.__e),n.__c=n.__=n.__e=n.__d=void 0}function R(n,l,t){return this.constructor(n,t)}function q(t,u,i){var o,r,e,f;l.__&&l.__(t,u),r=(o="function"==typeof i)?null:i&&i.__k||u.__k,e=[],f=[],O(u,t=(!o&&i||u).__k=b(C,null,[t]),r||d,d,u.namespaceURI,!o&&i?[i]:r?null:u.firstChild?n.call(u.childNodes):null,e,!o&&i?i:r?r.__e:u.firstChild,o,f),V(e,t,f)}n=y.slice,l={__e:function(n,l,t,u){for(var i,o,r;l=l.__;)if((i=l.__c)&&!i.__)try{if((o=i.constructor)&&null!=o.getDerivedStateFromError&&(i.setState(o.getDerivedStateFromError(n)),r=i.__d),null!=i.componentDidCatch&&(i.componentDidCatch(n,u||{}),r=i.__d),r)return i.__E=i}catch(l){n=l}throw n}},t=0,u=function(n){return null!=n&&null==n.constructor},x.prototype.setState=function(n,l){var t;t=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=g({},this.state),"function"==typeof n&&(n=n(g({},t),this.props)),n&&g(t,n),null!=n&&this.__v&&(l&&this._sb.push(l),P(this))},x.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),P(this))},x.prototype.render=C,i=[],r="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,e=function(n,l){return n.__v.__b-l.__v.__b},$.__r=0,f=0,c=T(!1),s=T(!0),a=0,h={__proto__:null,render:q,hydrate:function n(l,t){q(l,t,n)},createElement:b,h:b,Fragment:C,createRef:function(){return{current:null}},isValidElement:u,Component:x,cloneElement:function(l,t,u){var i,o,r,e,f=g({},l.props);for(r in l.type&&l.type.defaultProps&&(e=l.type.defaultProps),t)"key"==r?i=t[r]:"ref"==r?o=t[r]:f[r]=void 0===t[r]&&void 0!==e?e[r]:t[r];return arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):u),k(l.type,f,i||l.key,o||l.ref,null)},createContext:function(n,l){var t={__c:l="__cC"+a++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var t,u;return this.getChildContext||(t=new Set,(u={})[l]=this,this.getChildContext=function(){return u},this.componentWillUnmount=function(){t=null},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&t.forEach(function(n){n.__e=!0,P(n)})},this.sub=function(n){t.add(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){t&&t.delete(n),l&&l.call(n)}}),n.children}};return t.Provider.__=t.Consumer.contextType=t},toChildArray:function n(l,t){return t=t||[],null==l||"boolean"==typeof l||(_(l)?l.some(function(l){n(l,t)}):t.push(l)),t},options:l},typeof module<"u"?module.exports=h:self.preact=h}();
//# sourceMappingURL=preact.min.js.map
END OF PREACT CODEBASE.  

Now analyze how we did react-gleam and tell us step by step what we need to change to reimplement it for PREACT instead. Make an outline , kanban board, and start writing code too for a design document for preact-gleam. Deminify anything from the preact codebase too as needed please.  Basically i want you to create preact-gleam from scratch, make the entire project tree of files, and reference the minified library code for everything to map it out to gleam functions on an as needed basis. Make ALL the files including README.md for preact-gleam please, and implement clientside and server side code for actually SERVING our distributed webserver app using our custom cookie encryption system we wrot. Make it a scalable server too.