fix(plugins): file events are objects, deletion is a typed error, DocumentFrame owns its fallback

- watch listeners receive { type: "changed" } | { type: "deleted" }, so later
  versions can add fields without breaking them.
- PluginFileDeletedError joins PluginFileConflictError. The read hooks report a
  deleted file with it, and a write over a deleted file rejects with it; Source
  shows that as the deleted notice. PluginFileConflictError now means only
  "changed on disk".
- DocumentFrame on Android shows Paseo's "<TYPE> files can't be shown on this
  device" message for documents its WebView cannot display, so the PDF example
  is useFileUrl + Loading + DocumentFrame with no platform branch.
This commit is contained in:
Mohamed Boudra
2026-10-20 08:18:20 +02:00
parent cd102d08b6
commit 58edbdcd0e
22 changed files with 168 additions and 56 deletions
@@ -1,5 +1,6 @@
import {
PluginFileConflictError,
PluginFileDeletedError,
type PluginFilePanelProps,
useFileText,
} from "@getpaseo/plugin/client";
@@ -47,6 +48,7 @@ function MarkdownEditor({ file, tab, theme }: PluginFilePanelProps) {
await file.writeText(next, { force });
} catch (cause) {
if (cause instanceof PluginFileConflictError) setState("conflict");
else if (cause instanceof PluginFileDeletedError) setState("deleted");
else setError(messageOf(cause));
return;
}
@@ -77,7 +79,7 @@ function MarkdownEditor({ file, tab, theme }: PluginFilePanelProps) {
useEffect(() => {
void load();
const unwatch = file.watch((event) => {
if (event === "deleted") setState("deleted");
if (event.type === "deleted") setState("deleted");
else if (unsaved.current === null) void load();
else setState("conflict");
});