@omercnet/paseo-omp 5.1.1-next.340.1 → 5.1.1-next.343.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PluginTimelineItemProps } from "@getpaseo/plugin/client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { Modal, ScrollView } from "@getpaseo/plugin/client/ui";
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { Image, Pressable, Text, View } from "react-native";
|
|
4
5
|
import { type OmpImageTimelineData, visibleOmpImageText } from "../shared/provider-image";
|
|
5
6
|
|
|
6
7
|
export function OmpImageTimeline({ item, theme }: PluginTimelineItemProps<OmpImageTimelineData>) {
|
|
@@ -31,6 +32,8 @@ export function OmpImageTimeline({ item, theme }: PluginTimelineItemProps<OmpIma
|
|
|
31
32
|
}),
|
|
32
33
|
[theme],
|
|
33
34
|
);
|
|
35
|
+
const [enlargedId, setEnlargedId] = useState<string | null>(null);
|
|
36
|
+
const enlarged = item.data.images.find((image) => image.id === enlargedId);
|
|
34
37
|
const [failedImageIds, setFailedImageIds] = useState<ReadonlySet<string>>(() => new Set());
|
|
35
38
|
const visibleText = useMemo(() => visibleOmpImageText(item.data.text), [item.data.text]);
|
|
36
39
|
return (
|
|
@@ -46,21 +49,131 @@ export function OmpImageTimeline({ item, theme }: PluginTimelineItemProps<OmpIma
|
|
|
46
49
|
</Text>
|
|
47
50
|
</View>
|
|
48
51
|
) : (
|
|
49
|
-
<
|
|
52
|
+
<Pressable
|
|
50
53
|
key={image.id}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
accessibilityRole="button"
|
|
55
|
+
accessibilityLabel={`Enlarge ${item.data.label} image ${index + 1}`}
|
|
56
|
+
onPress={() => setEnlargedId(image.id)}
|
|
57
|
+
>
|
|
58
|
+
<Image
|
|
59
|
+
source={{ uri: `data:${image.mimeType};base64,${image.data}` }}
|
|
60
|
+
resizeMode="contain"
|
|
61
|
+
accessibilityLabel={`${item.data.label} image ${index + 1}`}
|
|
62
|
+
onError={() =>
|
|
63
|
+
setFailedImageIds((current) => {
|
|
64
|
+
if (current.has(image.id)) return current;
|
|
65
|
+
return new Set([...current, image.id]);
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
style={styles.image}
|
|
69
|
+
/>
|
|
70
|
+
</Pressable>
|
|
62
71
|
),
|
|
63
72
|
)}
|
|
73
|
+
<Modal
|
|
74
|
+
title={item.data.label}
|
|
75
|
+
open={enlarged !== undefined}
|
|
76
|
+
onOpenChange={(open) => {
|
|
77
|
+
if (!open) setEnlargedId(null);
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
<Modal.Content scrollable={false}>
|
|
81
|
+
{enlarged ? (
|
|
82
|
+
<EnlargedImage
|
|
83
|
+
key={enlarged.id}
|
|
84
|
+
failed={failedImageIds.has(enlarged.id)}
|
|
85
|
+
uri={`data:${enlarged.mimeType};base64,${enlarged.data}`}
|
|
86
|
+
label={item.data.label}
|
|
87
|
+
theme={theme}
|
|
88
|
+
/>
|
|
89
|
+
) : null}
|
|
90
|
+
</Modal.Content>
|
|
91
|
+
</Modal>
|
|
92
|
+
</View>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Fits the image to the dialog; a tap shows it at its real pixel size, panned by scrolling. */
|
|
97
|
+
function EnlargedImage({
|
|
98
|
+
uri,
|
|
99
|
+
label,
|
|
100
|
+
failed,
|
|
101
|
+
theme,
|
|
102
|
+
}: {
|
|
103
|
+
uri: string;
|
|
104
|
+
failed: boolean;
|
|
105
|
+
label: string;
|
|
106
|
+
theme: PluginTimelineItemProps<OmpImageTimelineData>["theme"];
|
|
107
|
+
}) {
|
|
108
|
+
const [natural, setNatural] = useState<{ width: number; height: number } | null>(null);
|
|
109
|
+
const [actualSize, setActualSize] = useState(false);
|
|
110
|
+
const [renderFailed, setRenderFailed] = useState(false);
|
|
111
|
+
const [sizeUnavailable, setSizeUnavailable] = useState(false);
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
let current = true;
|
|
114
|
+
Image.getSize(
|
|
115
|
+
uri,
|
|
116
|
+
(width, height) => current && setNatural({ width, height }),
|
|
117
|
+
() => current && setSizeUnavailable(true),
|
|
118
|
+
);
|
|
119
|
+
return () => {
|
|
120
|
+
current = false;
|
|
121
|
+
};
|
|
122
|
+
}, [uri]);
|
|
123
|
+
const canZoom = natural !== null;
|
|
124
|
+
// The modal body is bounded (`scrollable={false}`), so the image and its scroller share its height
|
|
125
|
+
// with the caption instead of guessing from the window.
|
|
126
|
+
const fill = { flex: 1, minHeight: 0 };
|
|
127
|
+
const mutedText = {
|
|
128
|
+
color: theme.colors.foregroundMuted,
|
|
129
|
+
fontSize: 12,
|
|
130
|
+
textAlign: "center" as const,
|
|
131
|
+
};
|
|
132
|
+
if (failed || renderFailed) {
|
|
133
|
+
return <Text style={mutedText}>This image could not be rendered on this Paseo client.</Text>;
|
|
134
|
+
}
|
|
135
|
+
return (
|
|
136
|
+
<View style={[fill, { gap: 8 }]}>
|
|
137
|
+
<Pressable
|
|
138
|
+
accessibilityRole="button"
|
|
139
|
+
accessibilityLabel={
|
|
140
|
+
actualSize ? `Fit ${label} image to window` : `Show ${label} image at actual size`
|
|
141
|
+
}
|
|
142
|
+
disabled={!canZoom}
|
|
143
|
+
onPress={() => setActualSize((value) => !value)}
|
|
144
|
+
style={fill}
|
|
145
|
+
>
|
|
146
|
+
{actualSize && natural ? (
|
|
147
|
+
<ScrollView style={fill}>
|
|
148
|
+
<ScrollView horizontal>
|
|
149
|
+
<Image
|
|
150
|
+
source={{ uri }}
|
|
151
|
+
accessibilityLabel={`${label} enlarged image`}
|
|
152
|
+
onError={() => setRenderFailed(true)}
|
|
153
|
+
style={{ width: natural.width, height: natural.height }}
|
|
154
|
+
/>
|
|
155
|
+
</ScrollView>
|
|
156
|
+
</ScrollView>
|
|
157
|
+
) : (
|
|
158
|
+
<Image
|
|
159
|
+
source={{ uri }}
|
|
160
|
+
resizeMode="contain"
|
|
161
|
+
accessibilityLabel={`${label} enlarged image`}
|
|
162
|
+
onError={() => setRenderFailed(true)}
|
|
163
|
+
style={[fill, { width: "100%" }]}
|
|
164
|
+
/>
|
|
165
|
+
)}
|
|
166
|
+
</Pressable>
|
|
167
|
+
{canZoom ? (
|
|
168
|
+
<Text style={mutedText}>
|
|
169
|
+
{actualSize
|
|
170
|
+
? `${natural.width} × ${natural.height}px · tap to fit`
|
|
171
|
+
: `Tap the image to see it at ${natural.width} × ${natural.height}px`}
|
|
172
|
+
</Text>
|
|
173
|
+
) : null}
|
|
174
|
+
{sizeUnavailable ? (
|
|
175
|
+
<Text style={mutedText}>The actual size of this image is not available.</Text>
|
|
176
|
+
) : null}
|
|
64
177
|
</View>
|
|
65
178
|
);
|
|
66
179
|
}
|
package/package.json
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Release Please keeps this bundle-safe constant aligned with package.json. */
|
|
2
|
-
export const PASEO_OMP_PACKAGE_VERSION = "5.1.1-next.
|
|
2
|
+
export const PASEO_OMP_PACKAGE_VERSION = "5.1.1-next.343.1"; // x-release-please-version
|
|
@@ -130,6 +130,7 @@ export const OMP_RPC_COMMAND_POLICIES = {
|
|
|
130
130
|
bash: "unsupported",
|
|
131
131
|
abort_bash: "unsupported",
|
|
132
132
|
get_session_stats: "implemented",
|
|
133
|
+
get_session_lease: "unsupported",
|
|
133
134
|
export_html: "unsupported",
|
|
134
135
|
switch_session: "unsupported",
|
|
135
136
|
branch: "implemented",
|