Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ builder can render the same component from stored JSON.
| Block | Purpose | Key props |
|---|---|---|
| `PageHero` | inner-page intro | eyebrow, title, lede, note, actions[{label, href, variant?, chat?}], back, mascot{mood, accessory} \| false, `aside` slot |
| `HomeHero` | homepage promise + live chat card | eyebrow, title, lede, primary, secondary, note, chat{channel, label, lines} |
| `HomeHero` | homepage promise + live chat card | eyebrow, title, lede, primary, secondary, microcopy (one line under the buttons, read by everyone), note, chat{channel, label (screen readers), caption (visible "illustrative" note), lines} |
| `MultiplayerWorkflow` | one piece of work as a swimlane: people, agents and the shared task board, joined by numbered handoffs | id, eyebrow, title, lede, label (must say the story is illustrative), kinds{human, agent, board}, boardLabel, steps[{kind, actor, role, text, status?{label, tone}, tasks?[{title, owner, status}]}], outcome{title, body}, band |
| `SectionHead` | section heading | eyebrow, title, lede, note, align, level, size (xl/l/m) |
| `ProofStrip` | ruler of verified numbers | items[{value, label}], label |
| `HomeworkChecklist` | sheet of ticks under a drum edge, sealed "10" | eyebrow, title, lede, items[{text, detail?}], grade, gradeLabel, signature |
Expand Down
64 changes: 64 additions & 0 deletions docs/marketing-facts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Marketing facts and the release copy refresh

Every number the site quotes about dewee has one owner and one way to re-check it. This file
records both, so a stale figure is a mistake someone can find in minutes rather than a belief
nobody remembers verifying. The shared values live in `src/content/facts.ts`; pages import
`FACTS` instead of typing the number.

"dewee repo" below means `nextlevelbuilder/dewee`, checked out next to this repository. `<tag>` is
the latest stable tag (`gh release list --repo nextlevelbuilder/dewee --exclude-pre-releases`).

## Figures

