split global.css

This commit is contained in:
2026-05-17 14:44:45 +02:00
parent ac99cc724a
commit 93fdb8d1fc
19 changed files with 3605 additions and 3397 deletions
@@ -0,0 +1,74 @@
import { type RefObject, useEffect, useState } from 'react';
import type { EditorView } from '@codemirror/view';
import type { Asset } from '../../../../lib/types';
interface Opts {
getView: () => EditorView | null;
editorRef: RefObject<HTMLDivElement | null>;
getCachedAssets: () => Promise<Asset[]>;
}
/** Inline `/` or `!` asset autocomplete dropdown. */
export function useAssetAutocomplete({ getView, editorRef, getCachedAssets }: Opts) {
const [showAutocomplete, setShowAutocomplete] = useState(false);
const [autocompleteAssets, setAutocompleteAssets] = useState<Asset[]>([]);
const [autocompletePos, setAutocompletePos] = useState({ top: 0, left: 0 });
async function triggerAutocomplete(view: EditorView) {
try {
const assets = await getCachedAssets();
setAutocompleteAssets(assets.slice(0, 8));
const pos = view.state.selection.main.head;
const coords = view.coordsAtPos(pos);
if (coords) {
const editorRect = editorRef.current?.getBoundingClientRect();
if (editorRect) {
setAutocompletePos({
top: coords.bottom - editorRect.top + 4,
left: coords.left - editorRect.left,
});
}
}
setShowAutocomplete(true);
} catch {
/* ignore */
}
}
function insertAssetMarkdown(asset: Asset) {
const view = getView();
if (!view) return;
const isImage = /\.(jpg|jpeg|png|webp|gif|svg)$/i.test(asset.name);
const md = isImage ? `![${asset.name}](${asset.url})` : `[${asset.name}](${asset.url})`;
const pos = view.state.selection.main.head;
const line = view.state.doc.lineAt(pos);
const textBefore = line.text.slice(0, pos - line.from);
const triggerIdx = Math.max(textBefore.lastIndexOf('/'), textBefore.lastIndexOf('!'));
if (triggerIdx !== -1) {
const from = line.from + triggerIdx;
view.dispatch({ changes: { from, to: pos, insert: md } });
} else {
view.dispatch({ changes: { from: pos, insert: md } });
}
view.focus();
setShowAutocomplete(false);
}
useEffect(() => {
if (!showAutocomplete) return;
const handler = () => setShowAutocomplete(false);
window.addEventListener('click', handler);
return () => window.removeEventListener('click', handler);
}, [showAutocomplete]);
return {
showAutocomplete,
setShowAutocomplete,
autocompleteAssets,
autocompletePos,
triggerAutocomplete,
insertAssetMarkdown,
};
}