The field guide

How Unicron works, day to day — capture, find, write, and work the graph, plus every keyboard shortcut. This is the same guide that ships inside the app, so you can read the whole thing before you download.

Concepts

What the vault is and how it reasons.

What Unicron is

Unicron is a second brain built on a folder of plain markdown files — no database. Every note is a file, and every link between notes is an edge. Each note also has a type and a schema it's checked against, which is what makes the vault a small knowledge graph rather than a pile of linked notes: typed things, with defined relationships the app can read and reason over. It reads those files and draws them as a living graph you can browse and search.

What you actually do is capture — paste a meeting transcript, jot a thought, drop something in the inbox. Claude Code is the engine that turns that into structure: running locally, it reads your messy input, writes it into a typed note, and works out where the note belongs: its area, its project, the people it involves. From that the app infers the graph's edges, and you skim and confirm. You never assemble the graph by hand; you capture, Claude Code structures it, and the relationships fall out.

This guide covers what the vault knows, how it works out relationships on its own, and where it's headed. It's meant to grow — a map we keep updating as we build, with a status badge on everything so it stays honest about what's real.

Four words to know

Once you think of the vault as a knowledge graph, four words show up everywhere:

  • T-Box (terminological) — the schema. The kinds of note that exist and the links allowed between them. It lives in ontology.md.
  • A-Box (assertional) — the assertions. Your actual notes, and the links between them.
  • Predicate — a typed edge. Not just "A links to B" but how: belongsTo, involves, mentions.
  • Provenance — where a fact came from and how sure we are. A link you wrote by hand, or one the app inferred.

Two graphs in one vault

There are really two graphs hiding in a vault.

The one you started with is an entity graph: it describes what exists and how things sit together — areas, projects, people, and meetings, joined by containment and mention.

The second is a process graph: what happens, and what leads to what. A decision, the steps it sets off, the outcome it produces. That's the decision module, and it's live now — a genuinely different shape from the entity graph. The web standardized this shape long ago as PROV-O, the Provenance Ontology (Agent, Activity, Entity), which is a good sign it holds well beyond a personal vault.

Your plans, formalized

Sketch how something actually gets done — who's acting, the steps they take, the things those touch, the result it produces — and it maps almost one-to-one onto PROV-O, the web's standard for provenance.

  • The one making the calls is an Agent — you, or Claude working on your behalf.
  • The steps are Activities.
  • The things they touch are Entities.
  • The result is the Outcome.

Nearly every edge in that picture you can assert for free: an agent performed a step, a step produced a result. Plain facts, no judgment call. The edge you can't is the causal one. That the outcome followed the plan is checkable; that it was caused by the plan is a claim only you should confirm. So that edge — a decision's leadsTo — stays dotted and proposed until you sign off.

That's the aim: your ordinary planning, captured as a graph with real provenance, where the app asserts what it can prove and leaves the causal claims to you.

leadsTo · proposed, needs your sign-off You AGENT Gather inputs ACTIVITY · 01 Weigh options ACTIVITY · 02 Make the call ACTIVITY · 03 Act on it ACTIVITY · 04 A note ENTITY Check GATE Result OUTCOME NODE ROLES Agent Activity Entity Gate Outcome EDGE STATES authored inferred proposed

Keeping it honest

This graph stays true as it grows by running a loop between two halves that fail in opposite ways.

  • Neuro — Claude reads your messy notes and proposes structure. High recall, but it can guess wrong.
  • Symbolic — the ontology and its rules check each proposal, store it with its provenance, and never invent anything. High precision.
  • You — view and confirm. Confirmed facts ground the next pass.

You already run this loop every day. It's called triage: Claude proposes where a capture belongs, you confirm. Everything here is that same pattern, widened from filing notes to confirming relationships.

One rule keeps it from rotting: inferred and proposed facts stay re-derivable and never get hand-edited. The moment you edit a guessed edge as if you wrote it, the system can't tell truth from guess anymore.

STICKING TO THE TRUTH 01 NEURO Claude proposes 02 SYMBOLIC rules validate 03 YOU VERIFY view & confirm 04 CONFIRMED grounds next pass

Your schema

The types, relationships, and rules — read live from ontology.md.

The note types

