Skip to content

Commit 1912f44

Browse files
committed
chore(blog): trim demo comments and apply cleanup (adopt cn, hoist languageMap, chip color)
1 parent 32151a0 commit 1912f44

3 files changed

Lines changed: 74 additions & 103 deletions

File tree

apps/sim/content/blog/agent-as-yjs-peer/components/agent-peer-demo.tsx

Lines changed: 35 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,15 @@
33
import { useEffect, useRef, useState } from 'react'
44

55
/**
6-
* A small, self-contained illustration for the post: two peers editing the same document at once.
7-
* A teammate ("Zoe") extends the intro line while the agent ("Sim") writes a short formatted block
8-
* below — a bold lead, an inline code chip, and a bullet list, arriving already rendered. Neither one
9-
* overwrites the other. It is a scripted animation, not a live CRDT, but it is faithful to the idea:
10-
* both carets advance independently and both edits land.
6+
* A small illustration for the post: two peers editing one document at once. A teammate ("Zoe")
7+
* extends the intro line while the agent ("Sim") writes a short formatted block below (bold lead,
8+
* code chip, bullet list). Neither overwrites the other. A scripted animation, but faithful to the
9+
* idea: both carets advance independently and both edits land.
1110
*
12-
* Everything is styled from the SAME design tokens the real markdown editor uses (`--font-inter`,
13-
* `--font-martian-mono`, `--text-primary`, `--surface-5`, `--border`, `--surface-1`, `--bg`), the
14-
* carets replicate `CollaborationCaret`'s bar + notched name label (rich-markdown-editor.css), and
15-
* the identity colors are the real `USER_COLORS` (lib/workspaces/colors.ts). No shadows — the app
16-
* frames surfaces with a 1px border. The finished document is always laid out; unrevealed text is
17-
* kept `visibility: hidden` so the card never changes size as content streams in. Degrades to the
18-
* finished document when JavaScript is off or reduced motion is requested.
11+
* Styled from the same design tokens as the real markdown editor, with carets replicating
12+
* `CollaborationCaret` and identity colors from `USER_COLORS`. The finished document is always laid
13+
* out; unrevealed text is kept `visibility: hidden` so the card never resizes as content streams in.
14+
* Degrades to the finished document with JavaScript off or reduced motion.
1915
*/
2016

