Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ TextAgent has undergone significant evolution since its inception. What started

| Date | Commits | Feature / Update |
|------|---------|-----------------:|
| **2026-05-14** | — | 🖥️ **Calculator — Live Expression Display + Paste-Into-Top** — calculator's small top expression line now shows the **full expression as it is typed** (every digit, operator and bracket appears immediately, e.g. `(2+3)^4*4`), while the big bottom line shows a **live partial result** evaluated on every keystroke (falls back to the current operand when the partial isn't yet valid, e.g. half-open brackets / trailing operator); fixed a paste bug on the small expression line — pasting `(2+3)^4*4` used to concatenate onto the rendered `"0"` (producing `"0(2+3)^4*4"` → `Error`); the paste handler now treats clipboard text as a complete replacement and routes straight to `applyPasted` (same code path as paste-into-result), so a single paste evaluates instantly with no follow-up Enter/blur; `commitExprEdit` skips re-evaluating when the line already matches the latest history entry, eliminating duplicate "edit" rows after a paste; `js/templates/tools.js` only — `show()` simplified (`pending` and "typing operand" branches merged) and `exprLine.paste` handler rewritten |
| **2026-05-14** | — | 🧮 **Tools Category + iOS-Style BODMAS Calculator** — new **Tools** template category (`bi-tools` icon) with a fully working calculator as the first template; iOS-style two-line display (small dim expression line above, large result line below); both lines `contenteditable` — click the expression to edit it and re-evaluate live; full **BODMAS** support with visible buttons `(`, `)`, `xʸ`, `÷`, `×`, `−`, `+`, plus `AC`, `±`, `%`, `⌫`, `.`, `=`; live running total updates the result line while typing; **History side panel** lists every calculation as `expr = result` with both cells editable — edits re-evaluate and push the latest result back to the main display, `↩` button sends any row's result to the display, `Clear` empties history; paste support for numbers and full expressions (`12*7+3`, `(2+3)^2*4`, `1,234.5`); unicode operator normalization (`×`, `÷`, `−`, commas); strict allowlist `safeEval` (`^[-+*/().\d^]+$`) with `^` translated to JS `**` for right-associative exponent precedence — no `eval()`, no identifiers, `alert(1)` rejected as `Error`; operator-aware backspace (cancels pending operator or removes one char); keyboard support for digits, `.`, `+ - * / ^ ( )`, `Backspace`, `Enter`/`=`, `Escape`; renderer round-trip safe — embedded `escapeHtml` builds entity strings via `String.fromCharCode(38)` so they survive `<pre><code>.textContent` extraction; `js/templates/tools.js` (~475 lines, single `html-autorun` block) + `tools` category pill in `modal-templates.js` + icon/color mapping in `templates.js` + dynamic import in `src/main.js`; Playwright assertion for Tools pill + Calculator card |
| **2026-04-15** | — | 🎙️ **Podcast Generation System** — new `{{@Podcast:}}` document tag for AI-powered multi-speaker podcast creation; 3-phase pipeline (web research via Jina API → AI script generation with `[Speaker]` markers → Kokoro TTS multi-speaker audio synthesis); configurable styles (debate, interview, chat, lecture, storytelling); `parseScript()` speaker segmentation; `createWavBlob()` Float32Array→WAV encoder; real-time progress UI with phase indicators; WAV audio download; **Podcast Marketplace** with 15+ curated templates across 5 categories (Tech, Science, Business, Creative, Education); search/filter, template cards with metadata; `podcast-docgen.js` (~1046 lines) + `podcast-marketplace.js` (~923 lines) + `css/podcast-docgen.css` + `css/podcast-marketplace.css` + `js/templates/podcasts.js` |
| **2026-04-15** | — | 🔧 **TTS Worker Multi-Speaker Fix** — fixed critical bug where Web Worker silently dropped `speak-multi` messages after async `init` handler completed; root cause: service worker (`sw.js`) used cache-first strategy for `.js` files, serving stale `tts-worker.js` indefinitely; fix: (1) extracted `processMultiSegments()` as standalone async function, (2) bundled segments with `init` message via `pendingSegments` field for same-handler-execution processing, (3) added cache-busting `?v=` param to worker URL, (4) excluded worker files from service worker caching, (5) bumped `CACHE_NAME` v2→v3, (6) added `worker.onerror` handler, (7) per-chunk 90s timeout, event loop yields, voice pre-fetch phase, heartbeat logger, version stamping |
Expand Down
41 changes: 41 additions & 0 deletions changelogs/CHANGELOG-calculator-display-paste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Calculator — Live-Expression Display + Paste-Into-Top-Line

- Top expression line now shows the **full expression** as you type — every digit, operator and bracket appears immediately (e.g. `(2+3)^4*4` is fully visible before pressing `=`)
- Bottom result line shows a **live running total** evaluated from the partial expression on every keystroke; falls back to the current operand when the partial isn't yet evaluable (half-open bracket, trailing operator)
- Pasting into the small top expression line now **replaces** that line and **evaluates immediately** — no more append-to-`0` artefact, no follow-up Enter/blur required
- `commitExprEdit` skips re-running `applyPasted` if the line already matches the latest history entry, preventing duplicate "edit" rows when the user clicks away after a paste
- Top + bottom lines stay in sync through paste, button input, keyboard input, and history-row edits

---

## Summary
Two small but visible behaviour changes to the Tools → Calculator template that ship as a single `html-autorun` edit:

