resworbDownload resworb
Back

tasks.lmth

All 54 lines. No libraries, nothing hidden. Read it before you run it.

tasks.lmth
<!doctype html>
<meta charset="utf-8">
<meta name="resworb-write" content="./*.md">
<title>Tasks</title>
<style>
  body { margin: 0; padding: 24px; color: #111; font: 14px/1.4 'Helvetica Neue', sans-serif; }
  main { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
  section { min-height: 70vh; padding: 12px; border-top: 1px solid #111; background: #f7f7f8; }
  h2 { margin: 0 0 12px; color: #002fa7; font-size: 13px; font-weight: 500; text-transform: uppercase; }
  article { margin-bottom: 8px; padding: 10px 12px; border: 1px solid #e3e3e8; background: #fff; cursor: grab; }
  small { display: block; color: #6b6b72; }
</style>
<main id="board">
  <section data-status="todo"><h2>To do</h2></section>
  <section data-status="doing"><h2>Doing</h2></section>
  <section data-status="done"><h2>Done</h2></section>
</main>
<script type="module">
  // Each .md file in this folder is a card. Its column is the status line at the top, which a drop rewrites:
  // ---
  // status: doing
  // ---
  const columns = [...board.children];
  const statusLine = /^---\nstatus: (\w+)\n---\n/;

  for (const column of columns) {
    column.ondragover = event => event.preventDefault();
    column.ondrop = async event => {
      const name = event.dataTransfer.getData('text/plain');
      const text = await resworb.readFile(name);
      await resworb.writeFile(name, `---\nstatus: ${column.dataset.status}\n---\n${text.replace(statusLine, '')}`);
    };
  }

  // Draws every card at once, so a watch event that lands mid-draw can't leave a card twice.
  async function draw() {
    const cards = { todo: [], doing: [], done: [] };
    for (const name of await resworb.readdir('.')) {
      if (!name.endsWith('.md')) continue;
      const text = await resworb.readFile(name);
      const card = document.createElement('article');
      card.draggable = true;
      card.ondragstart = event => event.dataTransfer.setData('text/plain', name);
      card.textContent = text.match(/^# (.+)$/m)?.[1] ?? name;
      card.append(Object.assign(document.createElement('small'), { textContent: name }));
      (cards[text.match(statusLine)?.[1]] ?? cards.todo).push(card);
    }
    for (const column of columns) column.replaceChildren(column.firstElementChild, ...cards[column.dataset.status]);
  }

  await draw();
  // A drop above, or a save in another editor, redraws the board.
  await resworb.watch('.', draw);
</script>
Download tasks.lmth