From e8115c409eb926e4e18a6a783c9ecbd0e7fbb538 Mon Sep 17 00:00:00 2001 From: ubugeeei Date: Tue, 19 May 2026 15:33:15 +0900 Subject: [PATCH 1/6] brand: introduce visual identity for bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds brand/ with logo, design tokens, icon set, brand guide, and documentation template. Concept centers on "a node, a source, a single bit" — the pink dot is the shared minimum unit between the logomark (triad of nodes) and the wordmark (i-tittle). - Palette: 5-token strict (Bit Pink #FF2D6F, off-black ink, cream paper, stone, acid). No gradients, no shadows. - Typography: Space Grotesk + Space Mono. No italic. - Logo: mark / wordmark / horizontal + stack lockups, plus inverse and monochrome variants. Geometric primitives only. - Icons: node / source / distributed / communication in the same alphabet as the mark. - preview.html shows the system end-to-end; docs/docs-template.html applies it to a documentation page. Co-Authored-By: Claude Opus 4.7 (1M context) --- brand/README.md | 214 ++++++++++++ brand/docs/docs-template.html | 387 +++++++++++++++++++++ brand/icons/communication.svg | 9 + brand/icons/distributed.svg | 17 + brand/icons/node.svg | 6 + brand/icons/source.svg | 15 + brand/logo/lockup-horizontal.svg | 28 ++ brand/logo/lockup-stack.svg | 28 ++ brand/logo/mark-inverse.svg | 12 + brand/logo/mark-mono.svg | 12 + brand/logo/mark.svg | 16 + brand/logo/wordmark-inverse.svg | 13 + brand/logo/wordmark.svg | 16 + brand/preview.html | 571 +++++++++++++++++++++++++++++++ brand/tokens/tokens.css | 150 ++++++++ brand/tokens/tokens.json | 100 ++++++ 16 files changed, 1594 insertions(+) create mode 100644 brand/README.md create mode 100644 brand/docs/docs-template.html create mode 100644 brand/icons/communication.svg create mode 100644 brand/icons/distributed.svg create mode 100644 brand/icons/node.svg create mode 100644 brand/icons/source.svg create mode 100644 brand/logo/lockup-horizontal.svg create mode 100644 brand/logo/lockup-stack.svg create mode 100644 brand/logo/mark-inverse.svg create mode 100644 brand/logo/mark-mono.svg create mode 100644 brand/logo/mark.svg create mode 100644 brand/logo/wordmark-inverse.svg create mode 100644 brand/logo/wordmark.svg create mode 100644 brand/preview.html create mode 100644 brand/tokens/tokens.css create mode 100644 brand/tokens/tokens.json diff --git a/brand/README.md b/brand/README.md new file mode 100644 index 00000000..185644b6 --- /dev/null +++ b/brand/README.md @@ -0,0 +1,214 @@ +# bit — Brand + +A visual identity for **bit**, a distributed Git implementation in MoonBit. + +The brand exists to make three ideas legible at a glance: + +> **a node, a source, a single bit of communication.** + +--- + +## Concept + +bit is distributed. Source is replicated. Peers communicate. The brand makes +this physical — a triad of geometric primitives, a pink node that always +represents *source*, an off-cream paper, an ink that is not quite black, and a +deliberately uncomfortable acid green held in reserve. + +Three rules that never bend: + +1. **No gradients.** Color is asserted, never blended. +2. **No shadows.** Depth comes from layout, not light. +3. **Geometric primitives only.** Circles, rectangles, lines. The wordmark is + constructed from the same primitives as the logomark — they are literally + the same alphabet. + +--- + +## Logo + +| Asset | File | Use | +| --- | --- | --- | +| Mark | [`logo/mark.svg`](logo/mark.svg) | App icon, avatar, square placements | +| Mark, inverse | [`logo/mark-inverse.svg`](logo/mark-inverse.svg) | Dark surfaces | +| Mark, mono | [`logo/mark-mono.svg`](logo/mark-mono.svg) | Single-channel printing, embossing | +| Wordmark | [`logo/wordmark.svg`](logo/wordmark.svg) | In-line product name | +| Wordmark, inverse | [`logo/wordmark-inverse.svg`](logo/wordmark-inverse.svg) | Dark surfaces | +| Lockup, horizontal | [`logo/lockup-horizontal.svg`](logo/lockup-horizontal.svg) | Header, README hero | +| Lockup, stacked | [`logo/lockup-stack.svg`](logo/lockup-stack.svg) | Square hero, cover art | + +**Mark composition** — three nodes form an asymmetric triad: + +- **pink filled** — *source*. The origin of truth. +- **ink outlined** — *peer*. A replica that has not converged. +- **ink filled** — *peer*. A replica that has. + +Edges are thin. The triangle is intentionally non-equilateral. Symmetry is the +enemy of *distributed*. + +**Wordmark construction** — `b`, `i`, `t` are rectangles and circles. The +tittle of the **i** is the pink dot. This is the brand's signature: a single +node, a single source, a single bit. Use it as a reductive brand mark on its +own where the full lockup would be too loud. + +### Clear space + +Minimum margin around any logo asset is **one source-node diameter** on every +side. The mark must never be cropped by a frame closer than this. When in +doubt, give it more room. + +### Minimum size + +- Mark: **24×24 px** on screen, **8 mm** in print. +- Wordmark: **80 px** wide on screen. +- Lockup: **160 px** wide on screen. + +### Misuse + +Do not stretch. Do not recolor (pink stays pink, ink stays ink). Do not add +strokes to the source node. Do not add a drop shadow. Do not place over a +photograph without a paper or ink rectangle behind it. Do not rotate beyond +the supplied compositions. + +--- + +## Color + +``` +Bit Pink #FF2D6F Primary. Source, accent, the single bit. +Bit Ink #0E0E12 Foreground. Off-black with a cool tint. +Bit Paper #F2EFE7 Background. Warm cream. +Bit Stone #D9D5CB UI lines, dividers, secondary tracks. +Bit Acid #D6FF3D Eccentric counterpoint. Use SPARINGLY. +``` + +Press / hover states: + +``` +Pink Deep #C7124F Pressed pink only. Never as a primary surface. +Pink Tint #FFD9E6 Chip/tag fill on paper. +``` + +**Rules.** Pink is a single state — owned by *source*, primary actions, and +the i-tittle. The acid green is permitted on one element per view and never +inside the logo. Ink is not `#000` and paper is not `#FFF` — the warmth in +paper and the coolness in ink are deliberate. + +Full token sources: [`tokens/tokens.css`](tokens/tokens.css) · +[`tokens/tokens.json`](tokens/tokens.json) (W3C draft format). + +--- + +## Typography + +| Role | Family | Loaded from | +| --- | --- | --- | +| Sans (display + body) | **Space Grotesk** — 300 / 400 / 500 / 600 / 700 | Google Fonts | +| Mono (machine voice, code) | **Space Mono** — 400 / 700 | Google Fonts | + +**No italic. Anywhere.** Italic is forbidden in the brand — not in display, +not in body, not in code. Emphasis is carried by weight, color, and tracking. + +### Scale + +| Token | px | Use | +| --- | --: | --- | +| `--type-001` | 11 | micro label, system meta | +| `--type-00` | 13 | caption | +| `--type-0` | 15 | small body | +| `--type-1` | 17 | body | +| `--type-2` | 21 | lead paragraph | +| `--type-3` | 28 | h4 | +| `--type-4` | 40 | h3 | +| `--type-5` | 60 | h2 | +| `--type-6` | 96 | h1 | +| `--type-7` | 148 | hero, poster | + +### Tracking + +- Display (≥ 60 px): **−0.035em** — tight, near-touching. +- Headings (28–60 px): **−0.018em**. +- Body: **0**. +- Labels (UPPERCASE, ≤ 13 px): **+0.16em**. + +### Use it + +- **Display headlines** in Space Grotesk **700** at `--type-5` or larger, tight + tracking, leading `0.95`–`1.08`. Let one phrase fill a column. Generous + whitespace below. +- **Body** in Space Grotesk **400** at `--type-1`, leading `1.55`. +- **Labels & meta** in Space Mono **400** UPPERCASE, `--type-001`, + `tracking +0.16em` — used for `// COMMIT`, `NODE.0142`, kicker text, and + data captions. This is where the eccentric mode-design voice lives. +- **Code** in Space Mono **400**, no italic. Comments in `--bit-ink-mute`. + +Never pair these with Inter, Roboto, Helvetica, or system defaults. If a +target environment can only render system fonts, fall back to the platform +sans — never substitute a "near miss" web font. + +--- + +## Iconography + +Concept icons are 64×64, drawn with the same primitives as the logo. They are +not UI glyphs — they sit at section heads, in tables of contents, and on +covers. + +| Icon | File | Concept | +| --- | --- | --- | +| ![node](icons/node.svg) | [`icons/node.svg`](icons/node.svg) | A single addressable unit | +| ![source](icons/source.svg) | [`icons/source.svg`](icons/source.svg) | Origin broadcasting to peers | +| ![distributed](icons/distributed.svg) | [`icons/distributed.svg`](icons/distributed.svg) | A constellation, no single owner | +| ![communication](icons/communication.svg) | [`icons/communication.svg`](icons/communication.svg) | An edge — one bit of exchange | + +Drawing rules: 2 px stroke, square endpoints on rectangles, round caps on +lines, **one** pink element per icon, no gradient, no shadow, no perspective. + +--- + +## Layout principles + +- **Asymmetry over symmetry.** Compositions should feel like the triad mark — + weighted, intentional, never centered for the sake of it. +- **Rules, not boxes.** Use a 1 px ink rule to divide content. Cards with + rounded corners are forbidden; use rectangular ink-stroked panels with + `radius: 0` and stroke `1.5px`. +- **Mono labels as kicker text.** A small Space Mono UPPERCASE label sits + above every editorial heading, e.g. `// 003 — COMMUNICATION`. +- **One pink per view.** The primary action, or the source node, or the + i-tittle in the wordmark — pick one anchor, not three. +- **Acid is a guest.** One acid element per page maximum. Often zero. + +--- + +## Files + +``` +brand/ +├── README.md ← you are here +├── tokens/ +│ ├── tokens.css ← CSS custom properties + @import for fonts +│ └── tokens.json ← W3C-draft design tokens +├── logo/ +│ ├── mark.svg +│ ├── mark-inverse.svg +│ ├── mark-mono.svg +│ ├── wordmark.svg +│ ├── wordmark-inverse.svg +│ ├── lockup-horizontal.svg +│ └── lockup-stack.svg +├── icons/ +│ ├── node.svg +│ ├── source.svg +│ ├── distributed.svg +│ └── communication.svg +├── preview.html ← visual showcase of the system +└── docs/ + └── docs-template.html ← documentation page applying the brand +``` + +--- + +**License.** Brand assets are released under the project's Apache-2.0 license. +The MoonBit name and pink palette are referenced as cultural lineage, not +ownership. diff --git a/brand/docs/docs-template.html b/brand/docs/docs-template.html new file mode 100644 index 00000000..fd822346 --- /dev/null +++ b/brand/docs/docs-template.html @@ -0,0 +1,387 @@ + + + + + +bit — docs · brand applied + + + + + + +
+
+ bit + docs · v0.1 +
+ +
+ + +
+ + + + + +
+ +
+

// guide · 002 · concepts

+

A node, a source, a single bit.

+

bit is a distributed Git implementation. Every clone is a peer. Every commit is a bit of evidence. There is no central authority — only a graph of nodes converging through gossip.

+
+ +

Nodes

+

A node in bit is any process holding a copy of the repository. Your laptop, a CI runner, a relay host — all peers, no master. Each node exposes an address and a set of refs, and accepts handshakes from other peers it trusts.

+ +

Nodes are addressed by URI:

+ +
// peer addresses
+bit://aurora.local:9418/repo
+relay://bit.example.com/u/mizchi/bit
+https://github.com/mizchi/bit.git
+ +
+

// note

+

The pink dot in the bit logo represents the source node — the origin of a particular truth. In a distributed system, every node can be a source. The brand asserts this visually: only one node in the triad is pink at a time, but which one can rotate.

+
+ +

Source

+

Source is whoever wrote the commit you are looking at. bit records the source identity in the commit metadata and reconciles concurrent writes through a conflict-free history graph — not through a server vote.

+ +

Replication

+

When two peers handshake, they exchange ref tips and walk the object graph backwards until they meet a common ancestor. Bytes flow in both directions. This is gossip, not consensus.

+ + + + + + + + + + + +
ModeTriggerDirection
pushexplicitlocal → remote
fetchexplicitremote → local
gossipperiodic, on handshakeboth
relayvia relay hostboth, async
+ +

Communication

+

A single edge between two nodes is the smallest unit of meaning in the system — one bit of exchange. Communication in bit is content-addressed: peers ask for object hashes, not for filenames, so the protocol is naturally idempotent and deduplicating.

+ +
// initiate a peer session
+let peer = Node::from_uri("bit://aurora.local:9418/repo")
+peer.handshake(timeout=2.s)?
+let wants = local.refs().diff(peer.refs())
+peer.fetch(wants)
+ +
+

// caveat

+

bit is experimental. Data corruption can occur in worst-case scenarios. Always keep backups of important repositories. The acid green of this callout exists for one reason: you should not miss it.

+
+ +

What's next

+
    +
  • Read the gossip protocol reference.
  • +
  • Try bit clone against a relay host.
  • +
  • Open a local PR with bit pr create.
  • +
+ +
+ + + + +
+ +
+ bit · docs + edited 2026-05-19 · apache-2.0 +
+ + + diff --git a/brand/icons/communication.svg b/brand/icons/communication.svg new file mode 100644 index 00000000..b0787767 --- /dev/null +++ b/brand/icons/communication.svg @@ -0,0 +1,9 @@ + + communication + An edge between two nodes. The smallest unit of exchange — a single bit. + + + + + + diff --git a/brand/icons/distributed.svg b/brand/icons/distributed.svg new file mode 100644 index 00000000..c6c37602 --- /dev/null +++ b/brand/icons/distributed.svg @@ -0,0 +1,17 @@ + + distributed + A constellation of peers, no single owner. Asymmetric by design. + + + + + + + + + + + + + + diff --git a/brand/icons/node.svg b/brand/icons/node.svg new file mode 100644 index 00000000..907e2f67 --- /dev/null +++ b/brand/icons/node.svg @@ -0,0 +1,6 @@ + + node + A single addressable unit. The atom of the system. + + + diff --git a/brand/icons/source.svg b/brand/icons/source.svg new file mode 100644 index 00000000..f612229b --- /dev/null +++ b/brand/icons/source.svg @@ -0,0 +1,15 @@ + + source + An origin node broadcasting outward. The root of truth, replicated by peers. + + + + + + + + + + + + diff --git a/brand/logo/lockup-horizontal.svg b/brand/logo/lockup-horizontal.svg new file mode 100644 index 00000000..c25c21b7 --- /dev/null +++ b/brand/logo/lockup-horizontal.svg @@ -0,0 +1,28 @@ + + bit horizontal lockup + Mark and wordmark side by side. Optical alignment: source node baseline aligned to wordmark cap height. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/brand/logo/lockup-stack.svg b/brand/logo/lockup-stack.svg new file mode 100644 index 00000000..db7d8398 --- /dev/null +++ b/brand/logo/lockup-stack.svg @@ -0,0 +1,28 @@ + + bit stacked lockup + Mark above wordmark, both centered. For square placements: avatars, app icons, cover composition. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/brand/logo/mark-inverse.svg b/brand/logo/mark-inverse.svg new file mode 100644 index 00000000..9c3df6a2 --- /dev/null +++ b/brand/logo/mark-inverse.svg @@ -0,0 +1,12 @@ + + bit logomark, inverse + Logomark for dark surfaces. Paper-colored peers, pink source unchanged. + + + + + + + + + diff --git a/brand/logo/mark-mono.svg b/brand/logo/mark-mono.svg new file mode 100644 index 00000000..58da1996 --- /dev/null +++ b/brand/logo/mark-mono.svg @@ -0,0 +1,12 @@ + + bit logomark, monochrome + Single-color variant for embossing, single-channel printing, and reductive contexts. + + + + + + + + + diff --git a/brand/logo/mark.svg b/brand/logo/mark.svg new file mode 100644 index 00000000..49eaf453 --- /dev/null +++ b/brand/logo/mark.svg @@ -0,0 +1,16 @@ + + bit logomark + A triad of nodes — one pink source, two ink peers — connected by edges. Distributed communication. + + + + + + + + + + + + + diff --git a/brand/logo/wordmark-inverse.svg b/brand/logo/wordmark-inverse.svg new file mode 100644 index 00000000..a7bba21b --- /dev/null +++ b/brand/logo/wordmark-inverse.svg @@ -0,0 +1,13 @@ + + bit wordmark, inverse + Paper-colored letterforms for dark surfaces. Pink tittle unchanged. + + + + + + + + + + diff --git a/brand/logo/wordmark.svg b/brand/logo/wordmark.svg new file mode 100644 index 00000000..36208fcf --- /dev/null +++ b/brand/logo/wordmark.svg @@ -0,0 +1,16 @@ + + bit wordmark + "bit" constructed from geometric primitives: rectangles and circles. The pink dot above the i is the brand signature — a single node, a single source, a single bit. + + + + + + + + + + + + + diff --git a/brand/preview.html b/brand/preview.html new file mode 100644 index 00000000..9c5a3053 --- /dev/null +++ b/brand/preview.html @@ -0,0 +1,571 @@ + + + + + +bit — brand system + + + + + + +
+
+ bit / brand + v0.1 — 2026 +
+ +

// distributed source control · MoonBit

+ +

A node.
A source.
A single bt.

+ +

bit is a distributed Git implementation. The brand is its physical form — geometric primitives, asserted color, the pink dot as the smallest unit of meaning.

+ +
+ bit lockup +
+
+ + +
+
+
// 001
+

Color is asserted, never blended.

+
+ +
+ +
+
+
+

Bit Pink

+
+ #FF2D6F
+ rgb 255 45 111
+ primary · source +
+
+
+ +
+
+
+

Bit Ink

+
+ #0E0E12
+ rgb 14 14 18
+ foreground · rule +
+
+
+ +
+
+
+

Bit Paper

+
+ #F2EFE7
+ rgb 242 239 231
+ background · cream +
+
+
+ +
+
+
+

Bit Acid

+
+ #D6FF3D
+ rgb 214 255 61
+ counterpoint · rare +
+
+
+ +
+
+ + +
+
+
// 002
+

Type carries the voice. Italic is forbidden.

+
+ +
+ +
+
Hero
148 / 700
+

distributed.

+
+ +
+
H1
96 / 700
+

peer to peer.

+
+ +
+
H2
60 / 700
+

A graph of nodes.

+
+ +
+
H3
40 / 700
+

Communication is the smallest unit.

+
+ +
+
Body
17 / 400
+

bit stores history as content-addressed objects, replicated across peers without a central authority. Every node is a source. Every commit is a bit of evidence. The graph converges through gossip, not consensus.

+
+ +
+
Label
11 / 500 / mono
+

// node.0142 — commit a8c2f6 — peer wrote 14 bytes

+
+ +
+
+ + +
+
+
// 003
+

Logo. Three nodes, one asymmetric triangle.

+
+ +
+
+
+

lockup · horizontal · on paper

+
+
+
+

lockup · inverse · on ink

+
+
+
+

lockup · on acid (rare)

+
+ +
+
+

mark · on paper

+
+
+
+

mark · inverse

+
+
+
+

wordmark · the pink dot is the i

+
+
+
+ + +
+
+
// 004
+

Concept icons. Same alphabet as the mark.

+
+ +
+
node
+
source
+
distributed
+
communication
+
+
+ + +
+
+
// 005
+

Components. Rectangles. No corners. No shadow.

+
+ +
+ +
+

// node.0142 — origin

+

aurora.local

+
refs14
+
last gossip2s ago
+
heada8c2f6
+
+ + +
+
+ +
+

// pr.0007 — open

+

Replace gossip backoff with token bucket

+
+ pink · primary + ghost · meta + acid · 1 per view +
+ +
// peer announcement
+let peer = Node::from_uri(uri)
+peer.handshake(timeout=2.s)?
+peer.send(Bit::source(commit))
+
+ +
+
+ + +
+
+

// 006 — manifesto

+

no center.
no master.
only peers.

+
+
+
system bit / 2026
+
kernel moonbit
+
license apache-2.0
+
+
+
+ +
+
+ +
+ bit · brand · v0.1 + — constructed from rectangles, circles, and ink — +
+ + + diff --git a/brand/tokens/tokens.css b/brand/tokens/tokens.css new file mode 100644 index 00000000..92b6e5b8 --- /dev/null +++ b/brand/tokens/tokens.css @@ -0,0 +1,150 @@ +/* + * bit — Design Tokens (CSS) + * Surface-level tokens for color, typography, spacing, motion, and shape. + * No gradients. No shadows. Strict palette. Geometric primitives only. + */ + +@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Space+Mono:wght@400;700&display=swap"); + +:root { + /* ────────────────────────────────────────────────────────────── + * COLOR — strict 5-token palette. Never blend, never gradient. + * ────────────────────────────────────────────────────────────── */ + + /* Primary — Bit Pink. The node, the source, the single bit. */ + --bit-pink: #FF2D6F; + --bit-pink-deep: #C7124F; /* for press/hover states only */ + --bit-pink-tint: #FFD9E6; /* for filled tag/chip backgrounds */ + + /* Ink — off-black with cool tint. Editorial. Never #000. */ + --bit-ink: #0E0E12; + --bit-ink-soft: #2B2B33; /* secondary text */ + --bit-ink-mute: #6B6B74; /* meta / captions */ + + /* Paper — warm cream. Never #FFF. */ + --bit-paper: #F2EFE7; + --bit-paper-deep: #E8E4D8; /* alt surface, panels */ + + /* Stone — desaturated neutral. UI lines, dividers, tracks. */ + --bit-stone: #D9D5CB; + + /* Acid — the eccentric counterpoint. Use SPARINGLY. */ + --bit-acid: #D6FF3D; + + /* ────────────────────────────────────────────────────────────── + * SEMANTIC ALIASES + * ────────────────────────────────────────────────────────────── */ + + --bg: var(--bit-paper); + --bg-alt: var(--bit-paper-deep); + --fg: var(--bit-ink); + --fg-soft: var(--bit-ink-soft); + --fg-mute: var(--bit-ink-mute); + --rule: var(--bit-ink); /* rules are ink, full strength */ + --rule-soft: var(--bit-stone); + --accent: var(--bit-pink); + --accent-press: var(--bit-pink-deep); + --highlight: var(--bit-acid); + + /* ────────────────────────────────────────────────────────────── + * TYPOGRAPHY + * Sans → Space Grotesk (display + body) + * Mono → Space Mono (machine voice, captions, code) + * No italic. Ever. + * ────────────────────────────────────────────────────────────── */ + + --font-sans: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; + --font-mono: "Space Mono", ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, monospace; + + /* Type scale — modular, tight at display, loose at body */ + --type-001: 11px; /* micro label */ + --type-00: 13px; /* caption, meta */ + --type-0: 15px; /* small body */ + --type-1: 17px; /* body */ + --type-2: 21px; /* lead */ + --type-3: 28px; /* h4 */ + --type-4: 40px; /* h3 */ + --type-5: 60px; /* h2 */ + --type-6: 96px; /* h1 */ + --type-7: 148px; /* hero, poster */ + + /* Line heights — display tight, body comfortable */ + --leading-tight: 0.95; + --leading-snug: 1.08; + --leading-body: 1.55; + --leading-loose: 1.72; + + /* Tracking — display tightens, micro labels open up */ + --track-display: -0.035em; + --track-heading: -0.018em; + --track-body: 0; + --track-label: 0.16em; /* UPPERCASE labels only */ + --track-mono: 0; + + /* ────────────────────────────────────────────────────────────── + * SHAPE — geometric primitives, no soft rounding. + * ────────────────────────────────────────────────────────────── */ + + --radius-none: 0; + --radius-dot: 999px; /* circles only — never used on rectangles */ + --stroke-hair: 1px; + --stroke-thin: 1.5px; + --stroke-med: 2px; + --stroke-thick: 4px; + + /* ────────────────────────────────────────────────────────────── + * SPACE — 4px base, prefer larger jumps for editorial whitespace. + * ────────────────────────────────────────────────────────────── */ + + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 24px; + --space-6: 32px; + --space-7: 48px; + --space-8: 72px; + --space-9: 112px; + --space-10: 168px; + + /* ────────────────────────────────────────────────────────────── + * MOTION — instant or slow. No "smooth-default". + * ────────────────────────────────────────────────────────────── */ + + --ease-snap: cubic-bezier(0.2, 0.8, 0.2, 1); + --ease-flat: linear; + --dur-fast: 120ms; + --dur-slow: 480ms; + + color-scheme: light; +} + +/* ────────────────────────────────────────────────────────────── + * DARK MODE — invert paper/ink, keep pink and acid identical. + * ────────────────────────────────────────────────────────────── */ +@media (prefers-color-scheme: dark) { + :root { + --bg: var(--bit-ink); + --bg-alt: #17171C; + --fg: var(--bit-paper); + --fg-soft: #C9C5BB; + --fg-mute: #8A867D; + --rule: var(--bit-paper); + --rule-soft: #3A3A42; + + color-scheme: dark; + } +} + +/* Forced dark utility (e.g. previews) */ +[data-theme="dark"] { + --bg: var(--bit-ink); + --bg-alt: #17171C; + --fg: var(--bit-paper); + --fg-soft: #C9C5BB; + --fg-mute: #8A867D; + --rule: var(--bit-paper); + --rule-soft: #3A3A42; + + color-scheme: dark; +} diff --git a/brand/tokens/tokens.json b/brand/tokens/tokens.json new file mode 100644 index 00000000..25de7294 --- /dev/null +++ b/brand/tokens/tokens.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://design-tokens.github.io/community-group/format/", + "$description": "bit brand design tokens. Strict palette, no gradients, no shadows.", + "color": { + "bit": { + "pink": { "$value": "#FF2D6F", "$type": "color", "$description": "Primary. The node, the source, the single bit." }, + "pink-deep": { "$value": "#C7124F", "$type": "color", "$description": "Press/hover only." }, + "pink-tint": { "$value": "#FFD9E6", "$type": "color", "$description": "Filled chip/tag background." }, + "ink": { "$value": "#0E0E12", "$type": "color", "$description": "Foreground. Never #000." }, + "ink-soft": { "$value": "#2B2B33", "$type": "color" }, + "ink-mute": { "$value": "#6B6B74", "$type": "color" }, + "paper": { "$value": "#F2EFE7", "$type": "color", "$description": "Background. Never #FFF." }, + "paper-deep": { "$value": "#E8E4D8", "$type": "color" }, + "stone": { "$value": "#D9D5CB", "$type": "color", "$description": "UI lines, dividers, tracks." }, + "acid": { "$value": "#D6FF3D", "$type": "color", "$description": "Eccentric counterpoint. Use sparingly." } + }, + "semantic": { + "bg": { "$value": "{color.bit.paper}", "$type": "color" }, + "bg-alt": { "$value": "{color.bit.paper-deep}", "$type": "color" }, + "fg": { "$value": "{color.bit.ink}", "$type": "color" }, + "fg-soft": { "$value": "{color.bit.ink-soft}", "$type": "color" }, + "fg-mute": { "$value": "{color.bit.ink-mute}", "$type": "color" }, + "rule": { "$value": "{color.bit.ink}", "$type": "color" }, + "rule-soft": { "$value": "{color.bit.stone}", "$type": "color" }, + "accent": { "$value": "{color.bit.pink}", "$type": "color" }, + "highlight": { "$value": "{color.bit.acid}", "$type": "color" } + } + }, + "font": { + "family": { + "sans": { "$value": "Space Grotesk, ui-sans-serif, system-ui, sans-serif", "$type": "fontFamily" }, + "mono": { "$value": "Space Mono, ui-monospace, JetBrains Mono, monospace", "$type": "fontFamily" } + }, + "weight": { + "light": { "$value": 300, "$type": "fontWeight" }, + "regular": { "$value": 400, "$type": "fontWeight" }, + "medium": { "$value": 500, "$type": "fontWeight" }, + "semi": { "$value": 600, "$type": "fontWeight" }, + "bold": { "$value": 700, "$type": "fontWeight" } + }, + "size": { + "001": { "$value": "11px", "$type": "dimension", "$description": "micro label" }, + "00": { "$value": "13px", "$type": "dimension", "$description": "caption" }, + "0": { "$value": "15px", "$type": "dimension" }, + "1": { "$value": "17px", "$type": "dimension", "$description": "body" }, + "2": { "$value": "21px", "$type": "dimension", "$description": "lead" }, + "3": { "$value": "28px", "$type": "dimension" }, + "4": { "$value": "40px", "$type": "dimension" }, + "5": { "$value": "60px", "$type": "dimension" }, + "6": { "$value": "96px", "$type": "dimension" }, + "7": { "$value": "148px","$type": "dimension", "$description": "hero / poster" } + }, + "tracking": { + "display": { "$value": "-0.035em", "$type": "string" }, + "heading": { "$value": "-0.018em", "$type": "string" }, + "body": { "$value": "0", "$type": "string" }, + "label": { "$value": "0.16em", "$type": "string", "$description": "UPPERCASE only" } + }, + "leading": { + "tight": { "$value": 0.95, "$type": "number" }, + "snug": { "$value": 1.08, "$type": "number" }, + "body": { "$value": 1.55, "$type": "number" }, + "loose": { "$value": 1.72, "$type": "number" } + } + }, + "space": { + "1": { "$value": "4px", "$type": "dimension" }, + "2": { "$value": "8px", "$type": "dimension" }, + "3": { "$value": "12px", "$type": "dimension" }, + "4": { "$value": "16px", "$type": "dimension" }, + "5": { "$value": "24px", "$type": "dimension" }, + "6": { "$value": "32px", "$type": "dimension" }, + "7": { "$value": "48px", "$type": "dimension" }, + "8": { "$value": "72px", "$type": "dimension" }, + "9": { "$value": "112px", "$type": "dimension" }, + "10": { "$value": "168px", "$type": "dimension" } + }, + "shape": { + "radius": { + "none": { "$value": "0", "$type": "dimension" }, + "dot": { "$value": "999px", "$type": "dimension", "$description": "Circle only. Never on rectangles." } + }, + "stroke": { + "hair": { "$value": "1px", "$type": "dimension" }, + "thin": { "$value": "1.5px", "$type": "dimension" }, + "med": { "$value": "2px", "$type": "dimension" }, + "thick": { "$value": "4px", "$type": "dimension" } + } + }, + "motion": { + "ease": { + "snap": { "$value": "cubic-bezier(0.2, 0.8, 0.2, 1)", "$type": "cubicBezier" }, + "flat": { "$value": "linear", "$type": "string" } + }, + "duration": { + "fast": { "$value": "120ms", "$type": "duration" }, + "slow": { "$value": "480ms", "$type": "duration" } + } + } +} From 252b5d5d7f11cea017cf3991bb1d2f3199b5c8f1 Mon Sep 17 00:00:00 2001 From: ubugeeei Date: Tue, 19 May 2026 15:47:22 +0900 Subject: [PATCH 2/6] update --- brand/README.md | 28 ++-- brand/icons/node.svg | 6 - brand/logo/lockup-horizontal.svg | 4 +- brand/logo/lockup-stack.svg | 4 +- .../{mark-inverse.svg => logo-inverse.svg} | 4 +- brand/logo/{mark-mono.svg => logo-mono.svg} | 4 +- brand/logo/{mark.svg => logo.svg} | 4 +- brand/preview.html | 11 +- site/assets/img/communication.svg | 9 ++ site/assets/img/distributed.svg | 17 ++ site/assets/img/lockup-horizontal.svg | 28 ++++ site/assets/img/lockup-stack.svg | 28 ++++ site/assets/img/logo-inverse.svg | 12 ++ site/assets/img/logo-mono.svg | 12 ++ site/assets/img/logo.svg | 16 ++ site/assets/img/source.svg | 15 ++ site/assets/img/wordmark-inverse.svg | 13 ++ site/assets/img/wordmark.svg | 16 ++ site/assets/tokens.css | 150 ++++++++++++++++++ 19 files changed, 344 insertions(+), 37 deletions(-) delete mode 100644 brand/icons/node.svg rename brand/logo/{mark-inverse.svg => logo-inverse.svg} (86%) rename brand/logo/{mark-mono.svg => logo-mono.svg} (86%) rename brand/logo/{mark.svg => logo.svg} (91%) create mode 100644 site/assets/img/communication.svg create mode 100644 site/assets/img/distributed.svg create mode 100644 site/assets/img/lockup-horizontal.svg create mode 100644 site/assets/img/lockup-stack.svg create mode 100644 site/assets/img/logo-inverse.svg create mode 100644 site/assets/img/logo-mono.svg create mode 100644 site/assets/img/logo.svg create mode 100644 site/assets/img/source.svg create mode 100644 site/assets/img/wordmark-inverse.svg create mode 100644 site/assets/img/wordmark.svg create mode 100644 site/assets/tokens.css diff --git a/brand/README.md b/brand/README.md index 185644b6..b27270b6 100644 --- a/brand/README.md +++ b/brand/README.md @@ -20,7 +20,7 @@ Three rules that never bend: 1. **No gradients.** Color is asserted, never blended. 2. **No shadows.** Depth comes from layout, not light. 3. **Geometric primitives only.** Circles, rectangles, lines. The wordmark is - constructed from the same primitives as the logomark — they are literally + constructed from the same primitives as the logo — they are literally the same alphabet. --- @@ -29,15 +29,15 @@ Three rules that never bend: | Asset | File | Use | | --- | --- | --- | -| Mark | [`logo/mark.svg`](logo/mark.svg) | App icon, avatar, square placements | -| Mark, inverse | [`logo/mark-inverse.svg`](logo/mark-inverse.svg) | Dark surfaces | -| Mark, mono | [`logo/mark-mono.svg`](logo/mark-mono.svg) | Single-channel printing, embossing | +| Logo | [`logo/logo.svg`](logo/logo.svg) | App icon, avatar, square placements | +| Logo, inverse | [`logo/logo-inverse.svg`](logo/logo-inverse.svg) | Dark surfaces | +| Logo, mono | [`logo/logo-mono.svg`](logo/logo-mono.svg) | Single-channel printing, embossing | | Wordmark | [`logo/wordmark.svg`](logo/wordmark.svg) | In-line product name | | Wordmark, inverse | [`logo/wordmark-inverse.svg`](logo/wordmark-inverse.svg) | Dark surfaces | | Lockup, horizontal | [`logo/lockup-horizontal.svg`](logo/lockup-horizontal.svg) | Header, README hero | | Lockup, stacked | [`logo/lockup-stack.svg`](logo/lockup-stack.svg) | Square hero, cover art | -**Mark composition** — three nodes form an asymmetric triad: +**Logo composition** — three nodes form an asymmetric triad: - **pink filled** — *source*. The origin of truth. - **ink outlined** — *peer*. A replica that has not converged. @@ -48,18 +48,18 @@ enemy of *distributed*. **Wordmark construction** — `b`, `i`, `t` are rectangles and circles. The tittle of the **i** is the pink dot. This is the brand's signature: a single -node, a single source, a single bit. Use it as a reductive brand mark on its -own where the full lockup would be too loud. +node, a single source, a single bit. Use it as a reductive brand element on +its own where the full lockup would be too loud. ### Clear space Minimum margin around any logo asset is **one source-node diameter** on every -side. The mark must never be cropped by a frame closer than this. When in +side. The logo must never be cropped by a frame closer than this. When in doubt, give it more room. ### Minimum size -- Mark: **24×24 px** on screen, **8 mm** in print. +- Logo: **24×24 px** on screen, **8 mm** in print. - Wordmark: **80 px** wide on screen. - Lockup: **160 px** wide on screen. @@ -156,7 +156,6 @@ covers. | Icon | File | Concept | | --- | --- | --- | -| ![node](icons/node.svg) | [`icons/node.svg`](icons/node.svg) | A single addressable unit | | ![source](icons/source.svg) | [`icons/source.svg`](icons/source.svg) | Origin broadcasting to peers | | ![distributed](icons/distributed.svg) | [`icons/distributed.svg`](icons/distributed.svg) | A constellation, no single owner | | ![communication](icons/communication.svg) | [`icons/communication.svg`](icons/communication.svg) | An edge — one bit of exchange | @@ -168,7 +167,7 @@ lines, **one** pink element per icon, no gradient, no shadow, no perspective. ## Layout principles -- **Asymmetry over symmetry.** Compositions should feel like the triad mark — +- **Asymmetry over symmetry.** Compositions should feel like the triad logo — weighted, intentional, never centered for the sake of it. - **Rules, not boxes.** Use a 1 px ink rule to divide content. Cards with rounded corners are forbidden; use rectangular ink-stroked panels with @@ -190,15 +189,14 @@ brand/ │ ├── tokens.css ← CSS custom properties + @import for fonts │ └── tokens.json ← W3C-draft design tokens ├── logo/ -│ ├── mark.svg -│ ├── mark-inverse.svg -│ ├── mark-mono.svg +│ ├── logo.svg +│ ├── logo-inverse.svg +│ ├── logo-mono.svg │ ├── wordmark.svg │ ├── wordmark-inverse.svg │ ├── lockup-horizontal.svg │ └── lockup-stack.svg ├── icons/ -│ ├── node.svg │ ├── source.svg │ ├── distributed.svg │ └── communication.svg diff --git a/brand/icons/node.svg b/brand/icons/node.svg deleted file mode 100644 index 907e2f67..00000000 --- a/brand/icons/node.svg +++ /dev/null @@ -1,6 +0,0 @@ - - node - A single addressable unit. The atom of the system. - - - diff --git a/brand/logo/lockup-horizontal.svg b/brand/logo/lockup-horizontal.svg index c25c21b7..c4828919 100644 --- a/brand/logo/lockup-horizontal.svg +++ b/brand/logo/lockup-horizontal.svg @@ -1,8 +1,8 @@ bit horizontal lockup - Mark and wordmark side by side. Optical alignment: source node baseline aligned to wordmark cap height. + Logo and wordmark side by side. Optical alignment: source node baseline aligned to wordmark cap height. - + diff --git a/brand/logo/lockup-stack.svg b/brand/logo/lockup-stack.svg index db7d8398..4127f116 100644 --- a/brand/logo/lockup-stack.svg +++ b/brand/logo/lockup-stack.svg @@ -1,8 +1,8 @@ bit stacked lockup - Mark above wordmark, both centered. For square placements: avatars, app icons, cover composition. + Logo above wordmark, both centered. For square placements: avatars, app icons, cover composition. - + diff --git a/brand/logo/mark-inverse.svg b/brand/logo/logo-inverse.svg similarity index 86% rename from brand/logo/mark-inverse.svg rename to brand/logo/logo-inverse.svg index 9c3df6a2..1205b9fe 100644 --- a/brand/logo/mark-inverse.svg +++ b/brand/logo/logo-inverse.svg @@ -1,5 +1,5 @@ - - bit logomark, inverse + + bit logo, inverse Logomark for dark surfaces. Paper-colored peers, pink source unchanged. diff --git a/brand/logo/mark-mono.svg b/brand/logo/logo-mono.svg similarity index 86% rename from brand/logo/mark-mono.svg rename to brand/logo/logo-mono.svg index 58da1996..d498733c 100644 --- a/brand/logo/mark-mono.svg +++ b/brand/logo/logo-mono.svg @@ -1,5 +1,5 @@ - - bit logomark, monochrome + + bit logo, monochrome Single-color variant for embossing, single-channel printing, and reductive contexts. diff --git a/brand/logo/mark.svg b/brand/logo/logo.svg similarity index 91% rename from brand/logo/mark.svg rename to brand/logo/logo.svg index 49eaf453..8f30f8c2 100644 --- a/brand/logo/mark.svg +++ b/brand/logo/logo.svg @@ -1,5 +1,5 @@ - - bit logomark + + bit logo A triad of nodes — one pink source, two ink peers — connected by edges. Distributed communication. diff --git a/brand/preview.html b/brand/preview.html index 9c5a3053..dd58c9af 100644 --- a/brand/preview.html +++ b/brand/preview.html @@ -477,11 +477,11 @@

