| ... | @@ -106,6 +106,7 @@ import { | ... | @@ -106,6 +106,7 @@ import { |
| 106 | resolveFilePreviewPath, | 106 | resolveFilePreviewPath, |
| 107 | setMarkdownTaskChecked, | 107 | setMarkdownTaskChecked, |
| 108 | shouldShowFileExplorer, | 108 | shouldShowFileExplorer, |
| | 109 | workspaceAssetResource, |
| 109 | } from "./filePreviewMode"; | 110 | } from "./filePreviewMode"; |
| 110 | import { useFileSaveCoordinator } from "./useFileSaveCoordinator"; | 111 | import { useFileSaveCoordinator } from "./useFileSaveCoordinator"; |
| 111 | import { | 112 | import { |
| ... | @@ -147,18 +148,23 @@ type FilePostRender = <LAnnotation>( | ... | @@ -147,18 +148,23 @@ type FilePostRender = <LAnnotation>( |
| 147 | function WorkspaceImagePreview(props: { | 148 | function WorkspaceImagePreview(props: { |
| 148 | readonly environmentId: EnvironmentId; | 149 | readonly environmentId: EnvironmentId; |
| 149 | readonly threadRef: ScopedThreadRef; | 150 | readonly threadRef: ScopedThreadRef; |
| | 151 | /** The thread is a draft the server does not know yet. */ |
| | 152 | readonly draft: boolean; |
| 150 | readonly absolutePath: string; | 153 | readonly absolutePath: string; |
| 151 | readonly workspaceRoot: string; | 154 | readonly workspaceRoot: string; |
| 152 | readonly alt: string; | 155 | readonly alt: string; |
| 153 | readonly workspaceMutationId: string | null; | 156 | readonly workspaceMutationId: string | null; |
| 154 | }) { | 157 | }) { |
| 155 | const resource = useMemo( | 158 | const resource = useMemo( |
| 156 | () => ({ | 159 | () => |
| 157 | _tag: "workspace-file" as const, | 160 | workspaceAssetResource({ |
| 158 | threadId: props.threadRef.threadId, | 161 | kind: "workspace-file", |
| 159 | path: props.absolutePath, | 162 | threadRef: props.threadRef, |
| 160 | }), | 163 | draft: props.draft, |
| 161 | [props.threadRef.threadId, props.absolutePath], | 164 | workspaceRoot: props.workspaceRoot, |
| | 165 | absolutePath: props.absolutePath, |
| | 166 | }), |
| | 167 | [props.threadRef, props.draft, props.workspaceRoot, props.absolutePath], |
| 162 | ); | 168 | ); |
| 163 | const assetUrl = useAssetUrlState(props.environmentId, resource); | 169 | const assetUrl = useAssetUrlState(props.environmentId, resource); |
| 164 | const [failedUrl, setFailedUrl] = useState<string | null>(null); | 170 | const [failedUrl, setFailedUrl] = useState<string | null>(null); |
| ... | @@ -213,6 +219,8 @@ function WorkspaceImagePreview(props: { | ... | @@ -213,6 +219,8 @@ function WorkspaceImagePreview(props: { |
| 213 | function WorkspaceBrowserPreview(props: { | 219 | function WorkspaceBrowserPreview(props: { |
| 214 | readonly environmentId: EnvironmentId; | 220 | readonly environmentId: EnvironmentId; |
| 215 | readonly threadRef: ScopedThreadRef; | 221 | readonly threadRef: ScopedThreadRef; |
| | 222 | /** The thread is a draft the server does not know yet. */ |
| | 223 | readonly draft: boolean; |
| 216 | readonly absolutePath: string; | 224 | readonly absolutePath: string; |
| 217 | readonly workspaceRoot: string; | 225 | readonly workspaceRoot: string; |
| 218 | readonly title: string; | 226 | readonly title: string; |
| ... | @@ -221,12 +229,15 @@ function WorkspaceBrowserPreview(props: { | ... | @@ -221,12 +229,15 @@ function WorkspaceBrowserPreview(props: { |
| 221 | const insideWorkspace = | 229 | const insideWorkspace = |
| 222 | mediaFileReference(props.absolutePath, props.workspaceRoot).relativePath !== undefined; | 230 | mediaFileReference(props.absolutePath, props.workspaceRoot).relativePath !== undefined; |
| 223 | const resource = useMemo( | 231 | const resource = useMemo( |
| 224 | () => ({ | 232 | () => |
| 225 | _tag: insideWorkspace ? ("workspace-file" as const) : ("media-file" as const), | 233 | workspaceAssetResource({ |
| 226 | threadId: props.threadRef.threadId, | 234 | kind: insideWorkspace ? "workspace-file" : "media-file", |
| 227 | path: props.absolutePath, | 235 | threadRef: props.threadRef, |
| 228 | }), | 236 | draft: props.draft, |
| 229 | [insideWorkspace, props.threadRef.threadId, props.absolutePath], | 237 | workspaceRoot: props.workspaceRoot, |
| | 238 | absolutePath: props.absolutePath, |
| | 239 | }), |
| | 240 | [insideWorkspace, props.threadRef, props.draft, props.workspaceRoot, props.absolutePath], |
| 230 | ); | 241 | ); |
| 231 | const assetUrl = useAssetUrlState(props.environmentId, resource); | 242 | const assetUrl = useAssetUrlState(props.environmentId, resource); |
| 232 | const revisionSuffix = | 243 | const revisionSuffix = |
| ... | @@ -260,18 +271,23 @@ function WorkspaceBrowserPreview(props: { | ... | @@ -260,18 +271,23 @@ function WorkspaceBrowserPreview(props: { |
| 260 | function WorkspaceVideoPreview(props: { | 271 | function WorkspaceVideoPreview(props: { |
| 261 | readonly environmentId: EnvironmentId; | 272 | readonly environmentId: EnvironmentId; |
| 262 | readonly threadRef: ScopedThreadRef; | 273 | readonly threadRef: ScopedThreadRef; |
| | 274 | /** The thread is a draft the server does not know yet. */ |
| | 275 | readonly draft: boolean; |
| 263 | readonly absolutePath: string; | 276 | readonly absolutePath: string; |
| 264 | readonly workspaceRoot: string; | 277 | readonly workspaceRoot: string; |
| 265 | readonly name: string; | 278 | readonly name: string; |
| 266 | readonly workspaceMutationId: string | null; | 279 | readonly workspaceMutationId: string | null; |
| 267 | }) { | 280 | }) { |
| 268 | const resource = useMemo( | 281 | const resource = useMemo( |
| 269 | () => ({ | 282 | () => |
| 270 | _tag: "media-file" as const, | 283 | workspaceAssetResource({ |
| 271 | threadId: props.threadRef.threadId, | 284 | kind: "media-file", |
| 272 | path: props.absolutePath, | 285 | threadRef: props.threadRef, |
| 273 | }), | 286 | draft: props.draft, |
| 274 | [props.threadRef.threadId, props.absolutePath], | 287 | workspaceRoot: props.workspaceRoot, |
| | 288 | absolutePath: props.absolutePath, |
| | 289 | }), |
| | 290 | [props.threadRef, props.draft, props.workspaceRoot, props.absolutePath], |
| 275 | ); | 291 | ); |
| 276 | const assetUrl = useAssetUrlState(props.environmentId, resource); | 292 | const assetUrl = useAssetUrlState(props.environmentId, resource); |
| 277 | const refreshAssetUrl = useAssetUrlRefresh(props.environmentId, resource); | 293 | const refreshAssetUrl = useAssetUrlRefresh(props.environmentId, resource); |
| ... | @@ -314,17 +330,23 @@ function WorkspaceVideoPreview(props: { | ... | @@ -314,17 +330,23 @@ function WorkspaceVideoPreview(props: { |
| 314 | function WorkspaceAudioPreview(props: { | 330 | function WorkspaceAudioPreview(props: { |
| 315 | readonly environmentId: EnvironmentId; | 331 | readonly environmentId: EnvironmentId; |
| 316 | readonly threadRef: ScopedThreadRef; | 332 | readonly threadRef: ScopedThreadRef; |
| | 333 | /** The thread is a draft the server does not know yet. */ |
| | 334 | readonly draft: boolean; |
| 317 | readonly absolutePath: string; | 335 | readonly absolutePath: string; |
| | 336 | readonly workspaceRoot: string; |
| 318 | readonly name: string; | 337 | readonly name: string; |
| 319 | readonly workspaceMutationId: string | null; | 338 | readonly workspaceMutationId: string | null; |
| 320 | }) { | 339 | }) { |
| 321 | const resource = useMemo( | 340 | const resource = useMemo( |
| 322 | () => ({ | 341 | () => |
| 323 | _tag: "media-file" as const, | 342 | workspaceAssetResource({ |
| 324 | threadId: props.threadRef.threadId, | 343 | kind: "media-file", |
| 325 | path: props.absolutePath, | 344 | threadRef: props.threadRef, |
| 326 | }), | 345 | draft: props.draft, |
| 327 | [props.threadRef.threadId, props.absolutePath], | 346 | workspaceRoot: props.workspaceRoot, |
| | 347 | absolutePath: props.absolutePath, |
| | 348 | }), |
| | 349 | [props.threadRef, props.draft, props.workspaceRoot, props.absolutePath], |
| 328 | ); | 350 | ); |
| 329 | const assetUrl = useAssetUrlState(props.environmentId, resource); | 351 | const assetUrl = useAssetUrlState(props.environmentId, resource); |
| 330 | const refreshAssetUrl = useAssetUrlRefresh(props.environmentId, resource); | 352 | const refreshAssetUrl = useAssetUrlRefresh(props.environmentId, resource); |
| ... | @@ -1022,6 +1044,8 @@ export default function FilePreviewPanel({ | ... | @@ -1022,6 +1044,8 @@ export default function FilePreviewPanel({ |
| 1022 | }: FilePreviewPanelProps) { | 1044 | }: FilePreviewPanelProps) { |
| 1023 | const relativePath = | 1045 | const relativePath = |
| 1024 | attachment === undefined ? resolveFilePreviewPath(requestedPath, cwd) : requestedPath; | 1046 | attachment === undefined ? resolveFilePreviewPath(requestedPath, cwd) : requestedPath; |
| | 1047 | // A draft's composer target is its draft id; a thread the server knows is a ref. |
| | 1048 | const draft = typeof composerDraftTarget === "string"; |
| 1025 | const { resolvedTheme } = useTheme(); | 1049 | const { resolvedTheme } = useTheme(); |
| 1026 | const wordWrap = useClientSettings((settings) => settings.wordWrap); | 1050 | const wordWrap = useClientSettings((settings) => settings.wordWrap); |
| 1027 | const canOperatePreview = useEnvironmentScope(environmentId, AuthPreviewOperateScope); | 1051 | const canOperatePreview = useEnvironmentScope(environmentId, AuthPreviewOperateScope); |
| ... | @@ -1334,6 +1358,7 @@ export default function FilePreviewPanel({ | ... | @@ -1334,6 +1358,7 @@ export default function FilePreviewPanel({ |
| 1334 | key={`${environmentId}:${threadRef.threadId}:${absolutePath}`} | 1358 | key={`${environmentId}:${threadRef.threadId}:${absolutePath}`} |
| 1335 | environmentId={environmentId} | 1359 | environmentId={environmentId} |
| 1336 | threadRef={threadRef} | 1360 | threadRef={threadRef} |
| | 1361 | draft={draft} |
| 1337 | absolutePath={absolutePath} | 1362 | absolutePath={absolutePath} |
| 1338 | workspaceRoot={cwd} | 1363 | workspaceRoot={cwd} |
| 1339 | name={relativePath} | 1364 | name={relativePath} |
| ... | @@ -1344,7 +1369,9 @@ export default function FilePreviewPanel({ | ... | @@ -1344,7 +1369,9 @@ export default function FilePreviewPanel({ |
| 1344 | key={`${environmentId}:${threadRef.threadId}:${absolutePath}`} | 1369 | key={`${environmentId}:${threadRef.threadId}:${absolutePath}`} |
| 1345 | environmentId={environmentId} | 1370 | environmentId={environmentId} |
| 1346 | threadRef={threadRef} | 1371 | threadRef={threadRef} |
| | 1372 | draft={draft} |
| 1347 | absolutePath={absolutePath} | 1373 | absolutePath={absolutePath} |
| | 1374 | workspaceRoot={cwd} |
| 1348 | name={relativePath} | 1375 | name={relativePath} |
| 1349 | workspaceMutationId={workspaceMutationId} | 1376 | workspaceMutationId={workspaceMutationId} |
| 1350 | /> | 1377 | /> |
| ... | @@ -1353,6 +1380,7 @@ export default function FilePreviewPanel({ | ... | @@ -1353,6 +1380,7 @@ export default function FilePreviewPanel({ |
| 1353 | key={absolutePath} | 1380 | key={absolutePath} |
| 1354 | environmentId={environmentId} | 1381 | environmentId={environmentId} |
| 1355 | threadRef={threadRef} | 1382 | threadRef={threadRef} |
| | 1383 | draft={draft} |
| 1356 | absolutePath={absolutePath} | 1384 | absolutePath={absolutePath} |
| 1357 | workspaceRoot={cwd} | 1385 | workspaceRoot={cwd} |
| 1358 | alt={relativePath} | 1386 | alt={relativePath} |
| ... | @@ -1363,6 +1391,7 @@ export default function FilePreviewPanel({ | ... | @@ -1363,6 +1391,7 @@ export default function FilePreviewPanel({ |
| 1363 | key={absolutePath} | 1391 | key={absolutePath} |
| 1364 | environmentId={environmentId} | 1392 | environmentId={environmentId} |
| 1365 | threadRef={threadRef} | 1393 | threadRef={threadRef} |
| | 1394 | draft={draft} |
| 1366 | absolutePath={absolutePath} | 1395 | absolutePath={absolutePath} |
| 1367 | workspaceRoot={cwd} | 1396 | workspaceRoot={cwd} |
| 1368 | title={relativePath} | 1397 | title={relativePath} |