mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 06:29:42 +02:00
fix(mobile): merge-time fixes for the prompt composer (#444)
- styles.css: restate the composer overlay's own bottom gutter after the fold rules (the generic .paste-overlay longhand erased it: 0px flat, hinge strip replacing it folded) and subtract the fold strip from the dialog's max-height - test/foldable-layout.test.ts: simulate the cascade for .paste-overlay.prompt-composer-overlay (fails without the CSS fix); pin the palette anchor by name instead of ELEMENTS.at(-1) - keyboard-accessory.js: guard the app global in refreshForActiveSession() like the rest of the file - keyboard-accessory.js: a whitespace-only draft is empty (Send no longer submits blank lines); the text still goes out untrimmed - keyboard-accessory.js: derive _composerMaxLength and the frame refusal from one 64 KiB frame limit minus both bracketed-paste markers so they cannot drift - keyboard-accessory.js: translate the textarea placeholder and label at build time, since the DOM translator skips <textarea> subtrees - i18n.js: zh-CN entries for the composer dialog copy - docs/wiki/Mobile-Guide.md: describe the Compose key instead of a clipboard key - CLAUDE.md: a "Mobile prompt composer" paragraph after the accessory bar one - test/mobile-prompt-composer.test.ts: pin the whitespace rule and the derived budget Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit f6725ba52da17b0bdbee8be3b5011e7cae514f69)
This commit is contained in:
@@ -313,11 +313,17 @@ describe('fold reserved region: every centred overlay is covered', () => {
|
||||
/**
|
||||
* The elements whose padding cascade is simulated: every derived overlay as
|
||||
* a bare element, plus the open command palette, which is a `.modal` wearing
|
||||
* two more classes and the one overlay mobile.css pads with a shorthand.
|
||||
* two more classes and the one overlay mobile.css pads with a shorthand, plus
|
||||
* the mobile prompt composer, a `.paste-overlay` wearing a second class that
|
||||
* carries its own `padding` shorthand. The derived list cannot see the
|
||||
* composer (it inherits the centring declarations rather than declaring
|
||||
* them), and simulating `['paste-overlay']` alone stayed green while the
|
||||
* generic `.paste-overlay` fold rule erased the composer's bottom gutter.
|
||||
*/
|
||||
const ELEMENTS: { name: string; classes: string[] }[] = [
|
||||
...CENTRED_OVERLAYS.map((o) => ({ name: o.selector, classes: classCompound(o.selector)! })),
|
||||
{ name: '.modal.command-palette-modal.active', classes: ['modal', 'command-palette-modal', 'active'] },
|
||||
{ name: '.paste-overlay.prompt-composer-overlay', classes: ['paste-overlay', 'prompt-composer-overlay'] },
|
||||
];
|
||||
|
||||
it('simulates the cascade the browser measured', () => {
|
||||
@@ -330,7 +336,7 @@ describe('fold reserved region: every centred overlay is covered', () => {
|
||||
const picker = ['path-picker-overlay'];
|
||||
expect(cascadedPadding(picker, 'right', 393, false)).toBe('0');
|
||||
expect(cascadedPadding(picker, 'right', 626, false)).toBe('16px');
|
||||
const palette = ELEMENTS.at(-1)!.classes;
|
||||
const palette = ELEMENTS.find((e) => e.name === '.modal.command-palette-modal.active')!.classes;
|
||||
expect(cascadedPadding(palette, 'right', 393, false)).toBeNull();
|
||||
expect(cascadedPadding(palette, 'right', 626, false)).toBe('0.75rem');
|
||||
expect(cascadedPadding(palette, 'bottom', 626, false)).toBe('0');
|
||||
|
||||
@@ -299,6 +299,32 @@ describe('mobile prompt composer', () => {
|
||||
expect(bar._composerDrafts.get('session-1')).toHaveLength(65525);
|
||||
});
|
||||
|
||||
it('treats a whitespace-only draft as empty instead of submitting blank lines', () => {
|
||||
const { app, bar, document } = loadComposer();
|
||||
bar.composePrompt();
|
||||
textarea(document).value = ' \n\n ';
|
||||
|
||||
(document.querySelector('.paste-send') as HTMLButtonElement).click();
|
||||
|
||||
expect(app._sendInputAsync).not.toHaveBeenCalled();
|
||||
expect(document.querySelector('.prompt-composer-overlay')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('derives the prompt budget from the 64 KiB input frame minus both paste markers', () => {
|
||||
// ws-routes.ts drops a frame longer than MAX_INPUT_LENGTH without an ACK,
|
||||
// so a prompt of exactly the budget must produce a frame of exactly 64 KiB.
|
||||
const { app, bar, document } = loadComposer();
|
||||
expect(bar._composerMaxLength).toBe(64 * 1024 - '\x1b[200~\x1b[201~'.length);
|
||||
bar.composePrompt();
|
||||
textarea(document).value = 'y'.repeat(bar._composerMaxLength);
|
||||
|
||||
(document.querySelector('.paste-send') as HTMLButtonElement).click();
|
||||
|
||||
expect(app._sendInputAsync).toHaveBeenCalledOnce();
|
||||
expect((app._sendInputAsync.mock.calls[0][1] as string).length).toBe(64 * 1024);
|
||||
expect(app.showToast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('preserves the draft and focuses xterm when Use terminal keyboard is chosen', () => {
|
||||
const { app, bar, document, localEcho, runTimers } = loadComposer();
|
||||
const composeButton = mountComposeButton(bar, document);
|
||||
|
||||
Reference in New Issue
Block a user