Logo. Three nodes, one asymmetric triangle.

-
+

mark · on paper

-
+

mark · inverse

@@ -495,11 +495,10 @@

Logo. Three nodes, one asymmetric triangle.

// 004
-

Concept icons. Same alphabet as the mark.

+

Concept icons. Same alphabet as the logo.

-
-
node
+
source
distributed
communication
@@ -558,7 +557,7 @@

no center.
no master.
only pee

- +
diff --git a/site/assets/img/communication.svg b/site/assets/img/communication.svg new file mode 100644 index 00000000..b0787767 --- /dev/null +++ b/site/assets/img/communication.svg @@ -0,0 +1,9 @@ + + communication + An edge between two nodes. The smallest unit of exchange — a single bit. + + + + + + diff --git a/site/assets/img/distributed.svg b/site/assets/img/distributed.svg new file mode 100644 index 00000000..c6c37602 --- /dev/null +++ b/site/assets/img/distributed.svg @@ -0,0 +1,17 @@ + + distributed + A constellation of peers, no single owner. Asymmetric by design. + + + + + + + + + + + + + + diff --git a/site/assets/img/lockup-horizontal.svg b/site/assets/img/lockup-horizontal.svg new file mode 100644 index 00000000..c4828919 --- /dev/null +++ b/site/assets/img/lockup-horizontal.svg @@ -0,0 +1,28 @@ + + bit horizontal lockup + Logo and wordmark side by side. Optical alignment: source node baseline aligned to wordmark cap height. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/assets/img/lockup-stack.svg b/site/assets/img/lockup-stack.svg new file mode 100644 index 00000000..4127f116 --- /dev/null +++ b/site/assets/img/lockup-stack.svg @@ -0,0 +1,28 @@ + + bit stacked lockup + Logo above wordmark, both centered. For square placements: avatars, app icons, cover composition. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/assets/img/logo-inverse.svg b/site/assets/img/logo-inverse.svg new file mode 100644 index 00000000..1205b9fe --- /dev/null +++ b/site/assets/img/logo-inverse.svg @@ -0,0 +1,12 @@ + + bit logo, inverse + Logomark for dark surfaces. Paper-colored peers, pink source unchanged. + + + + + + + + + diff --git a/site/assets/img/logo-mono.svg b/site/assets/img/logo-mono.svg new file mode 100644 index 00000000..d498733c --- /dev/null +++ b/site/assets/img/logo-mono.svg @@ -0,0 +1,12 @@ + + bit logo, monochrome + Single-color variant for embossing, single-channel printing, and reductive contexts. + + + + + + + + + diff --git a/site/assets/img/logo.svg b/site/assets/img/logo.svg new file mode 100644 index 00000000..8f30f8c2 --- /dev/null +++ b/site/assets/img/logo.svg @@ -0,0 +1,16 @@ + + bit logo + A triad of nodes — one pink source, two ink peers — connected by edges. Distributed communication. + + + + + + + + + + + + + diff --git a/site/assets/img/source.svg b/site/assets/img/source.svg new file mode 100644 index 00000000..f612229b --- /dev/null +++ b/site/assets/img/source.svg @@ -0,0 +1,15 @@ + + source + An origin node broadcasting outward. The root of truth, replicated by peers. + + + + + + + + + + + + diff --git a/site/assets/img/wordmark-inverse.svg b/site/assets/img/wordmark-inverse.svg new file mode 100644 index 00000000..a7bba21b --- /dev/null +++ b/site/assets/img/wordmark-inverse.svg @@ -0,0 +1,13 @@ + + bit wordmark, inverse + Paper-colored letterforms for dark surfaces. Pink tittle unchanged. + + + + + + + + + + diff --git a/site/assets/img/wordmark.svg b/site/assets/img/wordmark.svg new file mode 100644 index 00000000..36208fcf --- /dev/null +++ b/site/assets/img/wordmark.svg @@ -0,0 +1,16 @@ + + bit wordmark + "bit" constructed from geometric primitives: rectangles and circles. The pink dot above the i is the brand signature — a single node, a single source, a single bit. + + + + + + + + + + + + + diff --git a/site/assets/tokens.css b/site/assets/tokens.css new file mode 100644 index 00000000..92b6e5b8 --- /dev/null +++ b/site/assets/tokens.css @@ -0,0 +1,150 @@ +/* + * bit — Design Tokens (CSS) + * Surface-level tokens for color, typography, spacing, motion, and shape. + * No gradients. No shadows. Strict palette. Geometric primitives only. + */ + +@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Space+Mono:wght@400;700&display=swap"); + +:root { + /* ────────────────────────────────────────────────────────────── + * COLOR — strict 5-token palette. Never blend, never gradient. + * ────────────────────────────────────────────────────────────── */ + + /* Primary — Bit Pink. The node, the source, the single bit. */ + --bit-pink: #FF2D6F; + --bit-pink-deep: #C7124F; /* for press/hover states only */ + --bit-pink-tint: #FFD9E6; /* for filled tag/chip backgrounds */ + + /* Ink — off-black with cool tint. Editorial. Never #000. */ + --bit-ink: #0E0E12; + --bit-ink-soft: #2B2B33; /* secondary text */ + --bit-ink-mute: #6B6B74; /* meta / captions */ + + /* Paper — warm cream. Never #FFF. */ + --bit-paper: #F2EFE7; + --bit-paper-deep: #E8E4D8; /* alt surface, panels */ + + /* Stone — desaturated neutral. UI lines, dividers, tracks. */ + --bit-stone: #D9D5CB; + + /* Acid — the eccentric counterpoint. Use SPARINGLY. */ + --bit-acid: #D6FF3D; + + /* ────────────────────────────────────────────────────────────── + * SEMANTIC ALIASES + * ────────────────────────────────────────────────────────────── */ + + --bg: var(--bit-paper); + --bg-alt: var(--bit-paper-deep); + --fg: var(--bit-ink); + --fg-soft: var(--bit-ink-soft); + --fg-mute: var(--bit-ink-mute); + --rule: var(--bit-ink); /* rules are ink, full strength */ + --rule-soft: var(--bit-stone); + --accent: var(--bit-pink); + --accent-press: var(--bit-pink-deep); + --highlight: var(--bit-acid); + + /* ────────────────────────────────────────────────────────────── + * TYPOGRAPHY + * Sans → Space Grotesk (display + body) + * Mono → Space Mono (machine voice, captions, code) + * No italic. Ever. + * ────────────────────────────────────────────────────────────── */ + + --font-sans: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; + --font-mono: "Space Mono", ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, monospace; + + /* Type scale — modular, tight at display, loose at body */ + --type-001: 11px; /* micro label */ + --type-00: 13px; /* caption, meta */ + --type-0: 15px; /* small body */ + --type-1: 17px; /* body */ + --type-2: 21px; /* lead */ + --type-3: 28px; /* h4 */ + --type-4: 40px; /* h3 */ + --type-5: 60px; /* h2 */ + --type-6: 96px; /* h1 */ + --type-7: 148px; /* hero, poster */ + + /* Line heights — display tight, body comfortable */ + --leading-tight: 0.95; + --leading-snug: 1.08; + --leading-body: 1.55; + --leading-loose: 1.72; + + /* Tracking — display tightens, micro labels open up */ + --track-display: -0.035em; + --track-heading: -0.018em; + --track-body: 0; + --track-label: 0.16em; /* UPPERCASE labels only */ + --track-mono: 0; + + /* ────────────────────────────────────────────────────────────── + * SHAPE — geometric primitives, no soft rounding. + * ────────────────────────────────────────────────────────────── */ + + --radius-none: 0; + --radius-dot: 999px; /* circles only — never used on rectangles */ + --stroke-hair: 1px; + --stroke-thin: 1.5px; + --stroke-med: 2px; + --stroke-thick: 4px; + + /* ────────────────────────────────────────────────────────────── + * SPACE — 4px base, prefer larger jumps for editorial whitespace. + * ────────────────────────────────────────────────────────────── */ + + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 24px; + --space-6: 32px; + --space-7: 48px; + --space-8: 72px; + --space-9: 112px; + --space-10: 168px; + + /* ────────────────────────────────────────────────────────────── + * MOTION — instant or slow. No "smooth-default". + * ────────────────────────────────────────────────────────────── */ + + --ease-snap: cubic-bezier(0.2, 0.8, 0.2, 1); + --ease-flat: linear; + --dur-fast: 120ms; + --dur-slow: 480ms; + + color-scheme: light; +} + +/* ────────────────────────────────────────────────────────────── + * DARK MODE — invert paper/ink, keep pink and acid identical. + * ────────────────────────────────────────────────────────────── */ +@media (prefers-color-scheme: dark) { + :root { + --bg: var(--bit-ink); + --bg-alt: #17171C; + --fg: var(--bit-paper); + --fg-soft: #C9C5BB; + --fg-mute: #8A867D; + --rule: var(--bit-paper); + --rule-soft: #3A3A42; + + color-scheme: dark; + } +} + +/* Forced dark utility (e.g. previews) */ +[data-theme="dark"] { + --bg: var(--bit-ink); + --bg-alt: #17171C; + --fg: var(--bit-paper); + --fg-soft: #C9C5BB; + --fg-mute: #8A867D; + --rule: var(--bit-paper); + --rule-soft: #3A3A42; + + color-scheme: dark; +} From c42ecc1e97a31493c7baa7f6358391570ebf4dfa Mon Sep 17 00:00:00 2001 From: ubugeeei Date: Tue, 19 May 2026 15:49:14 +0900 Subject: [PATCH 3/6] update --- brand/README.md | 21 --------------------- brand/icons/communication.svg | 9 --------- brand/icons/distributed.svg | 17 ----------------- brand/icons/source.svg | 15 --------------- brand/preview.html | 14 -------------- site/.nojekyll | 0 site/assets/img/communication.svg | 9 --------- site/assets/img/distributed.svg | 17 ----------------- site/assets/img/source.svg | 15 --------------- 9 files changed, 117 deletions(-) delete mode 100644 brand/icons/communication.svg delete mode 100644 brand/icons/distributed.svg delete mode 100644 brand/icons/source.svg create mode 100644 site/.nojekyll delete mode 100644 site/assets/img/communication.svg delete mode 100644 site/assets/img/distributed.svg delete mode 100644 site/assets/img/source.svg diff --git a/brand/README.md b/brand/README.md index b27270b6..9496560d 100644 --- a/brand/README.md +++ b/brand/README.md @@ -148,23 +148,6 @@ sans — never substitute a "near miss" web font. --- -## Iconography - -Concept icons are 64×64, drawn with the same primitives as the logo. They are -not UI glyphs — they sit at section heads, in tables of contents, and on -covers. - -| Icon | File | Concept | -| --- | --- | --- | -| ![source](icons/source.svg) | [`icons/source.svg`](icons/source.svg) | Origin broadcasting to peers | -| ![distributed](icons/distributed.svg) | [`icons/distributed.svg`](icons/distributed.svg) | A constellation, no single owner | -| ![communication](icons/communication.svg) | [`icons/communication.svg`](icons/communication.svg) | An edge — one bit of exchange | - -Drawing rules: 2 px stroke, square endpoints on rectangles, round caps on -lines, **one** pink element per icon, no gradient, no shadow, no perspective. - ---- - ## Layout principles - **Asymmetry over symmetry.** Compositions should feel like the triad logo — @@ -196,10 +179,6 @@ brand/ │ ├── wordmark-inverse.svg │ ├── lockup-horizontal.svg │ └── lockup-stack.svg -├── icons/ -│ ├── source.svg -│ ├── distributed.svg -│ └── communication.svg ├── preview.html ← visual showcase of the system └── docs/ └── docs-template.html ← documentation page applying the brand diff --git a/brand/icons/communication.svg b/brand/icons/communication.svg deleted file mode 100644 index b0787767..00000000 --- a/brand/icons/communication.svg +++ /dev/null @@ -1,9 +0,0 @@ - - communication - An edge between two nodes. The smallest unit of exchange — a single bit. - - - - - - diff --git a/brand/icons/distributed.svg b/brand/icons/distributed.svg deleted file mode 100644 index c6c37602..00000000 --- a/brand/icons/distributed.svg +++ /dev/null @@ -1,17 +0,0 @@ - - distributed - A constellation of peers, no single owner. Asymmetric by design. - - - - - - - - - - - - - - diff --git a/brand/icons/source.svg b/brand/icons/source.svg deleted file mode 100644 index f612229b..00000000 --- a/brand/icons/source.svg +++ /dev/null @@ -1,15 +0,0 @@ - - source - An origin node broadcasting outward. The root of truth, replicated by peers. - - - - - - - - - - - - diff --git a/brand/preview.html b/brand/preview.html index dd58c9af..c6894ff4 100644 --- a/brand/preview.html +++ b/brand/preview.html @@ -495,20 +495,6 @@

