[READ-ONLY] Mirror of https://github.com/improsocial/impro An extensible Bluesky client for web impro.social
6

Configure Feed

Select the types of activity you want to include in your feed.

Fix ios share sheet paste

Grace Kind (Jul 18, 2026, 4:06 PM -0500) 978d1236 3a1e6ada

+54 -4
+1 -1
package.json
··· 1 1 { 2 2 "name": "impro", 3 - "version": "0.18.7", 3 + "version": "0.18.8", 4 4 "type": "module", 5 5 "scripts": { 6 6 "start": "rm -rf \"${BUILD_DIR:-build}\" && NODE_ENV=development eleventy --serve",
+13 -3
src/js/components/rich-text-input.js
··· 12 12 "app.bsky.richtext.facet#tag", 13 13 ]); 14 14 15 + function parseUriList(uriList) { 16 + return uriList 17 + .split(/\r?\n/) 18 + .filter((line) => line.length > 0 && !line.startsWith("#")) 19 + .join("\n"); 20 + } 21 + 15 22 function facetsOverlap(a, b) { 16 23 return ( 17 24 a.index.byteStart < b.index.byteEnd && a.index.byteEnd > b.index.byteStart ··· 443 450 @paste=${(e) => { 444 451 e.preventDefault(); 445 452 // https://stackoverflow.com/a/58980415 446 - const text = (e.clipboardData || window.clipboardData).getData( 447 - "text/plain", 448 - ); 453 + const clipboard = e.clipboardData || window.clipboardData; 454 + // iOS share-sheet links arrive as text/uri-list with no 455 + // text/plain representation 456 + const text = 457 + clipboard.getData("text/plain") || 458 + parseUriList(clipboard.getData("text/uri-list")); 449 459 // execCommand fires the input event synchronously, so 450 460 // handleInput runs while this flag is set 451 461 this.isPasting = true;
+40
tests/unit/specs/components/rich-text-input.test.js
··· 204 204 assert.deepEqual(details[0].inputType, "insertFromPaste"); 205 205 }); 206 206 207 + it("falls back to text/uri-list when text/plain is empty on paste", () => { 208 + const element = document.createElement("rich-text-input"); 209 + document.body.appendChild(element); 210 + const input = element.querySelector(".rich-text-input"); 211 + 212 + const details = []; 213 + element.addEventListener("input", (event) => { 214 + details.push(event.detail); 215 + }); 216 + 217 + const originalExecCommand = document.execCommand; 218 + document.execCommand = (name, _ui, value) => { 219 + input.textContent = input.textContent + value; 220 + input.dispatchEvent(new window.InputEvent("input")); 221 + return true; 222 + }; 223 + try { 224 + const pasteEvent = new window.Event("paste", { 225 + bubbles: true, 226 + cancelable: true, 227 + }); 228 + pasteEvent.clipboardData = { 229 + getData: (type) => 230 + type === "text/uri-list" 231 + ? "# copied from share sheet\r\nhttps://example.com/a\r\nhttps://example.com/b" 232 + : "", 233 + }; 234 + input.dispatchEvent(pasteEvent); 235 + } finally { 236 + document.execCommand = originalExecCommand; 237 + } 238 + 239 + assert.deepEqual(details.length, 1); 240 + assert.deepEqual( 241 + details[0].text, 242 + "https://example.com/a\nhttps://example.com/b", 243 + ); 244 + assert.deepEqual(details[0].inputType, "insertFromPaste"); 245 + }); 246 + 207 247 it("passes the native inputType through in the input detail", () => { 208 248 const element = document.createElement("rich-text-input"); 209 249 document.body.appendChild(element);