Introducing Openloop - an open-source workflow automation platform that runs on browser

January 31, 2025 (2 months ago)0 views

Openloop Usage

One week ago, I set out to build Openloop, an open-source take on workflow automation platforms. What started as a blank canvas quickly turned into a full-fledged tool that lets you create, connect, and run workflows entirely in the browser -- no servers, no authentication.**

🔥 What makes Openloop special?

🎨 Logo

To build it fast I sketched "Openloop" on my iPad, moved it to Figma as an SVG, and painted "open" green. I love how it turned out 🎨

Openloop Logo

📝 Forms

To not spend much time on forms, I decided to use automatically render forms based on zod schemas. I'm thinking on publishing a package for this. Here's sample code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function ZodForm({ schema, onSubmit, children, ...props }) {
  const [values, setValues] = useState({});
  return <form {...props} onSubmit={onSubmit}>
    <ZodFieldComponent
      name=""
      schema={schema}
      onChange={(_, value) => setValues(value)}
      value={values}
    />
    {children}
  </form>
}

function ZodFieldComponent({ name, schema: parentSchema, onChange, value }) {
  // find the actual field schema and not .default or .optional
  const schema = zodSchemaToInnerSchema(parentSchema)

  switch (schema._def.typeName) {
    case 'ZodObject':
      return <div>
        {Object.entries(schema.shape).map(([key, value]) => (
          <ZodFieldComponent key={key} name={key} schema={value} onChange={onChange} value={value} />
        ))}
      </div>
    case "ZodString":
      return <input type="text" name={name} value={value} onChange={(e) => onChange(name, e.target.value)} />
    case "ZodNumber":
      return <input type="number" name={name} value={value} onChange={(e) => onChange(name, e.target.value)} />
    case "ZodBoolean":
      return <input type="checkbox" name={name} checked={value} onChange={(e) => onChange(name, Boolean(e.target.checked))} />
    default:
      return null
  }
}

function zodSchemaToInnerSchema (f: ZodSchema) {
  let fieldInnerSchema = f as ZodFirstPartySchemaTypes;

  while (
    fieldInnerSchema._def.typeName === "ZodOptional" ||
    fieldInnerSchema._def.typeName === "ZodDefault"
  ) {
    fieldInnerSchema = fieldInnerSchema._def.innerType
  }

  return fieldInnerSchema
}

🎉 Conclusion

This took around a week to build, and I'm excited to share it with the world. If you're into automation, AI, or open-source tools, check it out, and let me know what you think!