Logo. Three nodes, one asymmetric triangle.

// 004
-

Concept icons. Same alphabet as the logo.

-
- -
-
source
-
distributed
-
communication
-
-
- - -
-
-
// 005

Components. Rectangles. No corners. No shadow.

diff --git a/site/.nojekyll b/site/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/site/assets/img/communication.svg b/site/assets/img/communication.svg deleted file mode 100644 index b0787767..00000000 --- a/site/assets/img/communication.svg +++ /dev/null @@ -1,9 +0,0 @@ - - communication - An edge between two nodes. The smallest unit of exchange — a single bit. - - - - - - diff --git a/site/assets/img/distributed.svg b/site/assets/img/distributed.svg deleted file mode 100644 index c6c37602..00000000 --- a/site/assets/img/distributed.svg +++ /dev/null @@ -1,17 +0,0 @@ - - distributed - A constellation of peers, no single owner. Asymmetric by design. - - - - - - - - - - - - - - diff --git a/site/assets/img/source.svg b/site/assets/img/source.svg deleted file mode 100644 index f612229b..00000000 --- a/site/assets/img/source.svg +++ /dev/null @@ -1,15 +0,0 @@ - - source - An origin node broadcasting outward. The root of truth, replicated by peers. - - - - - - - - - - - - From 48e6bc2ec89375a602bbef35e4127088b5e4af4c Mon Sep 17 00:00:00 2001 From: ubugeeei Date: Tue, 19 May 2026 15:58:00 +0900 Subject: [PATCH 4/6] site: GitHub Pages docs site (Learning Guide + Reference) Adds a static documentation site under site/ deployable to GitHub Pages. Applies the bit brand: Space Grotesk + Space Mono, pink-dot wordmark, asymmetric layouts, // numbered sections, no italic, no gradients. - Landing: hero with manifesto headline, three principles, quick-start shell + embedded MoonBit example, ink manifesto strip. - Learning Guide (learn/): TOC + chapters 01 Install, 02 First commit, 03 Going distributed. - Reference (reference/): TOC + R1 CLI commands (108 commands grouped porcelain/plumbing/hub/relay/extensions/ai), R2 Library API (Storage/Fs/Kv/Hub/Node), R3 Environment. - assets/syntax.js: vanilla MoonBit syntax highlighter (keywords, preamble, types, strings, comments, function-call detection) plus shell prompt highlighting; copy buttons on every code block. - assets/site.css: docs layout (topbar/sidebar/article/TOC), brand components, pager, callouts, mobile nav. - .github/workflows/pages.yml: deploys site/ on push to main or brand, re-syncing tokens.css and logo SVGs from brand/ before upload. - site/README.md documents local preview and how to add pages. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/pages.yml | 52 ++++ site/README.md | 88 ++++++ site/assets/site.css | 579 +++++++++++++++++++++++++++++++++++ site/assets/site.js | 23 ++ site/assets/syntax.js | 100 ++++++ site/index.html | 151 +++++++++ site/learn/distributed.html | 177 +++++++++++ site/learn/first-commit.html | 155 ++++++++++ site/learn/index.html | 97 ++++++ site/learn/install.html | 147 +++++++++ site/reference/cli.html | 201 ++++++++++++ site/reference/env.html | 115 +++++++ site/reference/index.html | 79 +++++ site/reference/library.html | 251 +++++++++++++++ 14 files changed, 2215 insertions(+) create mode 100644 .github/workflows/pages.yml create mode 100644 site/README.md create mode 100644 site/assets/site.css create mode 100644 site/assets/site.js create mode 100644 site/assets/syntax.js create mode 100644 site/index.html create mode 100644 site/learn/distributed.html create mode 100644 site/learn/first-commit.html create mode 100644 site/learn/index.html create mode 100644 site/learn/install.html create mode 100644 site/reference/cli.html create mode 100644 site/reference/env.html create mode 100644 site/reference/index.html create mode 100644 site/reference/library.html diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 00000000..e65b6295 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,52 @@ +name: Pages + +on: + push: + branches: [main, brand] + paths: + - "site/**" + - "brand/**" + - ".github/workflows/pages.yml" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Refresh brand assets in site + run: | + mkdir -p site/assets/img + cp brand/tokens/tokens.css site/assets/tokens.css + cp brand/logo/*.svg site/assets/img/ + touch site/.nojekyll + + - name: Configure Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./site + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/site/README.md b/site/README.md new file mode 100644 index 00000000..d829ea31 --- /dev/null +++ b/site/README.md @@ -0,0 +1,88 @@ +# bit — documentation site + +The static documentation site for **bit**. Deployed to GitHub Pages from this +folder. No build step: every page is hand-authored HTML referencing shared +CSS and a tiny vanilla JS syntax highlighter. + +## Layout + +``` +site/ +├── index.html ← landing page +├── learn/ ← Learning Guide +│ ├── index.html +│ ├── install.html +│ ├── first-commit.html +│ └── distributed.html +├── reference/ ← Reference +│ ├── index.html +│ ├── cli.html +│ ├── library.html +│ └── env.html +├── assets/ +│ ├── tokens.css ← brand design tokens (copied from /brand) +│ ├── site.css ← site-level layout + components +│ ├── syntax.js ← MoonBit / bash syntax highlighter +│ ├── site.js ← nav toggle, copy buttons +│ └── img/ ← logo, wordmark, lockups (copied from /brand) +└── .nojekyll ← tell GitHub Pages not to run Jekyll +``` + +## Preview locally + +Just open `site/index.html` in your browser. All paths are relative, so it +works straight off the filesystem. For a real HTTP server (recommended — the +clipboard API needs a secure context): + +```sh +python3 -m http.server -d site 8080 +# open http://localhost:8080 +``` + +## Deploy + +Pushed automatically by [`.github/workflows/pages.yml`](../.github/workflows/pages.yml) +on every push to `main` or `brand` that touches `site/` or `brand/`. + +The workflow re-syncs brand assets (tokens.css + logo SVGs) from `brand/` +into `site/assets/` before uploading, so the source of truth stays in +`brand/` and the site is always in lockstep. + +To enable: in the repo's **Settings → Pages**, set the source to +**GitHub Actions**. The workflow handles the rest. + +## Updating brand assets + +Don't edit `site/assets/tokens.css` or `site/assets/img/*.svg` directly — +edit the originals in [`brand/`](../brand/) and re-sync: + +```sh +cp brand/tokens/tokens.css site/assets/tokens.css +cp brand/logo/*.svg site/assets/img/ +``` + +The Pages workflow does this automatically on every deploy. + +## Adding pages + +1. Create the HTML file using one of the existing pages as a template + (`learn/install.html` is a good starting point for a guide chapter, + `reference/env.html` for a reference page). +2. Add the page to the relevant side navigation (`
diff --git a/site/learn/first-commit.html b/site/learn/first-commit.html index b29c2857..af3cbcfa 100644 --- a/site/learn/first-commit.html +++ b/site/learn/first-commit.html @@ -3,13 +3,12 @@ -Your first commit — Learning Guide +Your first commit -
bit @@ -17,7 +16,7 @@
@@ -28,17 +27,15 @@ @@ -49,104 +46,95 @@

