Field Notes

Jun 5, 2026 · Updated 14 days ago

We used to blog about how to code. Now it's time to blog the decisions. I've been thinking about writing field notes for a long time. The term usually means observing other people, but this is about observing my own thoughts.

Here I'll share some revamps and improvements across the projects I worked on. Treat it as field notes, a work archive, a showcase, or whatever you want :-)

Chat Context Menu

This is a crucial part of LobeHub. It carries every piece of context except the message itself. But a lot of it was off.

Old context menu
Old context menu
  • Ambiguous function: what does Show Formatting Toolbar do? Will this open a new dialog? (It's actually a checkbox)
  • Icon color swap based on state: Information overload.
  • Mixed trailing arrow icons → / ▶︎
  • Progressive exposure: why is Add Skills parallel with Skills, while View More Files is not parallel with Upload File or Image.
  • Wording: "Upload File or Image" should be "Attachment".
  • File name truncation: only truncating the trailing part. Should truncate the middle part.

After fixing all of these:

Revamped context menu
Revamped context menu

The rules are:

  • Consistent & clean wording
  • Information structure & progressive exposure
  • Consistency over tuning

Janky Transition

This is the major transition in Detail. People click a card to see more about that detail.

0:00 / 0:00
Before

Originally I put layout on the card and let it animate its own size and position. The contents drift along a slightly different path, arriving a beat early or late. Nothing is technically broken, yet the eye reads it as "off" instantly.

// DetailItem.tsx — the dialog frame
const layoutTransition = {
  layout: { type: "spring", stiffness: 320, damping: 32, mass: 0.6 },
};
<motion.article layout transition={layoutTransition} />

// DetailItemHeader.tsx — title on a near-but-different spring
const titleTransition = {
  layout: { type: "spring", stiffness: 320, damping: 30, mass: 0.7 }, // ← not the frame's
};
<motion.h3 layout transition={{ ...titleTransition, scale }} />

// ...and everything else just falls back to the default spring
<motion.p layout />              {/* summary */}
<motion.div layout />            {/* tags */}
<motion.div layout />            {/* media (DetailItemMedia.tsx) */}

The cause is that Framer Motion animates every layout element on its own independent spring. Leave the transition off and it falls back to the default; hand-tune each one, and they each run slightly different physics. The frame travels on one curve while its children travel on five others.

The fix is to make everything move on the same spring. So I pulled the spring out into a single constant and handed that exact transition to every animated element in the expand:

// animations.ts — one spring, shared by everything in the expandexport const DETAIL_LAYOUT_SPRING = {  type: "spring", stiffness: 320, damping: 32, mass: 0.6,};export const DETAIL_LAYOUT_TRANSITION = { layout: DETAIL_LAYOUT_SPRING };// DetailItem.tsx — the dialog frameconst layoutTransition = {  layout: { type: "spring", stiffness: 320, damping: 32, mass: 0.6 },};<motion.article layout transition={layoutTransition} /><motion.article layout transition={DETAIL_LAYOUT_TRANSITION} />// DetailItemHeader.tsx — title, summary, tagsconst titleTransition = {  layout: { type: "spring", stiffness: 320, damping: 30, mass: 0.7 },};<motion.h3 layout transition={{ ...titleTransition, scale }} /><motion.p layout /><motion.div layout />          {/* tags */}<motion.h3 layout transition={{ ...DETAIL_LAYOUT_TRANSITION, scale }} /><motion.p layout transition={DETAIL_LAYOUT_TRANSITION} /><motion.div layout transition={DETAIL_LAYOUT_TRANSITION} />  {/* tags */}// DetailItemMedia.tsx — media<motion.div layout /><motion.div layout transition={DETAIL_LAYOUT_TRANSITION} />

The revamped version feels like this:

0:00 / 0:00

The article frame, the media, the title, the summary, the tag row, and each tag chip now share one physics simulation, so they accelerate, travel, and settle together, riding the frame as a single object. The takeaway is small but general: for coordinated layout animations, consistency beats tuning.

Privacy Warning Dialog

The major problem here is wording.

Old version
Old version

The first time I saw the dialog, I almost immediately skipped it — no hook, no real reminder, just a warning that no one likes to read.

Revamped version
Revamped version

Chat Sharing

🚧 This section is WIP

My colleague handed this to me to "polish details". The feature lets people select multiple messages and forward them to another agent. At first glance, I spotted many problems:

  • The bottom toolbar takes too much vertical space, which matters more here because we want people to see more messages on one screen
  • The agent title and avatar combo repeats in every message, eating more vertical space
  • "Select to here" is ambiguous: from where to where?
  • Text hierarchy error in the Forward panel
  • Calling people "User" and calling AI "Assistant"
Revamped version
Revamped version

IM Channel Configuration

Before
Before

The constraints here:

  • We really need many boring fields per the platform's guidelines
  • A sidebar layout will break the pattern
  • People will most likely visit here only once

So the key is helping people get to their destination quickly and finish the setup. The platform description is there to help people pick a better platform to start with, because most people use more than one of the listed platforms.

0:00 / 0:00
After

Usage Dashboard

🚧 This section is WIP

0:00 / 0:00
Before

I redesigned the entire dashboard for the Workspace feature.

LobeHub usage card — afterLobeHub usage card — beforeBeforeAfter
Usage Dashboard of LobeHub

More Before / After

Here are more comparisons. Try adjusting the handle to see what's changed.

LobeHub share card — afterLobeHub share card — beforeBeforeAfter
Share Screen of LobeHub
LobeHub login screen — afterLobeHub login screen — beforeBeforeAfter
Login Screen of LobeHub
Inbox
Inbox

If there's more work worth sharing, I'll write a second one.