Title: Add a live word / character count indicator to the chat composer by DragonnZhang · Pull Request #61 · modelstudioai/openwork · GitHub
Open Graph Title: Add a live word / character count indicator to the chat composer by DragonnZhang · Pull Request #61 · modelstudioai/openwork
X Title: Add a live word / character count indicator to the chat composer by DragonnZhang · Pull Request #61 · modelstudioai/openwork
Description: Closes #60 What & why OpenWork's chat composer (FreeFormInput) gave no feedback about how much you've written. The toolbar already surfaces a context-usage indicator, a model picker, a thinking-level picker and an attach control — but there was no length feedback for the draft itself, which comparable desktop composers/editors (Codex desktop, VS Code / Google Docs status bars, the Claude/ChatGPT message boxes) all provide. This adds a small, unobtrusive draft count indicator to the composer toolbar: shows the word count 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 per keystroke, derived from the existing controlled input text (no new state, no persistence); hidden in compactMode (mirroring the context / thinking / model controls) and hidden entirely when the draft is empty. Frontend-only. No qwen-code backend change — the composer already stores the draft as a plain-text string, so the indicator is pure derived state. Changes lib/composer-count.ts — pure computeComposerCounts / shouldShowComposerCount helpers (Unicode-aware character count via code points, whitespace-collapsing word count, newline-based line count), DOM-free and unit-tested. components/app-shell/input/ComposerCountIndicator.tsx — the indicator: renders null for an empty draft, shows the word count inline with the full breakdown in a tooltip, and exposes data-word-count / data-char-count / data-line-count for e2e. components/app-shell/input/FreeFormInput.tsx — slots the indicator into the toolbar bottom row (between the left badge group and the flex spacer, !compactMode). i18n — three new keys (chat.composerWords, chat.composerCharacters, chat.composerLines) added to all 7 locales, using a plural-neutral label form ("Words: {{count}}") so no per-locale plural categories are needed. e2e/assertions/composer-count.assert.ts — new CDP assertion (below). Verification (DoD) bun run typecheck:all — zero new errors. packages/{core,shared,server-core,server,session-tools-core,ui} all pass clean; apps/electron shows only its 11 pre-existing baseline errors (auto-update.ts owner/repo ×4, a settings-default-thinking test tuple ×5, two test files importing vitest) — none in the files this PR touches. Unit tests — composer-count.test.ts 10/10 pass (empty / whitespace-only, single vs. multiple words, collapsed internal whitespace, multi-line, emoji/Unicode character counting). Touched-area suites (renderer/lib) 141/141 pass; the one unrelated SAFE_MODE_CONFIG failure pre-dates this change. bun run lint:i18n:parity — OK (6 locales, 1545 keys each — +3, all present in every locale). ESLint — clean on the new/changed files. Renderer build (bun run electron:build:renderer) — ✅ builds cleanly. CDP assertion transpiles (bun build e2e/assertions/composer-count.assert.ts) — ✅. CDP e2e assertion (composer-count.assert.ts) Drives the real built app over CDP entirely in the draft (no-session) state — no seeded conversation and no backend: While the composer is empty, the count indicator ([data-testid="composer-count"]) is absent. Real typing (CDP Input.insertText) of hello world makes it appear with data-word-count="2" and data-char-count="11". Appending again updates the counts live to data-word-count="3" / data-char-count="17" — proving per-keystroke reactivity. Selecting-all and deleting the draft makes the indicator disappear again. ⚠️ Local CDP run was blocked by this sandbox's egress policy, not by the feature — the e2e build fails before launch because the Electron binary download (GitHub release host) returns 403 and the libsignal WhatsApp-worker dependency can't be fetched, the same limitation noted on the other open loop-bot PRs. The assertion transpiles and is included so CI / a reviewer can run bun run e2e in an environment with normal network access. Everything that doesn't require launching Electron (typecheck, unit tests, i18n parity, renderer build) passes. Part of the autonomous desktop-feature loop (loop-bot). Generated by Claude Code
Open Graph Description: Closes #60 What & why OpenWork's chat composer (FreeFormInput) gave no feedback about how much you've written. The toolbar already surfaces a context-usage indicator, a model picker, a ...
X Description: Closes #60 What & why OpenWork's chat composer (FreeFormInput) gave no feedback about how much you've written. The toolbar already surfaces a context-usage indicator, a mode...
Opengraph URL: https://github.com/modelstudioai/openwork/pull/61
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:007773f4-e1c1-c4bc-b70c-fb0c8cfbb1de |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | DE96:190D34:3E32D7:53E0B7:6A5291EA |
| html-safe-nonce | 33a20e563b1de1a3ed0c4d377e324e09e388e7333a54dc89e3521f35aa24d05a |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJERTk2OjE5MEQzNDozRTMyRDc6NTNFMEI3OjZBNTI5MUVBIiwidmlzaXRvcl9pZCI6IjkwNDU3MDc5NDE4OTA2NTg3OTQiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 6ed0991eb0a4c80cfe66243ac3bd5895b19d8ccefdd64197e2b414eaf3ccd0ce |
| hovercard-subject-tag | pull_request:3995472797 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/modelstudioai/openwork/pull/61/files |
| twitter:image | https://avatars.githubusercontent.com/u/52599892?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/52599892?s=400&v=4 |
| og:image:alt | Closes #60 What & why OpenWork's chat composer (FreeFormInput) gave no feedback about how much you've written. The toolbar already surfaces a context-usage indicator, a model picker, a ... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | b9a586c06a05a7a86fc7e3f4dbd03e42f6869085879aa184aa6369456dbd50fb |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/modelstudioai/openwork git https://github.com/modelstudioai/openwork.git |
| octolytics-dimension-user_id | 283556804 |
| octolytics-dimension-user_login | modelstudioai |
| octolytics-dimension-repository_id | 1256697754 |
| octolytics-dimension-repository_nwo | modelstudioai/openwork |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1256697754 |
| octolytics-dimension-repository_network_root_nwo | modelstudioai/openwork |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 07a982c1d40157c619b364352b704c3ce66bb332 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width