Your first commit.

If you have ever used Git, you already know this. bit reuses the porcelain — init, add, commit, log all behave the way you expect. The first commit is a sanity check that the binary works.

-

Initialize a repository

-
-
// shell
-
$ mkdir hello-bit
+

Initialize a repository

+
// bash
$ mkdir hello-bit
 $ cd hello-bit
 $ bit init
-Initialized empty bit repository in .git/
-
-

bit writes a standard .git/ directory. Any existing Git tool can read it. There is no parallel .bit/ store.

- -

Write & stage

-
-
// shell
-
$ echo "hello, peer" > note.txt
+Initialized empty bit repository in .git/
+
+

bit writes a standard .git/ directory. Any existing Git tool can read it. There is no parallel .bit/ store.

+

Write and stage

+
// bash
$ echo "hello, peer" > note.txt
 $ bit add note.txt
 $ bit status
 On branch main
 
 Changes to be committed:
-        new file:   note.txt
-
- -
-

// note

-

The status output is intentionally identical to Git's. bit emits the same line shapes so your editor's plugins, your hooks, and your grep incantations keep working.

-
- -

Commit

-
-
// shell
-
$ bit commit -m "first node speaks"
+        new file:   note.txt
+
+
+

// note

+

The status output is intentionally identical to Git's. bit emits the same line shapes so your editor's plugins, your hooks, and your grep incantations keep working.

