Repository navigation
Live word / character count indicator in the chat composer #60
Description
Problem or Motivation
OpenWork's chat composer (FreeFormInput) gives no feedback about how much
you've written. The composer toolbar already surfaces a context-usage
indicator (tokens used / total), a model picker, a thinking-level picker and an
attach control — but there is no length feedback for the draft itself. When
composing a long prompt (a spec, a big paste, a multi-paragraph instruction) you
have no glanceable sense of its size.
Comparable desktop composers and editors ship exactly this. The Codex desktop
composer and editor status bars (VS Code, Google Docs, the Claude/ChatGPT desktop
message boxes) all show a live word / character count of what you're
currently typing. It is a small, universally-expected affordance that OpenWork's
otherwise feature-rich composer toolbar is missing.
Proposed Solution
Add a small, unobtrusive draft count indicator to the composer toolbar
(left-of-center, next to the attach / working-directory badges, mirroring their
!compactMode rule):
- Shows the word count of the current draft inline (e.g.
Words: 12), only
while the composer has non-whitespace text — so the empty composer stays clean. - A hover tooltip breaks down the full stats: Words, Characters, and
Lines. - Counts update live on every keystroke (derived from the existing controlled
inputtext — no extra state). - Hidden in
compactMode(mirroring the context-usage / thinking / model
controls) and hidden entirely when the draft is empty.
The counting itself (words / characters / lines) lives in a small pure,
unit-tested module so its semantics (Unicode-aware character count,
whitespace-collapsing word count, newline-based line count) are locked down
without a DOM.
Feasibility
Classification: pure frontend — candidate. No qwen-code backend involvement.
- The composer already stores the draft as a plain-text string in its controlled
inputstate (FreeFormInput.tsx); the indicator is pure derived state over
that value — no new IPC, no main-process change, no persistence. - The indicator reuses the existing composer-toolbar layout (it slots between the
left badge group and the flex spacer) and the sharedTooltipprimitives. - Three new i18n keys (
chat.composerWords,chat.composerCharacters,
chat.composerLines) added to all locales, using a plural-neutral
label form ("Words: {{count}}") so no per-locale plural categories are
needed.
Alternatives Considered
- Character count only: simpler, but word count is the more meaningful metric
for prose prompts; showing words inline and the full breakdown (words /
characters / lines) on hover covers both without clutter. - A live token count of the draft: more useful in theory but requires a
tokenizer that matches the backend model — that is backend-coupled and belongs
with the existing context-usage indicator, not a pure-frontend counter. - Always-visible (even when empty): would clutter the empty composer, which
the app deliberately keeps minimal; showing the indicator only when there's
content matches the toolbar's other content-dependent affordances.
Additional Context
Feasibility verified against apps/electron: the composer's input is a plain
string (coerceInputText), the RichTextInput is a hand-rolled contentEditable
that fires onChange with plain text on every input event, and the toolbar's
bottom row already has a left badge group + flex spacer where the indicator
slots in cleanly.
Acceptance criteria (CDP e2e assertion)
A new e2e/assertions/composer-count.assert.ts drives the real built app over CDP
entirely in the draft (no-session) state — no seeded conversation and no backend:
- The composer's contenteditable renders and, while empty, the count
indicator ([data-testid="composer-count"]) is absent — proving it stays
hidden for an empty draft. - Real typing (via CDP
Input.insertTextinto the focused editor) of
hello worldmakes the indicator appear withdata-word-count="2"and
data-char-count="11"— proving it reflects actual typed content. - Appending more text updates the counts live (word count increases, char count
increases) — proving it is reactive per keystroke, not a one-shot render. - Selecting-all and deleting the draft makes the indicator disappear again —
proving it reacts to the draft becoming empty.
In addition, the pure counting module ships with unit tests
(composer-count.test.ts) covering empty / whitespace-only input, single vs.
multiple words, collapsed internal whitespace, multi-line line counting, and
Unicode/emoji character counting.
Part of the autonomous desktop-feature loop (loop-bot).
Activity
Add live word/character count indicator to the chat composer
Add live word/character count indicator to the chat composer (#61)