performance improvements

This commit is contained in:
2026-05-14 17:52:13 +02:00
parent 6bc51d6d14
commit 046f60dcb6
9 changed files with 299 additions and 134 deletions
+40 -8
View File
@@ -100,6 +100,14 @@ export default function Editor({ editSlug }: Props) {
const [isDragging, setIsDragging] = useState(false);
const [uploadingCount, setUploadingCount] = useState(0);
const dragDepthRef = useRef(0);
const assetsCacheRef = useRef<Asset[] | null>(null);
async function getCachedAssets(): Promise<Asset[]> {
if (assetsCacheRef.current) return assetsCacheRef.current;
const assets = await getAssets();
assetsCacheRef.current = assets;
return assets;
}
function showAlertMsg(msg: string, type: 'success' | 'error') {
setAlert({ msg, type });
@@ -245,7 +253,7 @@ export default function Editor({ editSlug }: Props) {
async function triggerAutocomplete(view: EditorView) {
try {
const assets = await getAssets();
const assets = await getCachedAssets();
setAutocompleteAssets(assets.slice(0, 8));
const pos = view.state.selection.main.head;
const coords = view.coordsAtPos(pos);
@@ -292,22 +300,41 @@ export default function Editor({ editSlug }: Props) {
return;
}
setUploadingCount(c => c + images.length);
// Fire all uploads in parallel; the browser caps per-origin concurrency.
// Insert results in submission order so the markdown reflects user intent.
const uploads = images.map(file =>
uploadAsset(file).then(
asset => ({ ok: true as const, asset }),
err => ({ ok: false as const, err }),
),
);
let pos = typeof insertAt === 'number' ? insertAt : view.state.selection.main.head;
for (const file of images) {
try {
const asset = await uploadAsset(file);
const newAssets: Asset[] = [];
for (const promise of uploads) {
const result = await promise;
setUploadingCount(c => Math.max(0, c - 1));
if (result.ok) {
const { asset } = result;
newAssets.push(asset);
const md = `![${asset.name}](${asset.url})`;
const line = view.state.doc.lineAt(pos);
const atLineEnd = pos === line.to;
const insertText = atLineEnd ? `\n\n${md}\n` : `${md}\n\n`;
view.dispatch({ changes: { from: pos, insert: insertText } });
pos += insertText.length;
} catch (e) {
} else {
const e = result.err;
showAlertMsg(e instanceof ApiError ? `Upload failed: ${e.message}` : 'Upload failed.', 'error');
} finally {
setUploadingCount(c => Math.max(0, c - 1));
}
}
if (newAssets.length > 0) {
assetsCacheRef.current = assetsCacheRef.current
? [...newAssets, ...assetsCacheRef.current]
: null;
}
view.focus();
}
@@ -316,6 +343,11 @@ export default function Editor({ editSlug }: Props) {
setShowModal(false);
}
function closeAssetModal() {
assetsCacheRef.current = null;
setShowModal(false);
}
async function handleSave() {
const content = viewRef.current?.state.doc.toString() || '';
if (!title.trim() || !slug || !content) {
@@ -647,7 +679,7 @@ export default function Editor({ editSlug }: Props) {
<h2 className="font-display italic text-2xl md:text-3xl text-[var(--text)] leading-tight">Add image</h2>
<p className="text-xs text-[var(--subtext0)] font-display italic mt-1">Click an image to insert it. Drag new files in to upload.</p>
</div>
<button onClick={() => setShowModal(false)} className="p-2 text-[var(--subtext0)] hover:text-[var(--red)] transition-colors">
<button onClick={closeAssetModal} className="p-2 text-[var(--subtext0)] hover:text-[var(--red)] transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
</button>
</header>