+
+

Commit

+
// bash
$ bit commit -m "first node speaks"
 [main (root-commit) a8c2f6e] first node speaks
  1 file changed, 1 insertion(+)
- create mode 100644 note.txt
-
- -

Read the log

-
-
// shell
-
$ bit log --graph --oneline
-* a8c2f6e (HEAD -> main) first node speaks
-
-

The graph renderer is native — --graph, --stat, --name-only, --name-status, and --topo-order all run inside bit without shelling out.

- -

What just happened

-

bit wrote three Git objects under .git/objects/:

-
    -
  • a blob for note.txt (the file contents)
  • -
  • a tree describing the directory snapshot
  • -
  • a commit pointing at the tree, with your author identity and message
  • -
-

These are the same object types every Git client speaks. Anything you commit with bit can be pushed to a Git server, and anything cloned from a Git server can be operated on by bit.

- -

Try it from MoonBit

-

The same flow, scripted against an in-memory filesystem — no shell involved. This is how agents drive bit.

-
-
// moonbit
-
let fs = @bit.TestFs::new()
-let root = "/hello-bit"
-
-run_storage_command(fs, fs, root, "init", ["-q"])
-fs.write_string(root + "/note.txt", "hello, peer")
-run_storage_command(fs, fs, root, "add", ["note.txt"])
-run_storage_command(fs, fs, root, "commit", ["-m", "first node speaks"])
-
-let head = run_storage_command(fs, fs, root, "rev-parse", ["HEAD"])
-println("commit: \{head}")
-
- - + create mode 100644 note.txt +
+

Read the log

+
// bash
$ bit log --graph --oneline
+* a8c2f6e (HEAD -> main) first node speaks
+
+

The graph renderer is native — --graph, --stat, --name-only, --name-status, and --topo-order all run inside bit without shelling out.

+

What just happened

+

bit wrote three Git objects under .git/objects/:

+
    +
  • +

    a blob for note.txt (the file contents)

    +
  • +
  • +

    a tree describing the directory snapshot

    +
  • +
  • +

    a commit pointing at the tree, with your author identity and message

    +
  • +
+

These are the same object types every Git client speaks. Anything you commit with bit can be pushed to a Git server, and anything cloned from a Git server can be operated on by bit.

+

Try it from MoonBit

+

The same flow, scripted against an in-memory filesystem — no shell involved. This is how agents drive bit.

+
// moonbit
let fs = @bit.TestFs::new()
+let root = "/hello-bit"
+
+run_storage_command(fs, fs, root, "init", ["-q"])
+fs.write_string(root + "/note.txt", "hello, peer")
+run_storage_command(fs, fs, root, "add", ["note.txt"])
+run_storage_command(fs, fs, root, "commit", ["-m", "first node speaks"])
+
+let head = run_storage_command(fs, fs, root, "rev-parse", ["HEAD"])
+println("commit: \{head}")
+
+ + + diff --git a/site/learn/index.html b/site/learn/index.html index 49194eb7..540451bd 100644 --- a/site/learn/index.html +++ b/site/learn/index.html @@ -3,14 +3,12 @@ -Learning Guide — bit - +Learning Guide -
bit @@ -18,7 +16,7 @@
@@ -27,17 +25,24 @@
bit / learn - est. 35 min — five chapters + est. 35 min — six chapters

// guide

-

Learning
Guide.

-

A guided path through bit. By the end you will have installed bit, made commits against a real repository, replicated history to a second peer, and opened a local pull request — all without leaving your terminal.

+

Learning Guide.

+

A guided path through bit. By the end you will understand the model, install the binary, make commits against a real repository, replicate history to a second peer, and open a local pull request — all without leaving your terminal.

+ + diff --git a/site/learn/install.html b/site/learn/install.html index bf060cb5..03deabf8 100644 --- a/site/learn/install.html +++ b/site/learn/install.html @@ -3,13 +3,12 @@ -Install bit — Learning Guide +Install bit -
bit @@ -17,7 +16,7 @@
@@ -28,17 +27,15 @@ @@ -46,99 +43,83 @@

// More

// guide · 01

Install bit.

-

Two install paths: a single curl line, or via the MoonBit toolchain if you already have one. Both produce a bit binary on PATH.

+

Two install paths — a single curl line, or via the MoonBit toolchain if you already have one. Both produce a bit binary on PATH.

-

Supported platforms

-
    -
  • Linux x86_64
  • -
  • macOS arm64 & x86_64
  • -
-

Windows is not supported yet. WSL works.

- -

One-line install

-
-
// shell
-
$ curl -fsSL https://raw.githubusercontent.com/mizchi/bit-vcs/main/install.sh | bash
-
-

