diff --git a/README.md b/README.md index ad456b0..4ffa70e 100644 --- a/README.md +++ b/README.md @@ -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 `
.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 |
diff --git a/changelogs/CHANGELOG-calculator-display-paste.md b/changelogs/CHANGELOG-calculator-display-paste.md
new file mode 100644
index 0000000..11cc8a5
--- /dev/null
+++ b/changelogs/CHANGELOG-calculator-display-paste.md
@@ -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 |
diff --git a/js/templates/tools.js b/js/templates/tools.js
index 63112f4..a5d587f 100644
--- a/js/templates/tools.js
+++ b/js/templates/tools.js
@@ -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;
@@ -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') {