mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-11-05 18:56:23 +00:00
1ad05fe9e3
* fix(web): render file names bidi-isolated everywhere they're shown (TASK-4195) A file name is data a stranger controls; a right-to-left override inside it reorders the text around it. Visible-text sites now wrap the name in <bdi> (strip tile, storage row, lightbox title/fallback/meta, editor chip NodeView, markdown file chip, upload placeholder). Attribute and toast/prompt sites wrap only the name in FSI..PDI via new isolateText / displayFilenameIsolated helpers. displayFilename, download=, data-filename, node attrs, renderHTML and the Lightbox seeds stay untouched, so nothing stored or parsed back carries an isolate. 'bdi' joins MARKDOWN_ALLOWED_TAGS (the one sanitizer change); its attributes are still stripped. Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v * test(e2e): match the isolated delete-button name in the viewer-owners spec (TASK-4195) The strip's delete button now names the file between FSI and PDI, so the substring match on "Delete <name>" no longer finds it. Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v * fix(web,render): keep img alt unisolated, and give the Go file chip the same <bdi> with a byte-parity test (TASK-4195 r1) Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v * fix(render): byte-identical escaping for attachment chips and images across Go and TS (TASK-4195 r2) html.EscapeString emits " for a double quote where the TS escapeHtml emits ", so the Go and TS renderers disagreed on any file name or alt containing one. Add a local escapeHTML helper with exactly TS's five replacements and use it at every escape site in the attachment renderers. Extend the shared parity fixture to chips, images and missing placeholders (quote, apostrophe, ampersand, angle brackets, non-ASCII, bidi controls, alt fallback), asserted byte-for-byte by Go and vitest; vitest also asserts every case array is non-empty. Claude-Session: https://claude.ai/code/session_01Pa22hG9hiVzA8nRfEWhc5y * docs(render): scope the attachment parity test's URL half to UUID ids and post-BUG-3769 slugs (TASK-4195 r3) Go url.PathEscape and TS encodeURIComponent differ on !'()* and $&+=:@. The fixture pins parity for UUID attachment ids and slugs minted since BUG-3769 only; a preserved legacy slug may encode differently but decodes to the same route. Comment-only. Claude-Session: https://claude.ai/code/session_01Pa22hG9hiVzA8nRfEWhc5y * fix(web): route both upload-error toasts through a tested uploadFailedMessage helper (TASK-4195 site 17) Editor and CommentEditor built "Couldn't upload <name>: <message>" inline, and no test mounts the full Editor's upload error path, so removing the filename isolation survived (r1-L2, mutant M14). Both now call uploadFailedMessage in editor/attachment-upload.ts. A unit row pins the isolated output and a ?raw source pin asserts both sites call the helper. Claude-Session: https://claude.ai/code/session_01Pa22hG9hiVzA8nRfEWhc5y * docs(web): say where uploadFailedMessage's message comes from (TASK-4195 r5-L1) It is the upload call's Error.message, which may be the server's text; that text names at most an extension from the fixed map, never the file name. Comment-only. Claude-Session: https://claude.ai/code/session_01Pa22hG9hiVzA8nRfEWhc5y * docs(web): drop the reachability claim from uploadFailedMessage's comment (TASK-4195 r6-L1) A malformed multipart body makes the server echo the bad header line, so "never the file name" was false for non-browser clients. The comment now says only what holds: message is shown as received, not isolated. Comment-only. Claude-Session: https://claude.ai/code/session_01Pa22hG9hiVzA8nRfEWhc5y * test(web): match the site-17 test comment to the helper's (TASK-4195 r7-L1) "the server's message is ours" implied trusted text; it is shown as received. Comment-only. Claude-Session: https://claude.ai/code/session_01Pa22hG9hiVzA8nRfEWhc5y