The script detects your OS & architecture, downloads the matching release, and drops the binary at ~/.local/bin/bit. Add it to your shell's PATH if it isn't already.

- -
-

// note

-

Read the script before you pipe it to bash — that's the rule for every install script on the internet, not just this one. Mirror at github.com/mizchi/bit-vcs.

-
- -

Via the MoonBit toolchain

-

If you have moon installed:

-
-
// shell
-
$ moon install mizchi/bit/cmd/bit
-
-

This builds bit from source against your toolchain. Useful if you want to track main or contribute patches.

- -

Verify

-
-
// shell
-
$ bit --version
-bit 0.1.0 — distributed git in moonbit
-
- -

Shell completion

-

bit ships its own completion generator. Pick your shell and source the output from your rc file.

- -

bash

-
-
// ~/.bashrc
-
eval "$(bit completion bash)"
-
- -

zsh

-
-
// ~/.zshrc
-
eval "$(bit completion zsh)"
-
- -

Uninstall

-
-
// shell
-
$ rm ~/.local/bin/bit
-
-

That's it. bit has no daemons, no system services, no cache outside your repos.

+

Supported platforms

+
    +
  • +

    Linux x86_64

    +
  • +
  • +

    macOS arm64 & x86_64

    +
  • +
+

Windows is not supported yet. WSL works.

+

One-line install

+
// bash
$ curl -fsSL https://raw.githubusercontent.com/mizchi/bit-vcs/main/install.sh | bash
+
+

The script detects your OS and architecture, downloads the matching release, and drops the binary at ~/.local/bin/bit. Add it to your shell's PATH if it isn't already.

+
+

// note

+

Read the script before you pipe it to bash — that's the rule for every install script on the internet, not just this one. Mirror at github.com/mizchi/bit-vcs.

+
+

Via the MoonBit toolchain

+

If you have moon installed:

+
// bash
$ moon install mizchi/bit/cmd/bit
+
+

This builds bit from source against your toolchain. Useful if you want to track main or contribute patches.

+

Verify

+
// bash
$ bit --version
+bit 0.1.0 — distributed git in moonbit
+
+

Shell completion

+

bit ships its own completion generator. Pick your shell and source the output from your rc file.

+

bash

+
// bash
eval "$(bit completion bash)"
+
+

zsh

+
// bash
eval "$(bit completion zsh)"
+
+

Uninstall

+
// bash
$ rm ~/.local/bin/bit
+
+

That's it. bit has no daemons, no system services, no cache outside your repos.

+
+

// caveat

+

bit is experimental. Data corruption is possible in worst-case scenarios. Always keep a Git backup of important repositories until you have stress-tested your workflow.

+
-
-

// caveat

-

bit is experimental. Data corruption is possible in worst-case scenarios. Always keep a Git backup of important repositories until you've stress-tested your workflow.

-
- + diff --git a/site/reference/cli.html b/site/reference/cli.html index 29cabc00..b06619b3 100644 --- a/site/reference/cli.html +++ b/site/reference/cli.html @@ -3,13 +3,12 @@ -CLI commands — Reference +CLI commands -
bit @@ -18,7 +17,7 @@
@@ -28,18 +27,14 @@ @@ -50,130 +45,324 @@

CLI commands.

bit implements 108 Git commands natively. Anything not listed here falls back to Git's surface — read going distributed for the protocol-level differences.

-

Porcelain

-

Everyday commands. Identical surface to Git.

- - - - - - - - - - - - - - - - - - - - - - - - -
CommandDescription
bit initCreate an empty .git/ in the current directory.
bit clone <url>Clone over HTTP, relay, or a subdirectory URL (see extensions).
bit add <path>…Stage files. Supports -A, -p, --update.
bit commit -m <msg>Record staged changes. --amend, --no-edit, --allow-empty.
bit statusWorking tree state in Git's exact line format.
bit logNative graph renderer. --graph, --stat, --name-only, --topo-order.
bit diffWorktree vs index, or any two trees / commits.
bit checkoutSwitch branches, restore files. -b creates.
bit branchList, create, delete branches.
bit mergeThree-way merge with conflict markers.
bit rebaseNative -i with editor injection. See bit ai rebase.
bit cherry-pick <sha>Apply commits onto HEAD.
bit revert <sha>Reverse a commit.
bit fetchDownload refs & objects from a remote.
bit pullFetch and merge / rebase.
bit pushUpload refs & objects. HTTPS, relay, LFS upload.
bit remoteManage remotes.
bit tagLightweight and annotated tags.
bit stashShelve worktree state.
bit reset--soft, --mixed, --hard.
- -

Plumbing

-

Low-level operations on Git objects. Same surface as Git's plumbing.

- - - - - - - - - - - - - -
CommandDescription
bit hash-objectCompute the SHA-1 of a file (optionally write to objects).
bit cat-filePrint object type, size, or contents.
bit rev-parseResolve refs and revisions to SHAs.
bit ls-treeList tree contents.
bit ls-filesList files in the index.
bit update-refMove a ref atomically.
bit pack-objectsBuild a packfile.
bit unpack-objectsExplode a packfile into loose objects.
bit symbolic-refRead or write a symbolic ref like HEAD.
- -

Hub — PRs & issues

-

Local, server-less collaboration backed by Git objects. Sync via relay.

- - - - - - - - - - - - - - -
CommandDescription
bit pr initInitialize hub metadata in the current repo (writes .git/hub/policy.toml).
bit pr create--title, --body, --head, --base. Opens a local PR.
bit pr list--open, --closed, --all.
bit pr review <id>--approve, --request-changes, --commit <sha>.
bit pr merge <id>Merge into base. Honors policy.
bit pr search <q>Full-text over PR titles & bodies.
bit issue create--title, --body, --parent <id>.
bit issue list--open, --tree, --all, --parent.
bit issue link <issue> <pr>Cross-link issue ↔ PR.
bit issue search <q>Full-text over issues.
- -

Relay

-

Sync PR / issue metadata between peers via an HTTP relay.

- - - - - - - -
CommandDescription
bit relay sync push <url>Upload hub notes to a relay.
bit relay sync fetch <url>Download hub notes from a relay.
bit relay serveRun a relay host locally. Supports LFS Batch API.
- -

Extensions

- - - - - - - - - - - -
CommandDescription
bit subdir-clone <url> <path> <dst>Clone a subdirectory as an independent repo.
bit clone user/repo:pathShorthand for subdir-clone via the standard clone command.
bit hq get <user/repo>ghq-compatible repo manager. Default root: ~/bhq.
bit hq listList all repos under hq.
bit workspace flow <task>Workspace fingerprint-based task runner.
bit fingerprintWorkspace / PR fingerprint helpers.
bit completion <shell>Emit shell completion script. bash or zsh.
- -

AI assist

-

AI-assisted conflict resolution via OpenRouter. Default model: moonshotai/kimi-k2. Requires OPENROUTER_API_KEY.

- - - - - - - - - -
CommandDescription
bit ai rebase <base>Rebase with AI resolving conflicts. --continue, --abort, --skip.
bit ai merge <branch>Merge with AI assist.
bit ai commitAI commit message (--split for multi-commit suggestion).
bit ai cherry-pick <sha>Cherry-pick with AI conflict resolution.
bit ai revert <sha>Revert with AI.
- -

Common AI flags

-
    -
  • --model <id> — override the OpenRouter model.
  • -
  • --max-ai-rounds <n> — bound conflict-resolution turns.
  • -
  • --agent-loop — give the AI an agent loop, not single-shot.
  • -
  • --agent-max-steps <n> — bound the agent loop.
  • -
+

Porcelain

+

Everyday commands. Identical surface to Git.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescription
bit initCreate an empty .git/ in the current directory.
bit clone <url>Clone over HTTP, relay, or a subdirectory URL (see extensions).
bit add <path>…Stage files. Supports -A, -p, --update.
bit commit -m <msg>Record staged changes. --amend, --no-edit, --allow-empty.
bit statusWorking tree state in Git's exact line format.
bit logNative graph renderer. --graph, --stat, --name-only, --topo-order.
bit diffWorktree vs index, or any two trees / commits.
bit checkoutSwitch branches, restore files. -b creates.
bit branchList, create, delete branches.
bit mergeThree-way merge with conflict markers.
bit rebaseNative -i with editor injection.
bit cherry-pick <sha>Apply commits onto HEAD.
bit revert <sha>Reverse a commit.
bit fetchDownload refs and objects from a remote.
bit pullFetch and merge / rebase.
bit pushUpload refs and objects. HTTPS, relay, LFS upload.
bit remoteManage remotes.
bit tagLightweight and annotated tags.
bit stashShelve worktree state.
bit reset--soft, --mixed, --hard.
+

Plumbing

+

Low-level operations on Git objects. Same surface as Git's plumbing.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescription
bit hash-objectCompute the SHA-1 of a file (optionally write to objects).
bit cat-filePrint object type, size, or contents.
bit rev-parseResolve refs and revisions to SHAs.
bit ls-treeList tree contents.
bit ls-filesList files in the index.
bit update-refMove a ref atomically.
bit pack-objectsBuild a packfile.
bit unpack-objectsExplode a packfile into loose objects.
bit symbolic-refRead or write a symbolic ref like HEAD.
+

Hub — PRs and issues

+

Local, server-less collaboration backed by Git objects. Sync via relay.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescription
bit pr initInitialize hub metadata in the current repo (writes .git/hub/policy.toml).
bit pr create--title, --body, --head, --base. Opens a local PR.
bit pr list--open, --closed, --all.
bit pr review <id>--approve, --request-changes, --commit <sha>.
bit pr merge <id>Merge into base. Honors policy.
bit pr search <q>Full-text over PR titles and bodies.
bit issue create--title, --body, --parent <id>.
bit issue list--open, --tree, --all, --parent.
bit issue link <issue> <pr>Cross-link issue ↔ PR.
bit issue search <q>Full-text over issues.
+

Relay

+

Sync PR and issue metadata between peers via an HTTP relay.

+ + + + + + + + + + + + + + + + + + + + + +
CommandDescription
bit relay sync push <url>Upload hub notes to a relay.
bit relay sync fetch <url>Download hub notes from a relay.
bit relay serveRun a relay host locally. Supports LFS Batch API.
+

Extensions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescription
bit subdir-clone <url> <path> <dst>Clone a subdirectory as an independent repo.
bit clone user/repo:pathShorthand for subdir-clone via the standard clone command.
bit hq get <user/repo>ghq-compatible repo manager. Default root: ~/bhq.
bit hq listList all repos under hq.
bit workspace flow <task>Workspace fingerprint-based task runner.
bit fingerprintWorkspace / PR fingerprint helpers.
bit completion <shell>Emit shell completion script. bash or zsh.
+

AI assist

+

AI-assisted conflict resolution via OpenRouter. Default model: moonshotai/kimi-k2. Requires OPENROUTER_API_KEY.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandDescription
bit ai rebase <base>Rebase with AI resolving conflicts. --continue, --abort, --skip.
bit ai merge <branch>Merge with AI assist.
bit ai commitAI commit message (--split for multi-commit suggestion).
bit ai cherry-pick <sha>Cherry-pick with AI conflict resolution.
bit ai revert <sha>Revert with AI.
+

Common AI flags

+
    +
  • +

    --model <id> — override the OpenRouter model.

    +
  • +
  • +

    --max-ai-rounds <n> — bound conflict-resolution turns.

    +
  • +
  • +

    --agent-loop — give the AI an agent loop, not single-shot.

    +
  • +
  • +

    --agent-max-steps <n> — bound the agent loop.

    +
  • +
+
+

// see also

+

The Library API exposes the same surface to MoonBit code — drive any of the above from an agent without spawning a process.

+
-
-

// see also

-

Library API exposes the same surface to MoonBit code — drive any of the above from an agent without spawning a process.

-
- + diff --git a/site/reference/env.html b/site/reference/env.html index e1dbc5f3..613af91d 100644 --- a/site/reference/env.html +++ b/site/reference/env.html @@ -3,13 +3,12 @@ -Environment — Reference +Environment -
bit @@ -18,7 +17,7 @@
@@ -28,9 +27,14 @@ @@ -41,72 +45,130 @@

Environment variables.

Every BIT_* variable bit reads, plus the Git-compat variables it honors. Set them in your shell rc, in .envrc, or per-command.

-

bit-native

- - - - - - - - - -
VariableDefaultEffect
BIT_BENCH_GIT_DIROverride .git path for bench_real (vfs benchmarks).
BIT_PACK_CACHE_LIMIT2Max packfiles kept in memory. 0 disables the cache.
BIT_RACY_GITunsetWhen set, rehash even if stat matches — avoids racy-git false negatives on fast filesystems.
BIT_WORKSPACE_FINGERPRINT_MODEgitgit for add-all-style snapshots, fast for per-node directory hashes.
BIT_HUB_INIT_PROMPT1 (interactive)Force-on (1) or force-off (0) the bit pr init / bit issue init prompts.
- -

Git-compat

-

bit honors the standard Git environment so existing tooling, hooks, and editors keep working.

