frontend for xcvr appview

change to diff, probs break things

+36 -92
+8 -1
package-lock.json
··· 10 10 "dependencies": { 11 11 "@floating-ui/dom": "^1.7.2", 12 12 "@protobuf-ts/runtime": "^2.11.0", 13 - "@rachel-mp4/lrcproto": "^1.0.3" 13 + "@rachel-mp4/lrcproto": "^1.0.3", 14 + "fast-diff": "^1.3.0" 14 15 }, 15 16 "devDependencies": { 16 17 "@sveltejs/adapter-auto": "^6.0.0", ··· 1131 1132 "dependencies": { 1132 1133 "@jridgewell/sourcemap-codec": "^1.4.15" 1133 1134 } 1135 + }, 1136 + "node_modules/fast-diff": { 1137 + "version": "1.3.0", 1138 + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", 1139 + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", 1140 + "license": "Apache-2.0" 1134 1141 }, 1135 1142 "node_modules/fdir": { 1136 1143 "version": "6.4.5",
+2 -1
package.json
··· 24 24 "dependencies": { 25 25 "@floating-ui/dom": "^1.7.2", 26 26 "@protobuf-ts/runtime": "^2.11.0", 27 - "@rachel-mp4/lrcproto": "^1.0.3" 27 + "@rachel-mp4/lrcproto": "^1.0.3", 28 + "fast-diff": "^1.3.0" 28 29 } 29 30 }
+2 -3
src/lib/components/AutoGrowTextArea.svelte
··· 3 3 4 4 interface Props { 5 5 onBeforeInput?: (event: InputEvent) => void; 6 - onInput?: (event: Event) => void; 6 + onInput?: (event: InputEvent) => void; 7 7 placeholder?: string; 8 8 value?: string; 9 9 maxlength?: number; ··· 23 23 24 24 let inputEl: HTMLElement; 25 25 function adjust(event: Event) { 26 - onInput?.(event); 26 + onInput?.(event as InputEvent); 27 27 } 28 28 29 29 function adjustHeight() { ··· 62 62 border: none; 63 63 } 64 64 </style> 65 -
+24 -87
src/lib/components/Transmitter.svelte
··· 3 3 import { WSContext } from "$lib/wscontext.svelte"; 4 4 import { numToHex } from "$lib/colors"; 5 5 import AutoGrowTextArea from "$lib/components/AutoGrowTextArea.svelte"; 6 + import diff from "fast-diff"; 6 7 import { getPrevCharBoundary, getNextCharBoundary } from "$lib/utils"; 7 8 interface Props { 8 9 ctx: WSContext; ··· 33 34 }); 34 35 35 36 let color = $derived(numToHex(ctx.color)); 37 + const diffAndSend = (event: InputEvent) => { 38 + const el = event.target as HTMLInputElement; 39 + const result = diff(message, el.value); 40 + var idx = 0; 41 + result.forEach((d) => { 42 + switch (d[0]) { 43 + case -1: 44 + const idx2 = idx + d[1].length; 45 + ctx.delete(idx, idx2); 46 + break; 47 + case 0: 48 + idx = idx + d[1].length; 49 + break; 50 + case 1: 51 + ctx.insert(idx, d[1]); 52 + idx = idx + d[1].length; 53 + break; 54 + } 55 + }); 56 + message = el.value; 57 + }; 36 58 37 59 const bi = (event: InputEvent) => { 38 60 const el = event.target as HTMLInputElement; ··· 45 67 ctx.insertLineBreak(); 46 68 el.value = ""; 47 69 event.preventDefault(); 48 - return; 49 - } 50 - 51 - case "insertText": { 52 - const { selectionStart } = el; 53 - const { selectionEnd } = el; 54 - 55 - if ( 56 - selectionStart !== selectionEnd && 57 - selectionEnd !== null && 58 - selectionStart !== null 59 - ) { 60 - ctx.delete(selectionStart, selectionEnd); 61 - } 62 - ctx.insert(selectionStart ?? 0, event.data ?? ""); 63 - return; 64 - } 65 - 66 - case "insertFromPaste": { 67 - const { selectionStart } = el; 68 - const { selectionEnd } = el; 69 - if ( 70 - selectionStart !== selectionEnd && 71 - selectionEnd !== null && 72 - selectionStart !== null 73 - ) { 74 - ctx.delete(selectionStart, selectionEnd); 75 - } 76 - ctx.insert(selectionStart ?? 0, event.data ?? ""); 70 + message = ""; 77 71 return; 78 72 } 79 - 80 - case "deleteContent": { 81 - const { selectionStart } = el; 82 - const { selectionEnd } = el; 83 - if ( 84 - selectionStart !== selectionEnd && 85 - selectionStart !== null && 86 - selectionEnd !== null 87 - ) { 88 - ctx.delete(selectionStart, selectionEnd); 89 - } 90 - } 91 - 92 - case "deleteContentBackward": { 93 - const { selectionStart } = el; 94 - const { selectionEnd } = el; 95 - if (selectionStart !== null && selectionEnd !== null) { 96 - if (selectionStart !== selectionEnd) { 97 - ctx.delete(selectionStart, selectionEnd); 98 - } else if (selectionStart !== 0) { 99 - event.preventDefault(); 100 - const realStart = getPrevCharBoundary( 101 - el.value, 102 - selectionStart, 103 - ); 104 - ctx.delete(realStart, selectionEnd); 105 - el.value = 106 - el.value.slice(0, realStart) + 107 - el.value.slice(selectionEnd); 108 - el.setSelectionRange(realStart, realStart); 109 - } 110 - } 111 - } 112 - 113 - case "deleteContentForward": { 114 - const { selectionStart } = el; 115 - const { selectionEnd } = el; 116 - if (selectionStart !== null && selectionEnd !== null) { 117 - if (selectionStart !== selectionEnd) { 118 - ctx.delete(selectionStart, selectionEnd); 119 - } else if (selectionEnd !== el.value.length) { 120 - event.preventDefault(); 121 - const realEnd = getNextCharBoundary( 122 - el.value, 123 - selectionEnd, 124 - ); 125 - ctx.delete(selectionStart, realEnd); 126 - el.value = 127 - el.value.slice(0, selectionStart) + 128 - el.value.slice(realEnd); 129 - el.setSelectionRange(selectionStart, selectionStart); 130 - } 131 - } 132 - } 133 - 134 - case "historyUndo": { 135 - } 136 73 } 137 74 }; 138 75 </script> ··· 166 103 /> 167 104 </div> 168 105 <AutoGrowTextArea 169 - bind:value={message} 170 106 placeholder="start typing..." 171 107 onBeforeInput={bi} 108 + onInput={diffAndSend} 172 109 maxlength={65535} 173 110 /> 174 111 </div>