1. **Live expression on the top line.** Previously the small dim line above the result only showed the *committed chain* (e.g. `(2+3)^4*` while the user was typing `4`). It now shows the entire expression including the current operand (`(2+3)^4*4`), matching how an iOS-style calculator would render a complete typed expression before `=` is pressed. The bottom big line continues to show a live partial-evaluation result.

2. **Paste into the small top line.** Pasting a full expression like `(2+3)^4*4` directly onto the small expression line now evaluates instantly. The previous handler concatenated pasted text onto whatever was rendered there (often `"0"`), producing `"0(2+3)^4*4"` → `Error`. Paste is now treated as a complete replacement and routed straight to `applyPasted` (same code path as paste-into-result and the keyboard Enter handler).

---

## 1. Live Expression Display
**Files:** `js/templates/tools.js`
**What:** Collapsed the `pending` and "typing an operand" branches of `show()` into a single branch that writes `chain.join('') + (pending ? '' : cur)` to the top line. The bottom line shows `safeEval(partial)` where `partial` is `chain.slice(0,-1).join('')` when an operator is pending, otherwise the full `topExpr` itself — giving a live partial result on every keystroke.
**Impact:** Users see the full expression they are typing, matching the iOS Calculator visual model. Bug squashed: previously the top line "lost" the current operand whenever a digit was typed (because `chain.join('')` excluded it).

## 2. Paste-Into-Expression-Line Fix
**Files:** `js/templates/tools.js`
**What:** The `exprLine.addEventListener('paste', …)` handler used to write `(exprLine.textContent || '').replace(/=\s*$/, '') + txt` into the DOM and rely on a later blur to commit. That concatenated the pasted text onto the initial-state `"0"` (since `show()` had set the line to the running expression). Replaced with `applyPasted(txt); exprLine.blur()` — the pasted clipboard text is routed straight to the safe evaluator, in line with how the bottom result line already handled paste.
**Impact:** Paste a complete expression into the small expression line and it evaluates instantly (`(2+3)^4*4` → top `(2+3)^4*4 =`, bottom `2500`). Whitespace, commas and unicode operators (`×`, `÷`, `−`) are normalised via the same `normalize()` call. Unsafe inputs (e.g. `alert(1)`) still get rejected as `Error` by the existing strict allowlist.

## 3. Blur Dedup
**Files:** `js/templates/tools.js`
**What:** `commitExprEdit()` now early-returns when the line's trimmed text already equals the most recent history entry's `expr`. This prevents the post-paste blur from re-running `applyPasted` and creating a duplicate history row (paste → display shows `"expr ="` → user clicks elsewhere → blur fires → re-evaluate that same `"expr"`).
**Impact:** History stays clean — one entry per logical calculation, not one per paste + one per blur.

---

## Files Changed (1 total)

| File | Lines Changed | Type |
|------|:---:|------|
| `js/templates/tools.js` | +17 −16 | `show()` simplification + paste-replaces-line + blur dedup |
33 changes: 17 additions & 16 deletions js/templates/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,24 +143,22 @@ A **history panel** on the right lists every calculation. Edit the expression *o

function show() {
// iOS-style 2-line display:
// top (small, dim) = full typed expression so far
// bottom (big) = current operand, running result, or final = result
// top (small, dim) = full expression as you type it
// bottom (big) = live running result while typing, final result after =
var topExpr, bottomNum;
if (justEvaled && chain.length === 0) {
// Just pressed =: top = "expr =", bottom = result
var last = history[history.length - 1];
topExpr = last ? (last.expr + ' =') : '';
bottomNum = cur;
} else if (pending) {
// Operator just pressed: top = chain so far, bottom = running total of chain
topExpr = chain.join('');
var partial = chain.slice(0, -1).join('');
} else {
// Typing — top shows the entire expression so far (chain + current operand).
// Bottom shows a live partial-evaluation result; falls back to the current operand
// when the expression isn't yet complete (e.g. half-open brackets, trailing operator).
topExpr = chain.join('') + (pending ? '' : cur);
var partial = pending ? chain.slice(0, -1).join('') : topExpr;
var rv = partial ? safeEval(partial) : null;
bottomNum = (rv === null || !isFinite(rv)) ? cur : String(rv);
} else {
// Typing an operand
topExpr = chain.join('');
bottomNum = cur;
}
if (exprLine) exprLine.textContent = topExpr;
if (resultLine) resultLine.textContent = bottomNum;
Expand Down Expand Up @@ -432,17 +430,20 @@ A **history panel** on the right lists every calculation. Edit the expression *o
function commitExprEdit() {
var raw = (exprLine.textContent || '').replace(/=\\s*$/, '').trim();
if (!raw) return;
// Skip if the line already matches the latest history entry's "expr =" view
// (i.e. user blurred without actually editing anything).
var last = history[history.length - 1];
if (last && last.expr === raw) return;
applyPasted(raw);
}
exprLine.addEventListener('paste', function (e) {
e.preventDefault();
var txt = (e.clipboardData || window.clipboardData).getData('text');
exprLine.textContent = (exprLine.textContent || '').replace(/=\\s*$/, '') + txt;
// Place caret at end (best-effort)
try {
var r = document.createRange(); r.selectNodeContents(exprLine); r.collapse(false);
var s = window.getSelection(); s.removeAllRanges(); s.addRange(r);
} catch (_) {}
if (!txt) return;
// Replace the line entirely and evaluate immediately — pasting a full
// expression should not require a follow-up Enter/blur.
applyPasted(txt);
exprLine.blur();
});
exprLine.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
Expand Down
Loading