- - - - - - - - - - - -
VariableEffect
GIT_DIROverride the location of the repository's .git directory.
GIT_WORK_TREEOverride the working tree root.
GIT_CONFIG_GLOBALPath to global config (default ~/.gitconfig).
GIT_EDITOREditor used for commit messages, rebase conflict markers.
GIT_SEQUENCE_EDITOREditor used for the rebase todo list.
GIT_AUTHOR_NAME / GIT_AUTHOR_EMAILOverride commit author identity.
GIT_COMMITTER_NAME / GIT_COMMITTER_EMAILOverride committer identity.
- -

AI assist

- - - - - -
VariableEffect
OPENROUTER_API_KEYRequired for any bit ai * subcommand.
+

bit-native

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VariableDefaultEffect
BIT_BENCH_GIT_DIROverride .git path for bench_real (vfs benchmarks).
BIT_PACK_CACHE_LIMIT2Max packfiles kept in memory. 0 disables the cache.
BIT_RACY_GITunsetWhen set, rehash even if stat matches — avoids racy-git false negatives on fast filesystems.
BIT_WORKSPACE_FINGERPRINT_MODEgitgit for add-all-style snapshots, fast for per-node directory hashes.
BIT_HUB_INIT_PROMPT1 (interactive)Force-on (1) or force-off (0) the bit pr init / bit issue init prompts.
+

Git-compat

+

bit honors the standard Git environment so existing tooling, hooks, and editors keep working.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VariableEffect
GIT_DIROverride the location of the repository's .git directory.
GIT_WORK_TREEOverride the working tree root.
GIT_CONFIG_GLOBALPath to global config (default ~/.gitconfig).
GIT_EDITOREditor used for commit messages, rebase conflict markers.
GIT_SEQUENCE_EDITOREditor used for the rebase todo list.
GIT_AUTHOR_NAME / GIT_AUTHOR_EMAILOverride commit author identity.
GIT_COMMITTER_NAME / GIT_COMMITTER_EMAILOverride committer identity.
+

AI assist

+ + + + + + + + + + + + + +
VariableEffect
OPENROUTER_API_KEYRequired for any bit ai * subcommand.
+
+

// note

+

bit never reads secrets from the repository — keys live only in the environment. If you set OPENROUTER_API_KEY via .envrc, make sure that file is git-ignored.

+
-
-

// note

-

bit never reads secrets from the repository — keys live only in the environment. If you set OPENROUTER_API_KEY via .envrc, make sure that file is git-ignored.

-
- + diff --git a/site/reference/index.html b/site/reference/index.html index e3d74f28..0109cfe1 100644 --- a/site/reference/index.html +++ b/site/reference/index.html @@ -3,14 +3,12 @@ -Reference — bit - +Reference -
bit @@ -19,7 +17,7 @@
@@ -32,21 +30,19 @@

// reference

Reference.

-

Everything bit exposes — every command, every public type, every environment variable. Sorted, terse, link-anchored. Reach for this when you know what you're looking for.

+

Everything bit exposes — every command, every public type, every environment variable. Sorted, terse, link-anchored. Reach for this when you know what you are looking for.

-
+ + diff --git a/site/reference/library.html b/site/reference/library.html index a49cc001..9e58f98c 100644 --- a/site/reference/library.html +++ b/site/reference/library.html @@ -3,13 +3,12 @@ -Library API — Reference +Library API -
bit @@ -18,7 +17,7 @@
@@ -28,18 +27,14 @@ @@ -50,199 +45,269 @@

Library API.

bit is a MoonBit library first, a CLI second. Every CLI command is a thin shell around a public function. Embed bit directly in your agent, editor plugin, or CI runner — no subprocess required.

-

Package layout

-

Source is organized in five layers, dependencies flow downward only.

- - - - - - - - - -
LayerPathWhat lives here
coresrc/core/Object types, SHA, ref store primitives.
midsrc/mid/Index, pack, merge, diff algorithms.
highsrc/high/Porcelain operations (commit, rebase, …).
extsrc/ext/Hub, relay, LFS, AI, workspace.
cmdsrc/cmd/CLI entry points.
- -

Storage runtime

-

The entry point for embedding. Any storage that implements FileSystem + RepoFileSystem can host a repository.

- -

Traits

-
-
// moonbit
-
pub trait FileSystem {
-  write_string(Self, String, String) -> Unit
-  write_bytes(Self, String, Bytes) -> Unit
-  mkdir_p(Self, String) -> Unit
-  remove(Self, String) -> Unit
-  rename(Self, String, String) -> Unit
+

Package layout

+

Source is organized in five layers, dependencies flow downward only.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LayerPathWhat lives here
coresrc/core/Object types, SHA, ref store primitives.
midsrc/mid/Index, pack, merge, diff algorithms.
highsrc/high/Porcelain operations (commit, rebase, …).
extsrc/ext/Hub, relay, LFS, AI, workspace.
cmdsrc/cmd/CLI entry points.
+

Storage runtime

+

The entry point for embedding. Any storage that implements FileSystem and RepoFileSystem can host a repository.

+

Traits

+
// moonbit
pub trait FileSystem {
+  write_string(Self, String, String) -> Unit
+  write_bytes(Self, String, Bytes) -> Unit
+  mkdir_p(Self, String) -> Unit
+  remove(Self, String) -> Unit
+  rename(Self, String, String) -> Unit
 }
 
 pub trait RepoFileSystem {
-  read_string(Self, String) -> String?
-  read_bytes(Self, String) -> Bytes?
-  exists(Self, String) -> Bool
-  readdir(Self, String) -> Array[String]
-  stat(Self, String) -> FileStat?
-}
-
- -

Entry function

-
-
// moonbit
-
pub fn run_storage_command[F : FileSystem + RepoFileSystem](
+  read_string(Self, String) -> String?
+  read_bytes(Self, String) -> Bytes?
+  exists(Self, String) -> Bool
+  readdir(Self, String) -> Array[String]
+  stat(Self, String) -> FileStat?
+}
+
+

Entry function

+
// moonbit
pub fn run_storage_command[F : FileSystem + RepoFileSystem](
   fs   : F,
   rfs  : F,
   root : String,
   cmd  : String,
   args : Array[String]
-) -> String
-
- -

In-memory backend

-

TestFs is the reference implementation. Use it for agents, tests, and ephemeral repos.

-
-
// moonbit
-
let fs = @bit.TestFs::new()
-let root = "/agent-repo"
-
-run_storage_command(fs, fs, root, "init", ["-q"])
-fs.write_string(root + "/note.txt", "hello, peer")
-run_storage_command(fs, fs, root, "add", ["note.txt"])
-run_storage_command(fs, fs, root, "commit", ["-m", "agent snapshot"])
-
-let head = run_storage_command(fs, fs, root, "rev-parse", ["HEAD"])
-println("head: \{head}")
-
- -

Fs — virtual filesystem

-

Mount any commit as a read-only filesystem with lazy blob loading.

-
-
// moonbit
-
let mounted = @bit.Fs::from_commit(fs, ".git", commit_id)
-let entries = mounted.readdir(fs, "src")
-let content = mounted.read_file(fs, "src/main.mbt")?
+) -> String
+
+

In-memory backend

+

TestFs is the reference implementation. Use it for agents, tests, and ephemeral repos.

+
// moonbit
let fs = @bit.TestFs::new()
+let root = "/agent-repo"
+
+run_storage_command(fs, fs, root, "init", ["-q"])
+fs.write_string(root + "/note.txt", "hello, peer")
+run_storage_command(fs, fs, root, "add", ["note.txt"])
+run_storage_command(fs, fs, root, "commit", ["-m", "agent snapshot"])
+
+let head = run_storage_command(fs, fs, root, "rev-parse", ["HEAD"])
+println("head: \{head}")
+
+

Fs — virtual filesystem

+

Mount any commit as a read-only filesystem with lazy blob loading.

+
// moonbit
let mounted = @bit.Fs::from_commit(fs, ".git", commit_id)
+let entries = mounted.readdir(fs, "src")
+let content = mounted.read_file(fs, "src/main.mbt")?
 
 for name in entries {
-  println("- \{name}")
-}
-
- - - - - - - - - - -
MethodDescription
Fs::from_commit(fs, gitdir, sha)Open a virtual FS rooted at a commit's tree.
Fs::from_tree(fs, gitdir, sha)Open from a tree object directly.
readdir(self, fs, path)List entries in a directory.
read_file(self, fs, path)Read a blob lazily.
stat(self, fs, path)Get entry metadata (mode, size).
- -

Kv — distributed KV store

-

Git-backed key/value store with gossip sync. Useful when you want a small, replicated config alongside the repository.

-
-
// moonbit
-
let db = @bit.Kv::init(fs, fs, git_dir, node_id="aurora.local")
-
-db.set(fs, fs, "users/alice/profile", profile_bytes, ts=now())
-let value = db.get(fs, fs, "users/alice/profile")?
-
-db.sync_with_peer(fs, fs, "relay://bit.example.com/u/me/kv")
-
- -

Hub — PR / Issue API

-

Same data the bit pr / bit issue CLI commands operate on. Drive PRs from an agent, write a custom triage UI, or wire it into your editor.

-
-
// moonbit
-
let hub = @bit.Hub::init(fs, fs, git_dir)
+  println("- \{name}")
+}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodDescription
Fs::from_commit(fs, gitdir, sha)Open a virtual FS rooted at a commit's tree.
Fs::from_tree(fs, gitdir, sha)Open from a tree object directly.
readdir(self, fs, path)List entries in a directory.
read_file(self, fs, path)Read a blob lazily.
stat(self, fs, path)Get entry metadata (mode, size).
+

Kv — distributed KV store

+

Git-backed key/value store with gossip sync. Useful when you want a small, replicated config alongside the repository.

+
// moonbit
let db = @bit.Kv::init(fs, fs, git_dir, node_id="aurora.local")
+
+db.set(fs, fs, "users/alice/profile", profile_bytes, ts=now())
+let value = db.get(fs, fs, "users/alice/profile")?
+
+db.sync_with_peer(fs, fs, "relay://bit.example.com/u/me/kv")
+
+

Hub — PR / Issue API

+

Same data the bit pr and bit issue CLI commands operate on. Drive PRs from an agent, write a custom triage UI, or wire it into your editor.

+
// moonbit
let hub = @bit.Hub::init(fs, fs, git_dir)
 
 let pr = hub.create_pr(
   fs, fs,
-  title          = "Replace gossip backoff with token bucket",
-  body           = "fixes thrashing under high churn",
-  source_branch  = "feature/token-bucket",
-  target_branch  = "main",
-  author         = "ubugeeei",
+  title          = "Replace gossip backoff with token bucket",
+  body           = "fixes thrashing under high churn",
+  source_branch  = "feature/token-bucket",
+  target_branch  = "main",
+  author         = "ubugeeei",
   ts             = now()
 )
 
 hub.review(fs, fs, pr.id, status=Approve, commit=Some(pr.head))?
-hub.merge(fs, fs, pr.id)?
-
- - - - - - - - - - - - -
FunctionReturns
Hub::init(fs, rfs, gitdir)Hub
hub.create_pr(...)Pr
hub.list_prs(state)Array[Pr]
hub.review(id, status, commit?)Result[Review, Error]
hub.merge(id)Result[Sha, Error]
hub.create_issue(...)Issue
hub.link(issue_id, pr_id)Unit
- -

Node — peer protocol

-

Open sessions with other peers, exchange refs, fetch object graphs.

-
-
// moonbit
-
let peer = @bit.Node::from_uri("bit://aurora.local:9418/repo")
+hub.merge(fs, fs, pr.id)?
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionReturns
Hub::init(fs, rfs, gitdir)Hub
hub.create_pr(...)Pr
hub.list_prs(state)Array[Pr]
hub.review(id, status, commit?)Result[Review, Error]
hub.merge(id)Result[Sha, Error]
hub.create_issue(...)Issue
hub.link(issue_id, pr_id)Unit
+

Node — peer protocol

+

Open sessions with other peers, exchange refs, fetch object graphs.

+
// moonbit
let peer = @bit.Node::from_uri("bit://aurora.local:9418/repo")
 peer.handshake(timeout=2.s)?
 
-let local_refs = @bit.refs(fs, ".git")
+let local_refs = @bit.refs(fs, ".git")
 let wants      = local_refs.diff(peer.refs())
 peer.fetch(wants)?
 
-println("now at: \{peer.refs().get(\"refs/heads/main\")}")
-
- - - - - - - - - - - -
FunctionDescription
Node::from_uri(uri)Create a peer session (lazy connect).
peer.handshake(timeout?)Open the session, exchange capabilities.
peer.refs()Snapshot of remote refs.
peer.fetch(wants)Download objects reachable from wants.
peer.send(bit)Push a single object or pack.
peer.close()Drop the session.
- -
-

// see also

-

The CLI Reference documents the same surface, packaged as commands. The mapping is one-to-one: bit pr createHub::create_pr, bit fetchNode::fetch, etc.

-
- - +println("now at: \{peer.refs().get(\"refs/heads/main\")}") +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionDescription
Node::from_uri(uri)Create a peer session (lazy connect).
peer.handshake(timeout?)Open the session, exchange capabilities.
peer.refs()Snapshot of remote refs.
peer.fetch(wants)Download objects reachable from wants.
peer.send(bit)Push a single object or pack.
peer.close()Drop the session.
+
+

// see also

+

The CLI Reference documents the same surface, packaged as commands. The mapping is one-to-one: bit pr createHub::create_pr, bit fetchNode::fetch, and so on.