Every note declares a type. The Graph view colors each note by its type, using the same hues shown above. The legend is read straight from your vault's ontology.md, so it's your actual schema — not a description of it.

Notes under raw/ stay untyped on purpose — they're quick captures waiting for triage, not part of the graph yet.

How notes connect

Notes connect through typed relationships — predicates. Each one is derived: the app reads it from a frontmatter field, a folder, or a link you wrote. The provenance says how the edge came to be — one you authored (a link you typed) or one the app inferred (worked out from structure). This table reads live from ontology.md.

The rules it checks

Rules the vault can quietly break — read live from ontology.md. They're advisory: the app checks your notes against them and flags what's drifted, but never blocks or edits anything. See how your vault does just below.

How your vault checks out

Every note, measured against the rules above. A broken reference — a project: or area: slug that points at a note that doesn't exist — is flagged in red; softer drifts (a project with no area, an area missing from your profile's ## Areas list, an unfamiliar kind or status) in amber. Most carry a picker that sets the field for you; a few — like an area missing from your profile — just open the note, because the repair is a line of prose somewhere else rather than a value to choose. Nothing here changes your files on its own.

Using it

What you do day to day — capture, find, write, and work the graph.

Capturing

The fastest way in is Add — the + in the top bar, or ⌘N. Pick what you're capturing (a Meeting, Project, Person, Area, Decision, Journal, or a quick Task), paste or jot what you've got, and Claude Code writes it into a properly-typed note in the right folder. For a meeting you can paste the whole transcript; it pulls out who was there, updates those people, and files the note under its area.

The inbox

Anything you drop in raw (a paste, a note from your phone, a Shortcut) lands in the inbox, untyped and waiting. Run Triage and Claude reads each capture, works out its type and where it belongs, and files it as a real note — you review the result. The top bar flags the inbox whenever something's waiting.

Writing a note

A note opens in preview; hit ⌘E (or the pencil) to edit. In edit mode, type @ and a name to drop in a person link (the path is worked out for you), and / for a menu of formatting and blocks. Tables edit cell by cell, with row and column controls. Save, and the app re-reads the note so the graph stays current.

Diagrams. A fenced mermaid block renders as a diagram in preview, and stays plain text in edit mode and in every other markdown editor — it's a code fence, not a special file format:

```mermaid
flowchart LR
  A[Opened] --> B[Consent] --> C[Identity]
```

Flowcharts, sequence diagrams, state diagrams, ER diagrams — mermaid's syntax covers all of it. A diagram wider than the reading column scrolls inside its own box rather than stretching the page, and one that fails to parse shows the error and its source instead of taking the note down with it.

You can theme a diagram inline with a %%{init: …}%% directive on its first line. When you do, that wins — the app only applies its own light/dark theme to diagrams that haven't asked for one.

Tasks

Any - [ ] line in any note is a task, and the Tasks view gathers them all — no separate to-do app. But not every checkbox is yours to act on, so the view splits them by who owns the item:

  • Mine — your actual to-do list, and the bucket that counts as load. Check things off, defer one to a later date, or hit Select to mark, or assign, a whole batch at once. Checking a task writes straight back to the note it came from.
  • Waiting on — things other people owe you. Put a name in front of the task (**Jane** — send the contract), or write it in that person's note, and it lands here instead of Mine. No checkbox and no count: you scan them to see who owes you what, you don't maintain them. When someone finally delivers, whatever you do next is a fresh task of your own.
  • Threads — ambient context in a client area that was never a to-do. Anything under a client area — its projects, meetings and decisions, not just the area note itself — that doesn't name an owner lands here. Same passive treatment as Waiting on.

Who owns a task is read from the line, so the fix for one in the wrong bucket is to say whose it is. The menu on any row has Assign to, and Select mode has the same thing in its bar for doing a batch at once. It writes the name into the markdown — - [ ] **Jane** — send the contract — so you sort a line once and it stays sorted. ⌘Z takes it back like any other change.

Assigning is the one thing Waiting on and Threads allow, because pulling back work that's actually yours is the only way something leaves those buckets. Everything else there stays passive — you don't complete other people's commitments.