2117
type Seg = { t: string } | { code: string } | { b: string }
@@ -27,16 +23,15 @@ interface Block {
2723
const FONT_SANS = 'var(--font-inter, ui-sans-serif, system-ui, -apple-system, sans-serif)'
2824
const FONT_MONO = 'var(--font-martian-mono, ui-monospace, SFMono-Regular, Menlo, monospace)'
2925

30-
// Two collaborators, colored from the real identity palette (USER_COLORS), as getUserColor assigns.
31-
const AGENT = { name: 'Sim', color: '#60C5FF' } // Blue
32-
const HUMAN = { name: 'Zoe', color: '#F472B6' } // Pink
26+
// Colors from the real identity palette (USER_COLORS / getUserColor).
27+
const AGENT = { name: 'Sim', color: '#60C5FF' }
28+
const HUMAN = { name: 'Zoe', color: '#F472B6' }
3329

3430
// The intro line already exists; the teammate is still appending to it.
3531
const HUMAN_BASE = 'Rollout is set for Friday.'
3632
const HUMAN_ADD = ' Ops signed off this morning.'
3733

38-
// The block the agent writes from empty, streamed one character at a time. It arrives already
39-
// formatted: the bold lead, the code chip, and the bullets all render as they land.
34+
// The agent's block, streamed one char at a time, arriving already formatted.
4035
const AGENT_BLOCKS: Block[] = [
4136
{
4237
kind: 'p',
@@ -52,11 +47,9 @@ const HUMAN_TOTAL = HUMAN_ADD.length
5247
const AGENT_TOTAL = AGENT_FLAT.length
5348

5449
/**
55-
* Builds a per-character reveal timeline with an organic, non-uniform cadence: a smooth
56-
* multi-frequency wobble in typing speed, plus natural pauses at spaces and punctuation. Fully
57-
* deterministic (no RNG) so the server and client agree and it never jitters between renders — the
58-
* variation comes from the character index and content, not randomness. `times[i]` is the ms at
59-
* which character `i` should appear.
50+
* Per-character reveal timeline with a deterministic (no-RNG) speed wobble plus pauses at spaces and
51+
* punctuation, so the server and client agree and it never jitters between renders. `times[i]` is the
52+
* ms at which character `i` appears.
6053
*/
6154
function buildSchedule(
6255
text: string,
@@ -75,8 +68,7 @@ function buildSchedule(
7568
return times
7669
}
7770

78-
// The agent streams a touch faster and steadier (it is a machine); the teammate types slower with
79-
// bigger word/sentence pauses, and starts a beat later so the two are never metronomically locked.
71+
// Agent streams faster and steadier; the human is slower and starts a beat later, so the two never lock in sync.
8072
const AGENT_TIMES = buildSchedule(AGENT_FLAT, { base: 34, wobble: 0.5, space: 45, punct: 150 })
8173
const HUMAN_TIMES = buildSchedule(HUMAN_ADD, {
8274
base: 58,
@@ -93,9 +85,7 @@ const countReached = (times: number[], t: number): number => {
9385
return n
9486
}
9587

96-
// The demo streams through ONCE when it scrolls into view, then rests on the finished document —
97-
// no loop, no fade. `RUN_MS` is when the last character has landed; at that point the reveal is
98-
// clamped to the finished state and the animation stops (no further frames are scheduled).
88+
// RUN_MS: when the last character lands. The stream plays once, then clamps to the finished doc and stops.
9989
const RUN_MS = Math.max(lastTime(AGENT_TIMES), lastTime(HUMAN_TIMES))
10090

10191
const proseStyle: React.CSSProperties = {
@@ -116,9 +106,8 @@ const codeStyle: React.CSSProperties = {
116106
padding: '0.125rem 0.375rem',
117107
}
118108

119-
/** Replicates CollaborationCaret in its active state: a 2px identity-colored bar with the notched
120-
* name label above it (rich-markdown-editor.css `.collaboration-carets__bar` / `__label`). Zero
121-
* inline width, so inserting it at the write head never shifts the surrounding text. */
109+
/** Replicates CollaborationCaret (rich-markdown-editor.css): a 2px identity-colored bar with the
110+
* notched name label above it. Zero inline width, so placing it at the write head never shifts text. */
122111
function Caret({ who }: { who: typeof HUMAN }) {
123112
return (
124113
<span
@@ -165,10 +154,9 @@ function Caret({ who }: { who: typeof HUMAN }) {
165154
)
166155
}
167156

168-
/** Render one block's inline segments, revealing up to `revealed` characters (global index) and
169-
* reserving the rest with `visibility: hidden` so layout is stable. Drops the caret exactly at the
170-
* write head when the boundary falls inside this block. Returns the nodes and whether it placed the
171-
* caret, so the caller can append a trailing caret when the whole document is complete. */
157+
/** Renders a block's segments, revealing up to `revealed` chars and reserving the rest with
158+
* `visibility: hidden` for stable layout. Places the caret at the write head if it falls in this
159+
* block; returns the nodes and whether the caret was placed. */
172160
function renderBlockNodes(
173161
block: Block,
174162
blockStart: number,
@@ -187,10 +175,8 @@ function renderBlockNodes(
187175
const key = `${blockStart}-${si}`
188176

189177
if ('code' in seg) {
190-
// Type the chip in character by character, at the same cadence as the surrounding text, with
191-
// the caret moving through it. The box reserves its full width up front (the unrevealed tail is
192-
// visibility:hidden inside it) so the card never reflows. Revealing it as one chunk instead made
193-
// it either freeze-and-jump (caret parked before an empty gap) or fly past as a skipped unit.
178+
// Type the chip char by char with the caret moving through it; the box reserves full width up
179+
// front (hidden tail) so the card never reflows. Revealing it as one chunk froze/jumped the caret.
194180
const reached = revealed >= start
195181
const shown = reached ? Math.min(raw.length, revealed - start) : 0
196182
const atBoundary = !didPlace && reached && shown < raw.length
@@ -230,7 +216,7 @@ function renderBlockNodes(
230216
}
231217
offset = end
232218
}
233-
// Once the whole block is written, the caret rests at the end of the last block the peer stays present.
219+
// Caret rests at the end of the last block; the peer stays present.
234220
if (isLast && !didPlace) {
235221
nodes.push(<Caret key='end-caret' who={AGENT} />)
236222
didPlace = true
@@ -271,10 +257,8 @@ function AgentDoc({ revealed }: { revealed: number }) {
271257
offset = blockStart + block.segs.reduce((m, s) => m + segText(s).length, 0)
272258

273259
if (block.kind === 'li') {
274-
// The bullet is the <li>'s own list marker, so hiding the item's text with visibility:hidden
275-
// would still leave an empty bullet showing. Hide the whole item until the caret reaches it, so
276-
// each bullet appears with its line rather than all of them up front. The hidden item still
277-
// reserves its height, so the card never changes size.
260+
// visibility:hidden on the text still leaves the <li> marker showing, so hide the whole item
261+
// until the caret reaches it. Hidden items still reserve height, so the card never resizes.
278262
list.push(
279263
<li
280264
key={`li-${bi}`}
@@ -323,9 +307,7 @@ function Avatar({ who, overlap }: { who: typeof HUMAN; overlap: boolean }) {
323307
}
324308

325309
export function AgentPeerDemo() {
326-
// Initial (SSR / no-JS / reduced-motion) state is the finished document, so the content is always
327-
// present without JavaScript. When the demo scrolls into view it resets to empty, streams through
328-
// once, and rests on the finished document.
310+
// Initial state (SSR / no-JS / reduced-motion) is the finished document; streaming resets it on scroll-in.
329311
const [humanN, setHumanN] = useState(HUMAN_TOTAL)
330312
const [agentN, setAgentN] = useState(AGENT_TOTAL)
331313
const cardRef = useRef<HTMLDivElement>(null)
@@ -335,30 +317,27 @@ export function AgentPeerDemo() {
335317
typeof window !== 'undefined' &&
336318
window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
337319
const el = cardRef.current
338-
// Reduced motion, no element, or an environment without IntersectionObserver (older browsers,
339-
// some test runners): leave the finished document in place as the fallback and never clear it.
320+
// No IntersectionObserver / no element / reduced motion: keep the finished document, never clear it.
340321
if (reduce || !el || typeof IntersectionObserver === 'undefined') return
341322

342323
let raf = 0
343324
let played = false
344325

345326
const play = () => {
346-
// Clear to empty only now that the demo is actually on screen, so it's never left blank if the
347-
// observer never fires (the element never reaches the visibility threshold).
327+
// Clear to empty only once on-screen, so the doc is never left blank if the observer never fires.
348328
setHumanN(0)
349329
setAgentN(0)
350330
const start = performance.now()
351-
// A character lands every ~30–60ms but frames fire every ~16ms, so most frames reveal nothing
352-
// new. Track the last emitted counts and only setState on an actual change, so React renders
353-
// roughly once per character (~90 times total) rather than every frame.
331+
// Frames fire ~16ms but chars land every ~30-60ms; only setState when a count actually changes,
332+
// so React re-renders ~once per character instead of every frame.
354333
let lastH = 0
355334
let lastA = 0
356335
const tick = (now: number) => {
357336
const t = now - start
358337
if (t >= RUN_MS) {
359338
if (lastH !== HUMAN_TOTAL) setHumanN(HUMAN_TOTAL)
360339
if (lastA !== AGENT_TOTAL) setAgentN(AGENT_TOTAL)
361-
return // rest on the finished documentno loop, no further frames
340+
return // rest on the finished document; no loop
362341
}
363342
const h = countReached(HUMAN_TIMES, t)
364343
const a = countReached(AGENT_TIMES, t)
@@ -406,7 +385,7 @@ export function AgentPeerDemo() {
406385
overflow: 'hidden',
407386
}}
408387
>
409-
{/* header: filename + presence — mirrors Resource.Header (min-height 48px, border-b, px-4). */}
388+
{/* header: filename + presence, mirroring Resource.Header. */}
410389
<div
411390
style={{
412391
display: 'flex',
@@ -451,7 +430,7 @@ export function AgentPeerDemo() {
451430
</span>
452431
</div>
453432

454-
{/* document bodythe real prose scale (15px / 430 / 25px, --font-inter, --text-primary). */}
433+
{/* document body: the real prose scale (see proseStyle). */}
455434
<div style={{ ...proseStyle, padding: '18px 22px 22px' }}>
456435
<div
457436
style={{

apps/sim/lib/content/code.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ interface CodeBlockProps {
88
}
99

1010
/**
11-
* Blog code block. Renders through emcn's read-only `Code.Viewer` wearing the exact same
12-
* `chipFieldSurfaceClass` surface the in-app code editor uses (the custom-tools `CodeEditor`),
13-
* so a fenced block in a post matches the real editor: same Prism theme, gutter, and font, on
14-
* the same theme-following surface (`--surface-5`/`--border-1`, rounded-lg) — no forced dark,
15-
* all from existing design tokens.
11+
* Blog code block. Renders through emcn's read-only `Code.Viewer` on the same `chipFieldSurfaceClass`
12+
* surface as the in-app custom-tools `CodeEditor`, so a fenced block in a post matches the real editor
13+
* (Prism theme, gutter, font) on a theme-following surface rather than a forced-dark one.
1614
*/
1715
export function CodeBlock({ code, language }: CodeBlockProps) {
1816
return (

0 commit comments

Comments
 (0)