+
+ + + diff --git a/tools/build-docs.mjs b/tools/build-docs.mjs new file mode 100644 index 00000000..bd43902d --- /dev/null +++ b/tools/build-docs.mjs @@ -0,0 +1,412 @@ +#!/usr/bin/env node +// tools/build-docs.mjs +// Reads site/content/**/*.md, renders bodies via mizchi/markdown (via the +// MoonBit→JS module under tools/docs-build-mbt/), and writes site/
/*.html +// using inline templates that carry the bit brand layout. +// +// Frontmatter format: a leading `---` / `---` fenced block of flat key:value +// pairs (no nesting; structure encoded in named fields like prev_href / next_href). +// +// Usage: +// 1. (once) build the renderer: +// cd tools/docs-build-mbt && moon build --target js --release +// 2. node tools/build-docs.mjs + +import fs from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(here, ".."); +const contentDir = path.join(root, "site/content"); +const outDir = path.join(root, "site"); +const rendererPath = path.join( + root, + "tools/docs-build-mbt/_build/js/release/build/render/render.js", +); + +// ── load renderer ──────────────────────────────────────────────────────── +const { render } = await import(rendererPath).catch((err) => { + console.error( + `\nFailed to load MoonBit renderer at:\n ${rendererPath}\n\n` + + `Build it first:\n cd tools/docs-build-mbt && moon build --target js --release\n`, + ); + throw err; +}); + +// ── frontmatter parser ──────────────────────────────────────────────────── +function parseFrontmatter(src) { + const m = src.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/); + if (!m) return { meta: {}, body: src }; + const meta = {}; + for (const raw of m[1].split(/\r?\n/)) { + const line = raw.replace(/^\s+|\s+$/g, ""); + if (!line || line.startsWith("#")) continue; + const kv = line.match(/^([A-Za-z_][\w-]*)\s*:\s*(.*)$/); + if (!kv) continue; + let [, k, v] = kv; + v = v.replace(/^["']|["']$/g, ""); + if (/^-?\d+$/.test(v)) meta[k] = Number(v); + else if (v === "true" || v === "false") meta[k] = v === "true"; + else meta[k] = v; + } + return { meta, body: m[2] }; +} + +// ── walk content tree ──────────────────────────────────────────────────── +async function walk(dir) { + const out = []; + for (const ent of await fs.readdir(dir, { withFileTypes: true })) { + const p = path.join(dir, ent.name); + if (ent.isDirectory()) out.push(...(await walk(p))); + else if (ent.isFile() && ent.name.endsWith(".md")) out.push(p); + } + return out; +} + +// ── HTML post-processing ───────────────────────────────────────────────── +function slugify(s) { + return s + .toLowerCase() + .replace(/<[^>]+>/g, "") + .replace(/[^\p{Letter}\p{Number}]+/gu, "-") + .replace(/^-|-$/g, ""); +} + +function transformHeadings(html) { + // Number h2s "// 001" and add id + data-num. Leave h3/h4 alone. + let i = 1; + const out = html.replace( + /]*)?>([\s\S]*?)<\/h2>/g, + (_, inner) => { + const id = slugify(inner) || `s-${i}`; + const num = `// ${String(i).padStart(3, "0")}`; + i++; + return `

${inner}

`; + }, + ); + return out; +} + +function transformCodeBlocks(html) { + // mizchi/markdown emits
. + // Wrap in our .codeblock structure and hand the language off via data-lang + // so site/assets/syntax.js can pick it up. + return html.replace( + /
([\s\S]*?)<\/code><\/pre>/g,
+    (_, lang, code) => {
+      const langLabel = `// ${lang || "txt"}`;
+      const dataLang = lang || "";
+      return (
+        `
` + + `
${langLabel}` + + `
` + + `
${code}
` + + `
` + ); + }, + ); +} + +// Render a short string of markdown as inline HTML (no

wrapper). +// Useful for frontmatter fields like `lead` where we want `code` and **bold** +// to render but the result is dropped into an inline context. +function renderInline(md) { + if (!md) return ""; + const html = render(md).trim(); + return html + .replace(/^

/, "") + .replace(/<\/p>$/, "") + .replace(/<\/p>\s*

/g, "

"); +} + +function extractToc(html) { + const items = []; + const re = /([\s\S]*?)<\/h2>/g; + let m; + while ((m = re.exec(html)) !== null) { + items.push({ id: m[1], num: m[2], label: m[3].replace(/<[^>]+>/g, "").trim() }); + } + return items; +} + +// ── sidenav generation ─────────────────────────────────────────────────── +function sidenavFor(pages, section, activeSlug) { + const sectionPages = pages + .filter( + (p) => + p.meta.section === section && + p.meta.slug !== "index" && + p.meta.template !== "landing", + ) + .sort((a, b) => (a.meta.order ?? 99) - (b.meta.order ?? 99)); + + const items = sectionPages.map((p) => { + const href = `${p.meta.slug}.html`; + const num = + p.meta.order != null ? String(p.meta.order).padStart(2, "0") : ""; + const label = num ? `${num} ${p.meta.nav_label || p.meta.title}` : p.meta.title; + const active = p.meta.slug === activeSlug ? ' class="active"' : ""; + return `

  • ${label}
  • `; + }); + + const otherSection = section === "learn" ? "reference" : "learn"; + const otherLink = + section === "learn" + ? '
  • Reference
  • ' + : '
  • Learning Guide
  • '; + + return `

    // ${section === "learn" ? "Learning Guide" : "Reference"}

    +
      +${items.join("\n")} +
    +

    // More

    +
      +${otherLink} +
    • Home
    • +
    `; +} + +// ── chapter list for index pages ───────────────────────────────────────── +function chapterListFor(pages, section) { + const sectionPages = pages + .filter( + (p) => + p.meta.section === section && + p.meta.slug !== "index" && + p.meta.template !== "landing", + ) + .sort((a, b) => (a.meta.order ?? 99) - (b.meta.order ?? 99)); + + const items = sectionPages.map((p) => { + const href = `${p.meta.slug}.html`; + const num = String(p.meta.order ?? 0).padStart(2, "0"); + const tag = section === "learn" ? num : `R${p.meta.order ?? "?"}`; + return ` +
    // ${tag}
    +
    +

    ${p.meta.nav_label || p.meta.title}

    +

    ${renderInline(p.meta.summary || "")}

    +
    +
    ${p.meta.meta || ""}
    +
    `; + }); + + return ` `; +} + +// ── templates ──────────────────────────────────────────────────────────── +function pageHead({ title, description, rootPath }) { + return ` + + + + +${title}${ + description + ? `\n` + : "" + } + + + +`; +} + +function topbar({ rootPath, activeSection }) { + const cls = (s) => (activeSection === s ? ' class="active"' : ""); + return `
    +
    + bit + docs · v0.1 +
    + + +
    `; +} + +function footer({ rootPath, sectionLabel }) { + return ``; +} + +function tocAside(items) { + if (!items.length) return ""; + const lis = items + .map( + (it) => + `
  • ${it.num} ${it.label}
  • `, + ) + .join("\n"); + return ` `; +} + +function pager(meta) { + if (!meta.prev_href && !meta.next_href) return ""; + const left = meta.prev_href + ? ` + ← ${meta.prev_kicker || "back"} + ${meta.prev_label || ""} + ` + : ` `; + const right = meta.next_href + ? ` ` + : ` `; + return ``; +} + +function chapterPageTemplate({ + meta, + rootPath, + sidenav, + body, + toc, + description, +}) { + return `${pageHead({ title: meta.title, description, rootPath })} + +${topbar({ rootPath, activeSection: meta.section })} + +
    + + + +
    +
    +

    ${meta.kicker || ""}

    +

    ${meta.h1 || meta.title}

    ${ + meta.lead ? `\n

    ${renderInline(meta.lead)}

    ` : "" + } +
    + +${body} + +${pager(meta)} +
    + +${toc} + +
    + +${footer({ rootPath, sectionLabel: `${meta.section} · ${meta.slug}` })} + + + + + +`; +} + +function indexPageTemplate({ meta, rootPath, body, chapterList, description }) { + return `${pageHead({ title: meta.title, description, rootPath })} + +${topbar({ rootPath, activeSection: meta.section })} + +
    +
    + bit / ${meta.section} + ${meta.hero_tag || ""} +
    + +

    ${meta.kicker || ""}

    +

    ${meta.h1 || meta.title}

    ${ + meta.lead + ? `\n

    ${renderInline(meta.lead)}

    ` + : "" + } +
    + +
    +${chapterList} +
    + +${body ? `
    \n${body}\n
    \n` : ""} + +${footer({ rootPath, sectionLabel: meta.section })} + + + + + +`; +} + +// ── per-page renderer ──────────────────────────────────────────────────── +function renderPage(page, pages) { + const isIndex = page.meta.slug === "index" || page.meta.template === "index"; + const rootPath = "../"; // all rendered pages live under site/
    / + + const rawHtml = render(page.body || ""); + const body = transformCodeBlocks(transformHeadings(rawHtml)); + const toc = isIndex ? "" : tocAside(extractToc(body)); + const sidenav = sidenavFor(pages, page.meta.section, page.meta.slug); + + if (isIndex) { + return indexPageTemplate({ + meta: page.meta, + rootPath, + body, + chapterList: chapterListFor(pages, page.meta.section), + description: page.meta.description, + }); + } + return chapterPageTemplate({ + meta: page.meta, + rootPath, + sidenav, + body, + toc, + description: page.meta.description, + }); +} + +// ── main ──────────────────────────────────────────────────────────────── +const files = await walk(contentDir); +const pages = await Promise.all( + files.map(async (f) => { + const src = await fs.readFile(f, "utf8"); + const { meta, body } = parseFrontmatter(src); + const rel = path.relative(contentDir, f).replace(/\\/g, "/"); // posix + // Section/slug derived from path if not given in frontmatter + if (!meta.section || !meta.slug) { + const parts = rel.replace(/\.md$/, "").split("/"); + meta.section ||= parts[0]; + meta.slug ||= parts.slice(1).join("/") || "index"; + } + return { file: f, meta, body }; + }), +); + +let wrote = 0; +for (const page of pages) { + const html = renderPage(page, pages); + const slug = page.meta.slug === "index" ? "index" : page.meta.slug; + const out = path.join(outDir, page.meta.section, `${slug}.html`); + await fs.mkdir(path.dirname(out), { recursive: true }); + await fs.writeFile(out, html); + wrote++; + console.log(` ${path.relative(root, out)}`); +} + +console.log(`\nbuilt ${wrote} page${wrote === 1 ? "" : "s"}.`); diff --git a/tools/docs-build-mbt/.gitignore b/tools/docs-build-mbt/.gitignore new file mode 100644 index 00000000..338b48b1 --- /dev/null +++ b/tools/docs-build-mbt/.gitignore @@ -0,0 +1,3 @@ +_build/ +.mooncakes/ +target/ diff --git a/tools/docs-build-mbt/README.md b/tools/docs-build-mbt/README.md new file mode 100644 index 00000000..a403a5b3 --- /dev/null +++ b/tools/docs-build-mbt/README.md @@ -0,0 +1,15 @@ +# bit docs-build (MoonBit) + +A tiny MoonBit project that wraps [`mizchi/markdown`](https://github.com/mizchi/markdown.mbt) +into a single ESM-friendly `render(source) -> html` function. The `tools/build-docs.mjs` +Node script imports the compiled JS output, walks `site/content/**/*.md`, and writes +`site/learn/*.html` + `site/reference/*.html`. + +## Build + +```sh +moon update +moon build --target js --release +``` + +Output: `_build/js/release/build/render/render.js` exporting `{ render }`. diff --git a/tools/docs-build-mbt/moon.mod.json b/tools/docs-build-mbt/moon.mod.json new file mode 100644 index 00000000..4e97ca37 --- /dev/null +++ b/tools/docs-build-mbt/moon.mod.json @@ -0,0 +1,12 @@ +{ + "name": "mizchi/bit-docs-build", + "version": "0.0.1", + "deps": { + "mizchi/markdown": "0.4.9" + }, + "readme": "README.md", + "license": "Apache-2.0", + "description": "Build-time markdown renderer for the bit docs site. Wraps mizchi/markdown as a JS module consumed by tools/build-docs.mjs.", + "source": "src", + "preferred-target": "js" +} diff --git a/tools/docs-build-mbt/src/render/moon.pkg b/tools/docs-build-mbt/src/render/moon.pkg new file mode 100644 index 00000000..2131ecfc --- /dev/null +++ b/tools/docs-build-mbt/src/render/moon.pkg @@ -0,0 +1,14 @@ +import { + "mizchi/markdown" @markdown, +} + +options( + link: { + "js": { + "exports": [ + "render:render", + ], + "format": "esm", + }, + }, +) diff --git a/tools/docs-build-mbt/src/render/render.mbt b/tools/docs-build-mbt/src/render/render.mbt new file mode 100644 index 00000000..641ba974 --- /dev/null +++ b/tools/docs-build-mbt/src/render/render.mbt @@ -0,0 +1,8 @@ +///| Render a markdown source string to HTML. +/// +/// Thin wrapper over `mizchi/markdown`'s convenience `md_to_html`. We keep +/// bare URLs as autolinks (the default) and leave wikilinks off — the docs +/// site uses standard markdown link syntax everywhere. +pub fn render(source : String) -> String { + @markdown.md_to_html(source) +}