Writing one by hand, the shape is - [ ] **Sam** — send the contract. Shared work takes a list — **Sam + Alex** or **Sam / Alex** — and **Unassigned** is the honest answer when nobody's holding it yet. A task with no owner at all is a guess the app has to keep making, so Claude names one whenever it files a task. Bare lines in your vault predate that, and assigning them is how they get retired.

Later is the fourth chip: anything you've deferred, counted and sorted by when it comes back, out of the other buckets until then.

A task lives in exactly one note. The note it belongs to is the durable one — a person's note for what they owe, a project or area note for your own work. Meeting notes record what was assigned on the day as plain bullets, not checkboxes, because the same commitment used to get written into the meeting, every person named, and the area note all at once. Four copies, worded a little differently each time, and checking one off left the other three open. Claude writes the checkbox once now, in the note that will still be true next month, and the meeting keeps the full record without competing for it.

Running /focus or /tasks also writes Tasks.md to the vault root: your own open tasks, grouped by area, as plain bullets. It's a read-only mirror for reading the list on a phone through whatever syncs your vault. Nothing reads it back and every run overwrites it, so check things off here or in the source note.

That split runs everywhere tasks show up. A project's Open tasks count is yours alone, with a quiet "Waiting on" list beneath it, and inside a note someone else's - [ ] reads as a plain bullet instead of a checkbox you'd check off.

Filter the whole view to one area or project with the scope control up top.

Documents

A document is a long note that belongs to a project — research, thinking, a draft, talking points for a conversation. It's the one note type allowed to run long.

Everything else about it follows from that:

  • It always has a project, and through the project, an area. A document is never free-floating. If a piece of writing has no project, it isn't a document — it's a topic, which is where vault-level thinking lives.
  • It holds no tasks. A commitment that comes out of writing one belongs on the project note, where the rest of that project's tasks already are. Type a - [ ] into a document and the Field Guide flags it rather than silently swallowing it.
  • Its description is what gets indexed, not its body. The project note carries a ## Documents row — title, one line, link — and that row is all most things ever read. That's deliberate: a handful of 400-line documents in the ambient read set would double what the daily brief has to chew through every morning, for material that has nothing to do with today. So write a real description; it's the whole index.

Create one with Add ▸ Document, or /document in the terminal. Pick the project and the area comes with it.

Documents you want left alone entirely can carry readonly: true — see Keeping Claude out.

The graph & its edges

The Graph view draws the whole vault. Every note is a node, sized by how many links it has; every link is an edge. Two layouts:

  • Force — physics pulls connected notes together. The relationship view.
  • Tree — a top-down hierarchy rooted at you. The structure view.

Edges come in two kinds, and the Edges toggle in the graph toolbar switches between them:

  • Solid — written. A link you actually typed in a note.
  • Dashed — inferred. A relationship the app worked out from frontmatter and folders, like a project belonging to its area. Nobody wrote it as a link; Unicron figured it out.

Flip to Written to see only what you wrote by hand. Flip to All to add back everything the app inferred. Hover any edge to see where it came from — a written link, or which predicate inferred it.

You don't have to wire the graph

You never have to draw those dashed connections yourself.

When you tell a note where it lives — its area:, its project:, or just the folder you keep it in — the app reads that and makes the connection. A project joins its area; a meeting joins its project and its area. You fill in the note, and the relationships appear as dashed edges.

So don't spend effort linking a note back to its own area or project to "connect the graph." The engine already did, and deleting such a link wouldn't pull anything apart — the dashed edge stays.

Areas are the one exception, because an area has nothing above it to be inferred from. What ties it in is its row in the ## Areas list on your profile, and that link is written, not derived. /area and the Capture dialog add it when they create one; if an area is missing from the list you'll see it flagged below, and floating loose in the graph.

The links worth writing by hand are the ones the app can't guess: a person who matters to a project, a meeting that reaches into another area's work, a reference that crosses the usual lines. Those are your solid edges. Write about what's connected and the links follow; you're never gardening the graph to keep it whole.

Using Decisions

A decision is a choice you're tracking. Create one with ⌘N ▸ Decision — it lives in its area, like a project. Its steps are just task lines (- [ ]), so they roll into your Tasks, and you can note who made the call with a decidedBy field.

Move it through its lifecycle from the status control — in the note header or in View ▸ Decisions, no YAML editing: open while you're still deciding, made once you've called it, revisited if you come back to it.