| Figure | Value | Shown on | Source and command | Last verified |
|---|---|---|---|---|
| Latest stable release | v3.40.0 (2026-10-15) | Homepage proof, About report card, llms.txt, JSON-LD `softwareVersion` | `FACTS.stableRelease`; `gh release list --repo nextlevelbuilder/dewee --exclude-pre-releases --limit 3` | 2026-10-19 |
| Chat channels | 11 | Homepage, Integrations, Features catalogue, llms.txt | `FACTS.channels` = `CHANNELS.length` in `src/content/integrations.ts`; compare with the adapters in dewee `internal/channels/` (Zalo has three variants) | 2026-10-19 |
| Model providers | 22 | Homepage, Integrations, Features catalogue, llms.txt | `FACTS.providers` = `PROVIDERS.length`; compare with dewee `internal/providers` and `docs/02-providers.md` (Atlas Cloud added in v3.39.0) | 2026-10-19 |
| Go test functions | 13,351, shown as "13,000+" | Homepage proof, About report card | `FACTS.goTests`; in the dewee repo: `git grep -h -E '^func Test[A-Z_]' v<tag> -- '*_test.go' \| wc -l` | 2026-10-19 (v3.40.0) |
| Go lines under `internal/` | 921,264, shown as "900,000+" | Homepage proof | `FACTS.goLines`; in the dewee repo: `git grep -c '' v<tag> -- 'internal/*.go' \| awk -F: '{s+=$NF} END{print s}'` (tests included) | 2026-10-19 (v3.40.0) |
| Test cadence | Full suite runs on one preflighted commit before every release | Homepage proof, About report card | dewee commit `e511ada` (#1351, 2026-10-14): pull requests and `dev` pushes no longer run tests. Never write "on every change" or "on every pull request" | 2026-10-19 |
| Stable release history | v3.28 to v3.40, v3.38.1 has no notes | Features releases, Roadmap "Recently shipped" | `RELEASES` in `src/content/features.ts`; `gh release view v<x> --repo nextlevelbuilder/dewee` for each stable tag | 2026-10-19 |
| Roadmap columns | Now, Next, Later | Roadmap | `src/content/pages/roadmap.ts` header lists the dewee plan or beta release behind every item; no dates are promised | 2026-10-19 |
| Public MCP path | `/mcp/server` (legacy `/mcp` deprecated) | Integrations, Features | dewee `docs/18-http-api.md` at `v<tag>` | 2026-10-19 |
| Five layers of defence, AES-256-GCM, seven denied shell categories | as written | Security | dewee `docs/09-security.md`, `internal/crypto/aes.go` | 2026-09-25 |
| 8-stage agent loop, three memory tiers | as written | Architecture | dewee `docs/01-agent-loop.md`, `docs/07-bootstrap-skills-memory.md` | 2026-09-25 |
| Prices ($500 a year licence, On-Premises from $5K) | as written | Pricing, Install | `src/content/plans.ts`, `src/content/early-access.ts` (the owners' decision, not derived) | 2026-09-25 |
| NextLevelBuilder proof (130K+ stars, 115 contributors, 10,000+ businesses, 65K+ members) | as written | About | `plans/reports/scout-260924-2346-ecosystem-story.md`, GitHub counts read 2026-09-24 | 2026-09-24, not re-read in the last refresh |

Rows dated before 2026-10-19 were not re-checked in the last refresh. Treat them as due.

## Wording rules the figures depend on

- Say "before every release", not "on every change", for tests.
- Round tests and lines down ("13,000+", "900,000+"). Vietnamese uses a dot: "13.000+".
- Say "stable" for releases. Betas (`v3.41.0-beta.x`) never appear in feature lists; the Roadmap may
name a beta as "in beta" with its tag.
- Multiplayer means coordinated work by humans and agents: delegation, a shared task board,
approvals, traces. Do not write "first" or "only", live co-editing, shared cursors, real-time
sync, absolute safety, or invented customers and results.

## Release copy refresh

Run this when a new stable release ships, and again before any campaign that quotes the numbers.

1. Read the notes: `gh release view v<tag> --repo nextlevelbuilder/dewee`. Skip betas.
2. Update `src/content/facts.ts`: `stableRelease`, then re-count with the commands in the table
(tests and lines at the new tag) and update `goTests` and `goLines`. If the channel or provider
lists changed, update `CHANNELS` or `PROVIDERS` in `src/content/integrations.ts` so the counts follow.
3. Add a `RELEASES` entry at the top of `src/content/features.ts`: benefits first, only what the notes
support, EN and VI. Set `since` on a catalogue item only for a release listed in `RELEASES`, and
update the header comment with the verified date.
4. Update the Roadmap: move shipped items out of Now, add a "Recently shipped" line, and refresh the
"checked" note. Check the in-progress items against the dewee plans and the latest betas.
5. Nothing to do for the Features calendar: its month caption and accessible label are formatted
from `RELEASES[0].date` in `src/pages/[...lang]/features.astro`.
6. Search for stale figures. This should print nothing outside tests and code comments about history:

```bash
grep -rnE "8,900|8\.900|on every change|cho mỗi thay đổi|\b10 (chat )?channels|10 kênh" src
grep -rnE "v3\.[0-9]+(\.[0-9]+)?" src/content --include=*.ts | grep -v "features.ts\|releases.ts\|roadmap.ts\|facts.ts"
```

7. Update the "Last verified" column above for every figure you re-checked, then run
`pnpm check && pnpm test && pnpm build`.
38 changes: 27 additions & 11 deletions src/components/blocks/HomeHero.astro
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
---
/**
* Homepage hero: the promise on the left, a group chat on the right where dewee is already
* at work. The mascot drops in and peeks over the chat card; chat lines arrive one by one
* (CSS only) and are fully visible without JS or with reduced motion.
* Homepage hero: the promise on the left, a group chat on the right where people and agents are
* already working together. The mascot drops in and peeks over the chat card; chat lines arrive
* one by one (CSS only) and are fully visible without JS or with reduced motion.
* Both calls to action carry `data-hero-cta` so the analytics layer can name hero clicks
* (see PosthogAnalytics.astro and GoogleTagManager.astro); no tracking code lives here.
*/
import { escapeHtml, inlineMarkup } from "~/lib/inline-markup";
import DeweeMascot from "~/components/mascot/DeweeMascot.astro";
Expand All @@ -15,10 +17,13 @@ interface Props {
lede: string;
primary: { label: string; href: string };
secondary?: { label: string; href: string };
/** One short line under the buttons, read by everyone (unlike the handwritten `note`). */
microcopy?: string;
note?: string;
chat: { channel: string; label: string; lines: Line[] };
/** `label` names the chat for screen readers; `caption` is the visible note that it is simulated. */
chat: { channel: string; label: string; caption?: string; lines: Line[] };
}
const { eyebrow, title, lede, primary, secondary, note, chat } = Astro.props;
const { eyebrow, title, lede, primary, secondary, microcopy, note, chat } = Astro.props;
// Each sentence of the promise gets its own line so the break never lands mid-thought, and
// its last two words are glued together so no sentence ends on a lonely word ("is / yours.").
const titleLines = title.split(/(?<=[.!?])\s+/).map((l) => l.replace(/\s+(\S+)$/, "\u00a0$1"));
Expand All @@ -39,9 +44,10 @@ const richText = (text: string) =>
<h1 class="hero__title" data-reveal>{titleLines.map((l) => <span class="hero__line" set:html={inlineMarkup(l)} />)}</h1>
<p class="lede hero__lede" data-reveal>{lede}</p>
<div class="hero__actions" data-reveal>
<a class="btn btn--primary btn--lg" href={primary.href}>{primary.label}<Icon name="arrow-right" class="arrow" /></a>
{secondary && <a class="btn btn--ghost btn--lg" href={secondary.href}>{secondary.label}</a>}
<a class="btn btn--primary btn--lg" href={primary.href} data-hero-cta="primary">{primary.label}<Icon name="arrow-right" class="arrow" /></a>
{secondary && <a class="btn btn--ghost btn--lg" href={secondary.href} data-hero-cta="secondary">{secondary.label}</a>}
</div>
{microcopy && <p class="hero__micro" data-reveal><Icon name="users" size={16} />{microcopy}</p>}
</div>

<div class="hero__demo">
Expand All @@ -66,6 +72,7 @@ const richText = (text: string) =>
</li>
))}
</ol>
{chat.caption && <p class="chat-card__caption"><Icon name="info" size={14} />{chat.caption}</p>}
</figure>
{note && (
<p class="hero__note" aria-hidden="true">
Expand All @@ -82,10 +89,13 @@ const richText = (text: string) =>
.hero__bg { position: absolute; inset: 0; z-index: -1; --drum-x: 76%; --drum-y: 46%; }
.hero__grid { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: clamp(2.5rem, 1rem + 5vw, 6rem); align-items: center; }
.hero__copy { display: grid; gap: var(--space-5); justify-items: start; }
.hero__title { max-inline-size: 13ch; }
.hero__line { display: block; text-wrap: pretty; }
/* Balanced lines keep a one-sentence promise from stranding a short word ("got") mid-title. */
.hero__title { max-inline-size: 18ch; }
.hero__line { display: block; text-wrap: balance; }
.hero__lede { max-inline-size: 36rem; }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-block-start: var(--space-2); }
.hero__micro { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--step--1); color: var(--ink-3); }
.hero__micro :global(svg) { flex: none; color: var(--accent-ink); }

