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");
});
+2 -2
View File
@@ -4,8 +4,8 @@ Requires Paseo 0.13.0 or later. Install this directory with
`paseo plugin install /absolute/path/to/plugin-examples/pdf-panel`, then open a `.pdf` in Files.
The panel resolves the file's local URL with `useFileUrl`, shows `Loading` until it does, and passes
it to `DocumentFrame` on web and iOS. Android shows the filename, size, and a message that inline
PDF rendering is unavailable. The plugin does not construct daemon URLs.
it to `DocumentFrame`. Where a platform cannot show a PDF inline (Android), `DocumentFrame` says so.
The plugin does not construct daemon URLs.
See the [file panel reference](https://paseo.sh/docs/plugins/reference#file-panels).
+1 -17
View File
@@ -10,12 +10,7 @@ const messageStyle = {
padding: 24,
} as const;
export function PdfPanel(props: PluginFilePanelProps) {
if (props.layout.platform === "android") return <AndroidPdf {...props} />;
return <InlinePdf {...props} />;
}
function InlinePdf({ file, theme }: PluginFilePanelProps) {
export function PdfPanel({ file, theme }: PluginFilePanelProps) {
const url = useFileUrl(file);
const textStyle = useMemo(() => ({ color: theme.colors.statusDanger }), [theme]);
if (url.status === "loading") return <Loading />;
@@ -28,14 +23,3 @@ function InlinePdf({ file, theme }: PluginFilePanelProps) {
}
return <DocumentFrame url={url.url} testID="pdf-panel" />;
}
function AndroidPdf({ file, theme }: PluginFilePanelProps) {
const textStyle = useMemo(() => ({ color: theme.colors.foregroundMuted }), [theme]);
return (
<View style={messageStyle}>
<Text style={textStyle}>
{file.name} ({Math.round(file.size / 1024)} KB). Android cannot render PDF inline.
</Text>
</View>
);
}