When you know how it turned out, hit Record outcome — on the decision in the Decisions view, or from the note header. That writes the outcome note, points the decision's leadsTo at it, and adds a linked ## Outcome section to the decision itself, so the connection is visible when you read the note rather than living only in frontmatter. Recording one by hand is the assertion, so the edge is confirmed straight away.

Once a decision has an outcome, its header shows Outcome: →</strong> where the Record button was — the relationship is the point of a decision note, so the page shouldn't go quiet about it.</p> <p>A <code>leadsTo</code> written by hand, or proposed by Claude, is a different matter: the graph draws it <strong>dotted</strong> until you agree, because the app won't claim one thing caused another on its own say-so. Find the decision in <strong>View ▸ Decisions</strong> and hit <strong>Confirm</strong> to turn the dotted line solid.</p> <p>The rail keeps score at a glance — each decision carries a status dot on its right edge (open blue, made green, revisited amber) and a check once the loop is closed. That's the whole thing in miniature: the app proposes structure, and you confirm what's actually true.</p></div> </section><section class="guide__section" id="protected"> <h3 class="guide__section-title">Keeping Claude out</h3> <div class="guide__prose"><p>Claude can edit any note in your vault — that's the point of it — but some writing is yours and shouldn't be touched. Two markings say so.</p> <p><strong>A protected region</strong> fences off part of a note. Type <code>/</code> and pick <strong>Protected region</strong>, or select a passage first and the markers wrap it. They're HTML comments, so they're invisible everywhere the note gets read:</p> <pre><code><!-- claude:keep-out --> The part you wrote and want left alone. <!-- /claude:keep-out --> </code></pre> <p>Claude still reads it and can answer from it. It just won't rewrite it, and it will leave the rest of the note editable — which is the point, since an area note usually has your narrative sitting next to a Meeting history index Claude maintains.</p> <p><strong>A read-only note</strong> puts the whole file off-limits: add <code>readonly: true</code> to its frontmatter. Use it for something you type and maintain entirely yourself.</p> <p><strong>This is detection, not a lock.</strong> Claude writes with its own tools, so both markings are instructions it could misread. What the app guarantees is that it gets caught: every run is diffed, and a protected passage that comes back different is flagged in the review card with the revert one click away. If a note is genuinely irreplaceable, <code>chmod a-w</code> on the file is the only thing that physically stops a write.</p></div> </section> </section> <section class="guide__chapter" id="shortcuts" aria-labelledby="ch-shortcuts"> <div class="guide__chapter-head"> <h2 class="guide__chapter-title" id="ch-shortcuts">Keyboard shortcuts</h2> <p class="guide__chapter-blurb"> The full cheat sheet. Inside the app, press <kbd>⇧⌘K</kbd> to bring it up any time. </p> </div> <div class="guide__shortcuts"> <section class="guide__sc-group"> <h3 class="guide__sc-title">General</h3> <dl class="guide__sc-list"> <div class="guide__sc-row"> <dt class="guide__sc-desc">Open vault</dt> <dd class="guide__sc-keys"> <kbd>⌘O</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">New vault</dt> <dd class="guide__sc-keys"> <kbd>⇧⌘N</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Show the shortcuts list</dt> <dd class="guide__sc-keys"> <kbd>⇧⌘K</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Close a dialog or menu</dt> <dd class="guide__sc-keys"> <kbd>Esc</kbd> </dd> </div> </dl> </section><section class="guide__sc-group"> <h3 class="guide__sc-title">Navigation</h3> <dl class="guide__sc-list"> <div class="guide__sc-row"> <dt class="guide__sc-desc">Jump to a note</dt> <dd class="guide__sc-keys"> <kbd>⇧⌘J</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Profile</dt> <dd class="guide__sc-keys"> <kbd>⌘U</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Today</dt> <dd class="guide__sc-keys"> <kbd>⌘J</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Tasks</dt> <dd class="guide__sc-keys"> <kbd>⌘K</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Library</dt> <dd class="guide__sc-keys"> <kbd>⌘L</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Graph</dt> <dd class="guide__sc-keys"> <kbd>⌘;</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Chat</dt> <dd class="guide__sc-keys"> <kbd>⌘'</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Back / Forward</dt> <dd class="guide__sc-keys"> <kbd>⌘[</kbd> <span class="guide__sc-or">/</span><kbd>⌘]</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Toggle sidebar</dt> <dd class="guide__sc-keys"> <kbd>⌥⌘S</kbd> </dd> </div> </dl> </section><section class="guide__sc-group"> <h3 class="guide__sc-title">Search and find</h3> <dl class="guide__sc-list"> <div class="guide__sc-row"> <dt class="guide__sc-desc">Search</dt> <dd class="guide__sc-keys"> <kbd>/</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Find in page</dt> <dd class="guide__sc-keys"> <kbd>⌘F</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Find next / previous</dt> <dd class="guide__sc-keys"> <kbd>⌘G</kbd> <span class="guide__sc-or">/</span><kbd>⇧⌘G</kbd> </dd> </div> </dl> </section><section class="guide__sc-group"> <h3 class="guide__sc-title">Notes and capture</h3> <dl class="guide__sc-list"> <div class="guide__sc-row"> <dt class="guide__sc-desc">Add (capture)</dt> <dd class="guide__sc-keys"> <kbd>⌘N</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Submit a dialog (Save / Triage)</dt> <dd class="guide__sc-keys"> <kbd>⌘↵</kbd> <span class="guide__sc-or">/</span><kbd>⌘S</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Edit the current note</dt> <dd class="guide__sc-keys"> <kbd>⌘E</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Save while editing a note</dt> <dd class="guide__sc-keys"> <kbd>⌘S</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Refresh from disk</dt> <dd class="guide__sc-keys"> <kbd>⌘R</kbd> </dd> </div> </dl> </section><section class="guide__sc-group"> <h3 class="guide__sc-title">Tasks</h3> <dl class="guide__sc-list"> <div class="guide__sc-row"> <dt class="guide__sc-desc">Move the cursor</dt> <dd class="guide__sc-keys"> <kbd>J</kbd> <span class="guide__sc-or">/</span><kbd>K</kbd> <span class="guide__sc-or">/</span><kbd>↑</kbd> <span class="guide__sc-or">/</span><kbd>↓</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Mark done</dt> <dd class="guide__sc-keys"> <kbd>⌘D</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Mark won't do</dt> <dd class="guide__sc-keys"> <kbd>⇧⌘D</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Toggle selection (in Select mode)</dt> <dd class="guide__sc-keys"> <kbd>Space</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Undo the last mark</dt> <dd class="guide__sc-keys"> <kbd>⌘Z</kbd> </dd> </div> </dl> </section><section class="guide__sc-group"> <h3 class="guide__sc-title">Library and drilled views</h3> <dl class="guide__sc-list"> <div class="guide__sc-row"> <dt class="guide__sc-desc">Switch between filters and the list</dt> <dd class="guide__sc-keys"> <kbd>Tab</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Drill into an area/project · isolate a filter</dt> <dd class="guide__sc-keys"> <kbd>Enter</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Toggle a filter (in the chips)</dt> <dd class="guide__sc-keys"> <kbd>Space</kbd> </dd> </div><div class="guide__sc-row"> <dt class="guide__sc-desc">Back out of a drilled view</dt> <dd class="guide__sc-keys"> <kbd>←</kbd> </dd> </div> </dl> </section> </div> </section> <aside class="guide__cta"> <p class="guide__cta-text"> That's the map. The app ships with this same guide inside, plus a live view of your own vault's schema. </p> <a class="btn btn--primary" href="https://unicron.lemonsqueezy.com/checkout/buy/c3a649da-1f7c-4e25-9db3-755f52c7a964">Download Unicron</a> </aside> </div> </div> </div> </main> <footer class="footer"> <div class="container footer__inner"> <a class="footer__brand" href="#top"> <img src="/unicron.svg" alt="" width="22" height="22"> <span>Unicron</span> </a> <nav class="footer__links" aria-label="Footer"> <a href="/guide">Guide</a> <a href="/changelog">Changelog</a> <a href="/terms">Terms</a> <a href="/privacy">Privacy</a> <a href="/llms.txt" target="_blank" rel="noopener">llms.txt</a> </nav> <p class="footer__meta"> © <a href="https://konigi.com">Konigi</a> · Built by <a href="https://michaelangeles.com">Michael Angeles</a> </p> </div> </footer> <script type="module">const o=new Map;document.querySelectorAll(".guide__toc-link").forEach(e=>{const t=e.getAttribute("href");t&&t.startsWith("#")&&o.set(t.slice(1),e)});const r=[...o.keys()].map(e=>document.getElementById(e)).filter(e=>e);if(r.length){let t=null,s=!1;const l=()=>{s=!1;let i=r[0].id;for(const n of r)n.getBoundingClientRect().top-100<=0&&(i=n.id);i!==t&&(t=i,o.forEach((n,a)=>n.classList.toggle("is-active",a===i)))},c=()=>{s||(s=!0,requestAnimationFrame(l))};window.addEventListener("scroll",c,{passive:!0}),window.addEventListener("resize",c,{passive:!0}),l()}</script> <script> // Quiet scroll-reveal: fade + rise each [data-reveal] element in once as it // enters view, then stop observing it. No-JS / old browsers just see content, // and the CSS only hides elements when motion is allowed (prefers-reduced-motion). (() => { if (!('IntersectionObserver' in window)) return; const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (!entry.isIntersecting) return; entry.target.classList.add('is-in'); observer.unobserve(entry.target); }); }, { rootMargin: '0px 0px -10% 0px' } ); document.querySelectorAll('[data-reveal]').forEach((el) => observer.observe(el)); })(); </script> <script> // Video playback strategy, split by device: // // iPhone/iPad: never autoplay. Each clip shows its poster with a play button, // and the video only loads when the user taps it. Several large videos // autoplaying at once overruns iOS Safari's per-tab memory budget and WebKit // kills the tab — tap-to-play means at most one clip is ever decoding. // // Everywhere else: autoplay each clip while it's on screen. When it scrolls // away we detach its <source>s and call load() so the browser frees the // decoded frames (pause() alone leaves them resident). The poster image stays // visible throughout, so loading and teardown are both invisible. // // Honors prefers-reduced-motion (stays on the poster). No-JS shows the poster. (() => { const videos = [...document.querySelectorAll('video[data-inview]')]; if (!videos.length) return; const hasIO = 'IntersectionObserver' in window; const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; // Stash each video's original <source>/<img> markup so we can restore it // after teardown. Read it once up front, before anything is detached. const markup = new WeakMap(); videos.forEach((video) => markup.set(video, video.innerHTML)); const load = (video) => { if (video.dataset.live === '1') return; video.innerHTML = markup.get(video); video.load(); video.dataset.live = '1'; }; const release = (video) => { video.pause(); video.innerHTML = ''; video.removeAttribute('src'); video.load(); // aborts the resource and frees decoded frames; poster stays video.dataset.live = '0'; }; // iPadOS 13+ reports as a Mac, so also treat touch-capable "Macs" as iOS. const ua = navigator.userAgent; const isIOS = /iP(hone|ad|od)/.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); if (isIOS) { videos.forEach((video) => { const stage = video.parentElement; // .figure or .frame stage.classList.add('media--tap'); const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'media__play'; btn.setAttribute('aria-label', 'Play video'); btn.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg>'; stage.appendChild(btn); btn.addEventListener('click', () => { load(video); video.controls = true; // give them native pause/scrub once it's playing const play = video.play(); if (play && play.catch) play.catch(() => {}); stage.classList.add('is-playing'); }); // If a playing clip scrolls away, free its memory and put the poster + // button back, so playing several in a row can't pile up in memory. if (hasIO) { const io = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting || video.dataset.live !== '1') return; release(video); video.controls = false; stage.classList.remove('is-playing'); }); }, { threshold: 0 } ); io.observe(video); } }); return; } if (!hasIO || reduce) return; const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { const video = entry.target; if (entry.isIntersecting) { load(video); const play = video.play(); if (play && play.catch) play.catch(() => {}); } else { release(video); } }); }, { threshold: 0.25 } ); videos.forEach((video) => observer.observe(video)); })(); </script> <script type="module" src="/_astro/Base.astro_astro_type_script_index_0_lang.B_i0yCLd.js"></script> </body> </html>