.hero__demo { position: relative; padding-block-start: clamp(3.5rem, 2rem + 4vw, 5.5rem); }
.hero__mascot { position: absolute; inset-block-start: 0; inset-inline-end: 12%; z-index: 0; }
Expand Down Expand Up @@ -126,6 +136,12 @@ const richText = (text: string) =>
.line__text :global(.mention) { color: var(--accent-ink); font-weight: 600; background: var(--accent-soft); border-radius: 4px; padding-inline: 0.2em; }
.line--me .line__text :global(.mention) { background: var(--surface); }
.line__text :global(.tick) { color: var(--success); font-weight: 700; }
.chat-card__caption {
display: flex; align-items: center; gap: var(--space-2);
padding: var(--space-2) var(--space-4); border-block-start: 1px solid var(--rule);
background: var(--paper-2); font-size: var(--step--2); color: var(--ink-3);
}
.chat-card__caption :global(svg) { flex: none; color: var(--accent-ink); }

.hero__note { position: absolute; inset-inline-start: -2.5rem; inset-block-end: -3.25rem; display: flex; align-items: flex-end; gap: var(--space-1); transform: rotate(-4deg); }
.hero__note .note { font-size: var(--step-0); }
Expand All @@ -144,14 +160,14 @@ const richText = (text: string) =>
@media (max-width: 960px) {
.hero__grid { grid-template-columns: minmax(0, 1fr); }
.hero__bg { --drum-x: 50%; --drum-y: 72%; }
.hero__title { max-inline-size: 16ch; }
.hero__title { max-inline-size: 18ch; }
.hero__demo { max-inline-size: 34rem; inline-size: 100%; justify-self: center; }
.hero__note { inset-inline-start: auto; inset-inline-end: 0; inset-block-end: -3rem; }
}
@media (max-width: 560px) {
.hero__actions { inline-size: 100%; }
.hero__actions .btn { flex: 1 1 100%; }
.chat-card__list { padding-inline: var(--space-3); }
.chat-card__list, .chat-card__caption { padding-inline: var(--space-3); }
.hero__note { position: static; justify-content: flex-end; margin-block-start: var(--space-3); }
.hero__arrow { display: none; }
}
Expand Down
Loading
Loading