tfrere's picture
tfrere HF Staff
feat(editor): embed studio with data files and agent-aware editing
8fc8501
Raw
History Blame Contribute Delete
6.12 kB
import { X } from "lucide-react";
import { useState, useEffect, useCallback } from "react";
import { useFrontmatter } from "./useFrontmatter";
import type { FrontmatterStore, FrontmatterData } from "./frontmatter-store";
import type * as Y from "yjs";
import { HueSlider } from "./HueSlider";
interface SettingsDrawerProps {
open: boolean;
onClose: () => void;
store: FrontmatterStore | null;
settingsMap?: Y.Map<any> | null;
}
export function SettingsDrawer({ open, onClose, store, settingsMap }: SettingsDrawerProps) {
const { data, update } = useFrontmatter(store);
const [citationStyle, setCitationStyle] = useState("apa");
const [hue, setHue] = useState(47);
useEffect(() => {
if (!settingsMap) return;
const sync = () => {
const cit = settingsMap.get("citationStyle");
if (cit) setCitationStyle(cit as string);
const h = settingsMap.get("primaryHue");
if (h !== undefined) setHue(h as number);
};
sync();
settingsMap.observe(sync);
return () => settingsMap.unobserve(sync);
}, [settingsMap]);
const handleEscape = useCallback((e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
}, [onClose]);
useEffect(() => {
if (open) {
document.addEventListener("keydown", handleEscape);
return () => document.removeEventListener("keydown", handleEscape);
}
}, [open, handleEscape]);
if (!data) return null;
return (
<>
<div className={`drawer-backdrop ${open ? "open" : ""}`} onClick={onClose} />
<aside className={`drawer-panel ${open ? "open" : ""}`}>
<div className="settings-drawer">
<div className="settings-drawer__header">
<span className="settings-drawer__title">Article settings</span>
<button className="icon-btn" onClick={onClose} aria-label="Close">
<X size={16} />
</button>
</div>
<div className="settings-drawer__body">
<FieldGroup label="Template">
<select
className="form-select"
value={data.template}
onChange={(e) => update("template", e.target.value as FrontmatterData["template"])}
>
<option value="article">Article (full layout)</option>
<option value="paper">Paper (single column)</option>
</select>
</FieldGroup>
<FieldGroup label="Description (SEO)">
<textarea
className="form-input"
rows={3}
placeholder="Short description for meta tags..."
value={data.description}
onChange={(e) => update("description", e.target.value)}
/>
</FieldGroup>
<FieldGroup label="Citation style">
<select
className="form-select"
value={citationStyle}
onChange={(e) => {
const val = e.target.value;
setCitationStyle(val);
settingsMap?.set("citationStyle", val);
}}
>
<option value="apa">APA (7th edition)</option>
<option value="ieee">IEEE</option>
<option value="vancouver">Vancouver</option>
<option value="chicago-author-date">Chicago (author-date)</option>
<option value="harvard1">Harvard</option>
</select>
</FieldGroup>
<FieldGroup label="Primary color">
<HueSlider
hue={hue}
onChange={(h) => {
setHue(h);
settingsMap?.set("primaryHue", h);
}}
/>
</FieldGroup>
<hr className="divider-h" />
<SwitchField
label="Show PDF download"
checked={data.showPdf}
onChange={(v) => update("showPdf", v)}
/>
<SwitchField
label="Auto-collapse TOC"
checked={data.tableOfContentsAutoCollapse}
onChange={(v) => update("tableOfContentsAutoCollapse", v)}
/>
<hr className="divider-h" />
<FieldGroup label="DOI">
<input
className="form-input"
placeholder="10.xxxx/xxxxx"
value={data.doi}
onChange={(e) => update("doi", e.target.value)}
/>
</FieldGroup>
<FieldGroup label="Licence">
<textarea
className="form-input"
rows={2}
placeholder="e.g. CC BY 4.0 (HTML allowed)"
value={data.licence}
onChange={(e) => update("licence", e.target.value)}
/>
</FieldGroup>
<FieldGroup label="SEO thumbnail image">
<input
className="form-input"
placeholder="https://..."
value={data.seoThumbImage}
onChange={(e) => update("seoThumbImage", e.target.value)}
/>
</FieldGroup>
<SwitchField
label="PDF Pro only"
checked={data.pdfProOnly}
onChange={(v) => update("pdfProOnly", v)}
/>
</div>
</div>
</aside>
</>
);
}
function FieldGroup({ label, children }: { label: string; children: React.ReactNode }) {
return (
<div>
<label className="field-label">{label}</label>
{children}
</div>
);
}
function SwitchField({ label, checked, onChange }: { label: string; checked: boolean; onChange: (v: boolean) => void }) {
return (
<label className="form-switch-label">
<span className="form-switch">
<input
type="checkbox"
role="switch"
checked={checked}
onChange={(e) => onChange(e.target.checked)}
/>
<span className="form-switch__track" />
<span className="form-switch__thumb" />
</span>
{label}
</label>
);
}