Getting Started

Build a complete LOSOS app in 5 minutes. No npm. No build tools.

1. Get the files

Create a project directory and copy the framework files:

my-app/
  losos/
    html.js       ← from losos.org/losos/html.js
    store.js      ← from losos.org/losos/store.js
    shell.js      ← from losos.org/losos/shell.js
  lion/
    index.js      ← from losos.org/lion/index.js
  panes/
    my-pane.js    ← you write this
  data.jsonld     ← you write this
  index.html      ← you write this

2. Create the data

A JSON-LD file with @context mapping short keys to standard URIs:

{
  "@context": {
    "schema": "http://schema.org/",
    "dct": "http://purl.org/dc/terms/",
    "title": "dct:title",
    "item": "schema:hasPart",
    "name": "schema:name",
    "done": "schema:status",
    "List": "schema:ItemList",
    "Item": "schema:ListItem"
  },
  "@id": "#this",
  "@type": "List",
  "title": "My List",
  "item": [
    { "@id": "#1", "@type": "Item", "name": "First item", "done": false },
    { "@id": "#2", "@type": "Item", "name": "Second item", "done": true }
  ]
}

3. Create the pane

A pane is an ES module with canHandle and render:

import { createStore } from '../losos/store.js'
import { html, render, onUnmount, keyed } from '../losos/html.js'

export default {
  label: 'My App',
  icon: '📋',

  canHandle(subject, store) {
    var node = store.get(subject.value)
    var type = store.type(node)
    return type && type.includes('List')
  },

  render(subject, lionStore, container, rawData) {
    var data = rawData
    var store = createStore(data, { debounce: 800 })
    var root = store.get('#this')

    function renderApp() {
      var items = store.propAll(root, 'item')
      render(container, html`
        <h1>${data['title']}</h1>
        <input type="text" placeholder="Add item..."
          onkeydown="${function(e) {
            if (e.key !== 'Enter') return
            store.push(root, 'item', {
              '@id': '#' + Date.now(),
              '@type': 'Item',
              'name': e.target.value,
              'done': false
            })
            e.target.value = ''
          }}" />
        ${keyed(items, function(i) { return i['@id'] }, function(i) {
          return html`
            <div>
              <input type="checkbox"
                checked="${i['done']}"
                onchange="${function() { store.set(i, 'done', !i['done']) }}" />
              ${i['name']}
            </div>
          `
        })}
      `)
    }

    var unsub = store.onChange(renderApp)
    setTimeout(renderApp, 0)
    onUnmount(container, unsub)
  }
}

4. Create the HTML shell

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>My App</title>
</head>
<body>
  <script type="application/ld+json" src="data.jsonld"></script>
  <script type="module" data-pane src="panes/my-pane.js"></script>
  <div id="losos"></div>
  <script type="module" src="losos/shell.js"></script>
</body>
</html>

5. Open in browser

Serve with any static server:

npx serve .
# or
python3 -m http.server

That's it. No npm install, no build, no configuration.


What happens when you load the page

  1. Shell scans for <script data-pane> tags and loads each pane module
  2. Shell fetches data.jsonld into a LION store
  3. Shell finds the primary subject (#this), checks each pane's canHandle
  4. Shell builds a tab bar and renders the first matching pane
  5. Shell passes the parsed JSON-LD as the 4th argument to render

Adding persistence

To auto-save changes to a Solid pod or any HTTP server that accepts PUT:

var store = createStore(data, {
  url: 'https://my-pod.example/data.jsonld',
  authFetch: window.xlogin.authFetch,
  debounce: 800
})

Every store.set() now debounces a PUT request. If the server sends an Updates-Via header, the store auto-subscribes to WebSocket updates.


Next steps