const escape = value => String(value).replace(/[&<>"']/g, char => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char])); const banners = { handbook: new URL('./banners/banner-contour.svg', import.meta.url), showcase: new URL('./banners/banner-blue.svg', import.meta.url), development: new URL('./banners/banner-band.svg', import.meta.url), quality: new URL('./banners/banner-light.svg', import.meta.url), wiki: new URL('./banners/banner-topo.svg', import.meta.url), }; const link = (label, href, detail = '') => href ? `${escape(label)} ${detail ? `${escape(detail)}` : ''}` : ''; const panel = (title, text, links, extra = '') => `

${escape(title)}

${escape(text)}

${extra}`; export function buildPresentation(w) { const table = name => { const t = w.findTable(name); return t && !t.deletedAt ? `/w/weave/#/table/${t.id}` : ''; }; const space = name => { const s = w.listSpaces().find(x => x.name === name); return s ? `/w/weave/#/space/${s.id ?? s.spaceId}` : ''; }; const record = (name, title) => { const t = w.findTable(name); const r = t && w.findEntity(t, title); return r && !r.deletedAt ? `/w/weave/e/${r.id}` : ''; }; const guide = name => record('Handbook/Guide', name); const go = (name, text) => link(name, guide(name), text); const publicWork = [link('Issues', table('Development/Issue'), 'Report, follow, and investigate bugs.'), link('Roadmap', table('Development/Feature'), 'See what is proposed and being built.'), link('Release notes', table('Development/Release'), 'Read the changes in each release.'), link('Test suites', table('Quality/Suite'), 'Explore the public test documentation.'), link('Test cases', table('Quality/Case'), 'Follow the individual checks.')]; const examples = [link('Projects', table('Showcase/Projects'), 'Follow tasks back to their project.'), link('Notes', table('Showcase/Notes'), 'One relation can point to different tables.'), link('Expenses', table('Showcase/Expenses'), 'Read a total computed from line items.'), link('Field types', table('Showcase/Field Types'), 'Compare fields using real records.')]; const templateLinks = [link('CRM template', space('CRM'), 'Open the space, then choose Use template.'), link('Money template', space('Money'), 'Copy its structure into your workspace.')]; const start = panel('Explore weave by using weave.', 'Read a guide, follow a working example, or inspect the public work behind the product.', [go('Quickstart', 'Find your way around.'), go('A project and its linked work', 'Follow a connected example.'), go('Connect an agent', 'Set up access to your workspace.'), ...publicWork]); const showcase = panel('Follow the connections.', 'These are working tables. Open a record, follow a relation, then read how the field is configured.', [...examples, go('A project and its linked work', 'A guided route through the examples.'), ...templateLinks]); const development = panel('Built in public.', 'Issues, the roadmap, and release notes connect requests to the work that reaches users.', publicWork.slice(0, 3)); const quality = panel('Read the checks behind the product.', 'Suites group the tests by behavior. Cases describe the individual checks. Both remain part of the public workspace.', publicWork.slice(3)); const wiki = panel('Understand the design.', 'Read the architecture and the decisions behind the data model, then return to a working example.', [link('Articles', table('Wiki/Article')), go('A project and its linked work'), link('Showcase', space('Showcase'))]); const setup = `
Browser-based agent
  1. Open your own workspace’s agent connection guide.
  2. Copy its MCP address into your client’s connector setup.
  3. Sign in when the client opens weave. Your workspace role determines access.
Claude Code

Use the command in the native connection guide for your workspace. It adds the workspace’s HTTP MCP server; sign in when prompted.

Codex

Add your workspace’s MCP URL with codex mcp add weave --url <workspace-mcp-url>, then run codex mcp login weave.

Check the connection

Ask: “Read the workspace outline and describe its spaces and tables. Do not change anything.” Confirm it names your workspace before asking it to edit records.

`; const projectUrl = record('Showcase/Projects', 'Apollo Launch'); return { spaces: [ { name: 'Handbook', previousDescription: "**Official documentation and how-tos** — the pages you would hand to someone using Weave for the first time.\n\nPrescriptive rather than exploratory: how to install it, how the data model works, what each field type does. Design rationale and open questions belong in **Wiki** instead.", description: 'Guides to using weave: build a workspace, connect an agent, and learn from working examples.', bannerPath: banners.handbook, html: start }, { name: 'Showcase', previousDescription: "What a field can be, and what a document can hold. `Field Types` puts every field type and several configurations of the same type side by side in one grid; `Formatting` gives every document construct a row written in the construct it names.", description: 'Working examples of relations, workflows, computed fields, documents, and templates. Open the records to see how they connect.', bannerPath: banners.showcase, html: showcase }, { name: 'Development', bannerPath: banners.development, html: development }, { name: 'Quality', bannerPath: banners.quality, html: quality }, { name: 'Wiki', previousDescription: "**Design notes and architecture** — why Weave is shaped the way it is.\n\nWhere the Handbook says *how*, the Wiki says *why*: the trade-offs behind the storage layer, the reasoning behind the API surface, notes that are still being argued with. Content graduates to a Handbook guide once it stops changing.", description: 'Architecture, data-model decisions, and the reasoning behind weave.', bannerPath: banners.wiki, html: wiki }, ], tables: [ { name: 'Handbook/Guide', previousDescription: "**Handbook pages** — the prescriptive documentation set.\n\n| Field | Purpose |\n| --- | --- |\n| `Description` | The page body, in markdown |\n| `Audience` | Who it is written for |\n| `Order` | Reading sequence within the Handbook |\n\nA guide should be safe to follow verbatim. If something is still being decided, it belongs in a Wiki article until it settles.", description: "Practical guides for building a workspace, using linked records, and connecting an agent.", bannerPath: banners.handbook, html: start }, { name: 'Handbook/Fields', previousDescription: "**One page per field type** — the living reference for every field weave can make.\n\nThe storage primitives (`text`, `number`, `date`, `daterange`, `checkbox`, `url`, `email`), the choosers (`select`, `multiselect`, `workflow`), `relation` (always a bidirectional pair), the computed types (`lookup`, `rollup`, `formula`), `document`, and the three the meta-model earned: `field` (a value that IS a field definition), `key` (the name of a secret, never the secret) and `attachments`.\n\n`Kind` groups them. Each page names every config key the vocabulary lists for its type, the closed set each key draws from, and what bites. `test/handbook.test.mjs` fails if a type ships without a page, or a page quotes a vocabulary the engine no longer has.", description: "Every field type, with its configuration, examples, and limitations.", bannerPath: banners.quality, html: panel('Choose a field. See what it does.', 'Each field reference covers its configuration, rendering, and limitations.', [link('Working field examples', table('Showcase/Field Types')), go('A project and its linked work')]) }, { name: 'Wiki/Article', previousDescription: "**Wiki articles** — design notes, architecture write-ups and postmortems.\n\n`Topic` groups them. Unlike Handbook guides these are allowed to be provisional: an article can argue a position, record a decision that was later reversed, or capture an investigation that went nowhere. That history is the point.", description: "Architecture, design decisions, and investigations. Articles preserve the reasoning and its history.", bannerPath: banners.wiki, html: wiki }, { name: 'Development/Release', bannerPath: banners.development, html: panel('What changed, and how to try it.', 'Open a release for its changes and linked work. A merged pull request alone does not confirm a production deployment.', [go('Reading a release', 'Follow a change from evidence to example.'), ...publicWork.slice(0, 2)]) }, { name: 'Showcase/Projects', bannerPath: banners.showcase, html: panel('A project holds the context.', 'Follow its tasks, then open a related note in the Notes table.', [go('A project and its linked work'), link('Tasks', table('Showcase/Tasks')), link('Notes', table('Showcase/Notes'))]) }, ], releases: [{ name: 'v0.4.128', bannerPath: banners.development, html: panel('Workflows you can inspect. Schemas you can copy.', 'This release records changes to workflow health, schema round-trips, and document loading. Read the original notes below for fixes, limits, and test evidence.', [go('Automations', 'Read how rules, actions, and health work.'), go('Document formatting', 'Try the editor with a maintained guide.'), go('Reading a release', 'Understand the evidence behind a release.')]) }], guides: [ { name: 'Quickstart', bannerPath: banners.handbook, html: panel('Start with one example.', 'Open the guided project, then choose whether to build a workspace yourself or connect an agent.', [go('A project and its linked work'), go('Connect an agent'), ...templateLinks]) }, { name: 'A project and its linked work', bannerPath: banners.showcase, html: panel('One project, several views of the work.', 'Follow a project to its tasks and notes. A separate expense example shows how a rollup totals linked line items.', [link('Apollo Launch', projectUrl), ...examples.slice(0, 3)]) }, { name: 'Connect an agent', bannerPath: banners.handbook, html: panel('Give your agent a workspace.', 'Use your own workspace’s MCP address. Sign-in and permissions are handled by weave.', [link('Connection instructions', '/docs'), go('Agent access (MCP)', 'Tool reference and access roles.')], setup) }, { name: 'Reading a release', bannerPath: banners.development, html: panel('Read a change in context.', 'Start with what changed for a user, then follow its issue, tests, and working example.', publicWork) }, { name: 'Templates', bannerPath: banners.showcase, html: panel('Start with a structure you can change.', 'Use template copies the space’s schema into another workspace. Records and attachments stay in the source.', templateLinks) }, ], }; }