@ai-matrx/media 0.7.16 → 0.8.0
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.
- package/CHANGELOG.md +10 -0
- package/dist/files.cjs +1298 -0
- package/dist/files.cjs.map +1 -0
- package/dist/files.d.cts +1171 -0
- package/dist/files.d.ts +1171 -0
- package/dist/files.js +1275 -0
- package/dist/files.js.map +1 -0
- package/package.json +16 -5
package/dist/files.d.cts
ADDED
|
@@ -0,0 +1,1171 @@
|
|
|
1
|
+
import { AudioMediaPart, AudioOutputData, VideoMediaPart, VideoOutputData, ImageMediaPart, ImageOutputData, PartialImageData, RenderBlockPayload } from '@ai-matrx/agents/generated/stream-events';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* files/ref-types — the Matrx file reference vocabulary shared by every client:
|
|
6
|
+
* visibility, permission level, the wire `MediaRef`, and the identity hint a caller
|
|
7
|
+
* may already know. Moved from matrx-frontend `features/files/types.ts` (P16f).
|
|
8
|
+
*/
|
|
9
|
+
type Visibility = "personal" | "internal" | "link" | "public";
|
|
10
|
+
type PermissionLevel = "read" | "write" | "admin";
|
|
11
|
+
interface MediaRef {
|
|
12
|
+
/** cld_files UUID — preferred form for any file we own. */
|
|
13
|
+
file_id?: string;
|
|
14
|
+
/** Any URL we issued (durable download route, share link) OR external https://. */
|
|
15
|
+
url?: string;
|
|
16
|
+
/** Optional client hint; backend overrides with `cld_files.mime_type` for owned files. */
|
|
17
|
+
mime_type?: string;
|
|
18
|
+
/** Free-form per-call metadata. Keep small — this rides on every request. */
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Metadata a caller may already know when all it has is a durable file id.
|
|
23
|
+
* These values seed the canonical Redux record before field hydration runs;
|
|
24
|
+
* omitted keys remain genuinely unloaded and are fetched on demand.
|
|
25
|
+
*/
|
|
26
|
+
interface FileIdentityHint {
|
|
27
|
+
fileName?: string;
|
|
28
|
+
mimeType?: string | null;
|
|
29
|
+
fileSize?: number | null;
|
|
30
|
+
visibility?: Visibility;
|
|
31
|
+
/** Permanent public delivery only. Never place a signed URL here. */
|
|
32
|
+
publicUrl?: string | null;
|
|
33
|
+
/** Permanent public CDN delivery only. Never place a signed URL here. */
|
|
34
|
+
cdnUrl?: string | null;
|
|
35
|
+
}
|
|
36
|
+
/** Reads a stored visibility; anything unknown is the private default. */
|
|
37
|
+
declare function toVisibility(raw: string | null | undefined): Visibility;
|
|
38
|
+
/**
|
|
39
|
+
* Build a MediaRef from just a `file_id` (e.g. when an upload completes
|
|
40
|
+
* and the caller has the id but not the full record yet).
|
|
41
|
+
*/
|
|
42
|
+
declare function fileIdToMediaRef(fileId: string, mimeType?: string | null): MediaRef;
|
|
43
|
+
/**
|
|
44
|
+
* Build a MediaRef from an external URL (public website image, signed URL
|
|
45
|
+
* we don't own, etc.). Use this ONLY when you don't have a `file_id` —
|
|
46
|
+
* otherwise the backend has to follow the URL to resolve the file.
|
|
47
|
+
*/
|
|
48
|
+
declare function urlToMediaRef(url: string, mimeType?: string | null): MediaRef;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* features/files/blocks/types.ts
|
|
52
|
+
*
|
|
53
|
+
* THE canonical TypeScript shape for every piece of media (image / video /
|
|
54
|
+
* audio / document / youtube) in the app — the FE mirror of Python's
|
|
55
|
+
* `UnifiedMediaBlock` Pydantic union. See:
|
|
56
|
+
* - docs/PYTHON_UPDATES.md — Phase 0/1/2 wire contract & migration plan
|
|
57
|
+
* - features/files/blocks/image/UNIFIED_IMAGE_BLOCK.md — backstory
|
|
58
|
+
* - packages/matrx-connect/matrx_connect/context/media_block.py (backend)
|
|
59
|
+
*
|
|
60
|
+
* Naming convention:
|
|
61
|
+
* - This file uses **camelCase** (TS domain shape).
|
|
62
|
+
* - The wire shape (`MediaBlockData.block` in stream events,
|
|
63
|
+
* `cld_files.metadata.generation` on assets) uses **snake_case**.
|
|
64
|
+
* - The adapter `./adapters/from-media-block.ts` converts the wire shape
|
|
65
|
+
* to this shape at the boundary. Components only ever see camelCase.
|
|
66
|
+
*
|
|
67
|
+
* Two discriminators:
|
|
68
|
+
* - `kind` : "image" | "video" | "audio" | "document" | "youtube"
|
|
69
|
+
* Narrow on this first to access kind-specific fields
|
|
70
|
+
* (e.g. `width`/`height` on image, `durationMs` on audio).
|
|
71
|
+
* - `origin` : "matrx" | "external"
|
|
72
|
+
* Narrow on this second to access ownership-specific fields
|
|
73
|
+
* (`fileId` on matrx, `externalUrl` on external).
|
|
74
|
+
*
|
|
75
|
+
* Invariants (enforced at adapter boundary):
|
|
76
|
+
* 1. `origin === "matrx"` → `fileId` non-null when `status === "complete"`.
|
|
77
|
+
* 2. `origin === "external"` → `externalUrl` non-null OR `base64` non-null.
|
|
78
|
+
* 3. `status === "streaming"` → `base64` non-null (in-flight bytes).
|
|
79
|
+
* 4. `status === "error"` → `errorMessage` non-null.
|
|
80
|
+
*/
|
|
81
|
+
type MediaKind = "image" | "video" | "audio" | "document" | "youtube";
|
|
82
|
+
type MediaOrigin = "matrx" | "external";
|
|
83
|
+
type MediaStatus = "complete" | "streaming" | "error";
|
|
84
|
+
/**
|
|
85
|
+
* THE canonical `platform.visibility` enum — identical to
|
|
86
|
+
* `features/files/types.ts#Visibility`, the DB, and the server. This used to
|
|
87
|
+
* be its own dialect (`public | personal | shared`), which is how `internal`
|
|
88
|
+
* and `link` rows got mis-bucketed on the way to URL resolution.
|
|
89
|
+
*/
|
|
90
|
+
type MediaVisibility = "personal" | "internal" | "link" | "public";
|
|
91
|
+
interface MediaBlockBase {
|
|
92
|
+
/**
|
|
93
|
+
* "complete" — final media; all configured URLs are valid as of emission
|
|
94
|
+
* "streaming" — generating; `base64` (partial preview) is the only render source
|
|
95
|
+
* "error" — generation failed; `errorMessage` carries the reason
|
|
96
|
+
*/
|
|
97
|
+
status: MediaStatus;
|
|
98
|
+
/** 0–1 progress. Meaningful only when status === "streaming". */
|
|
99
|
+
progress: number | null;
|
|
100
|
+
/** Populated when status === "error". null otherwise. */
|
|
101
|
+
errorMessage: string | null;
|
|
102
|
+
/** Canonical MIME `type/subtype`. */
|
|
103
|
+
mimeType: string | null;
|
|
104
|
+
/** Display name (often AI-generated for ai-produced media). */
|
|
105
|
+
fileName: string | null;
|
|
106
|
+
/**
|
|
107
|
+
* File size in bytes (renamed from `file_size` in Phase 0 — see
|
|
108
|
+
* docs/PYTHON_UPDATES.md §3). null when unknown (typical for streaming
|
|
109
|
+
* partials and pre-finalize states).
|
|
110
|
+
*/
|
|
111
|
+
sizeBytes: number | null;
|
|
112
|
+
/**
|
|
113
|
+
* Inline base64 bytes. Used for streaming partials and tiny inline assets.
|
|
114
|
+
* Cleared once `status === "complete"` and a URL is available.
|
|
115
|
+
*/
|
|
116
|
+
base64: string | null;
|
|
117
|
+
/**
|
|
118
|
+
* Free-form. Phase 2 stamps `metadata.generation` (typed as
|
|
119
|
+
* `MediaGenerationMetadata` below) for any AI-generated asset.
|
|
120
|
+
* Existing top-level keys (`model`, `provider`, `prompt`, `cost`) are
|
|
121
|
+
* preserved for back-compat — new code should read `metadata.generation`.
|
|
122
|
+
*/
|
|
123
|
+
metadata: Record<string, unknown> | null;
|
|
124
|
+
/**
|
|
125
|
+
* The reference role this media played as an INPUT (subject, style,
|
|
126
|
+
* first_frame, extend, …) and its `@name`, carried from the persisted
|
|
127
|
+
* part so a reload redisplays it. Absent for generated output.
|
|
128
|
+
*/
|
|
129
|
+
referenceRole?: string | null;
|
|
130
|
+
referenceName?: string | null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Fields present on every matrx-owned (cld_files-backed) media block.
|
|
134
|
+
* `fileId` is the permanent identity the URL resolver re-mints from on
|
|
135
|
+
* expiry (via fileHandler). Native storage locations are server-only and
|
|
136
|
+
* never appear on the client.
|
|
137
|
+
*/
|
|
138
|
+
interface MatrxOriginFields {
|
|
139
|
+
origin: "matrx";
|
|
140
|
+
/** cld_files.id — the permanent identity. */
|
|
141
|
+
fileId: string;
|
|
142
|
+
/**
|
|
143
|
+
* cld_files.visibility — drives URL resolution:
|
|
144
|
+
* "public" — prefer cdnUrl; permanent URL, no auth required
|
|
145
|
+
* everything else ("personal" / "internal" / "link") — render through
|
|
146
|
+
* the durable authenticated URL (`fileUrls(fileId).inline`), which the
|
|
147
|
+
* browser authenticates via the `mx_files_session` cookie
|
|
148
|
+
*/
|
|
149
|
+
visibility: MediaVisibility;
|
|
150
|
+
/** Permanent CDN URL — no expiry, no auth required. */
|
|
151
|
+
cdnUrl: string | null;
|
|
152
|
+
/** Durable attachment-disposition variant. Used for the download action. */
|
|
153
|
+
downloadUrl: string | null;
|
|
154
|
+
/** cld_files.parent_file_id — derivation lineage. */
|
|
155
|
+
parentFileId: string | null;
|
|
156
|
+
/**
|
|
157
|
+
* cld_files.derivation_kind — how this was produced from `parentFileId`.
|
|
158
|
+
* E.g. "manual_upload", "extracted_pages", "cropped", "rendered_page_image".
|
|
159
|
+
*/
|
|
160
|
+
derivationKind: string | null;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Fields present on every external (third-party / user-pasted) media block.
|
|
164
|
+
* We never refresh these and never try to mint fresh URLs.
|
|
165
|
+
*/
|
|
166
|
+
interface ExternalOriginFields {
|
|
167
|
+
origin: "external";
|
|
168
|
+
/** The external URL. Always present — empty string fallback means broken. */
|
|
169
|
+
externalUrl: string;
|
|
170
|
+
/** Optional provenance label — e.g. "Wikimedia", "Tool: web_search". */
|
|
171
|
+
sourceLabel: string | null;
|
|
172
|
+
}
|
|
173
|
+
interface ImageKindFields {
|
|
174
|
+
kind: "image";
|
|
175
|
+
/** Pixel width. Null when unknown / pre-finalize. */
|
|
176
|
+
width: number | null;
|
|
177
|
+
/** Pixel height. Null when unknown / pre-finalize. */
|
|
178
|
+
height: number | null;
|
|
179
|
+
/**
|
|
180
|
+
* Optional content-class hint from vision models — e.g. "photograph",
|
|
181
|
+
* "diagram", "ui-screenshot". Free-form; consumers should treat as a UI
|
|
182
|
+
* hint only.
|
|
183
|
+
*/
|
|
184
|
+
visionClass: string | null;
|
|
185
|
+
}
|
|
186
|
+
interface VideoKindFields {
|
|
187
|
+
kind: "video";
|
|
188
|
+
width: number | null;
|
|
189
|
+
height: number | null;
|
|
190
|
+
/** Duration in milliseconds. */
|
|
191
|
+
durationMs: number | null;
|
|
192
|
+
/**
|
|
193
|
+
* Poster/cover frame URL (extracted at ~10% of timeline). Phase 1
|
|
194
|
+
* populates this for matrx-owned videos via `Asset.variants["poster_url"]`.
|
|
195
|
+
*/
|
|
196
|
+
posterUrl: string | null;
|
|
197
|
+
}
|
|
198
|
+
interface AudioKindFields {
|
|
199
|
+
kind: "audio";
|
|
200
|
+
/** Duration in milliseconds. */
|
|
201
|
+
durationMs: number | null;
|
|
202
|
+
/**
|
|
203
|
+
* Inline transcript text when present. Long-form transcripts go in a
|
|
204
|
+
* separate processed_documents row; this carries the short-form summary
|
|
205
|
+
* or null.
|
|
206
|
+
*/
|
|
207
|
+
transcript: string | null;
|
|
208
|
+
}
|
|
209
|
+
interface DocumentKindFields {
|
|
210
|
+
kind: "document";
|
|
211
|
+
/** Page count for paginated documents (PDF, DOCX, …). null otherwise. */
|
|
212
|
+
pageCount: number | null;
|
|
213
|
+
/**
|
|
214
|
+
* Page-1 preview rendered to image. Phase 1 populates this for PDFs.
|
|
215
|
+
* Useful when displaying an inline document chip.
|
|
216
|
+
*/
|
|
217
|
+
page1Url: string | null;
|
|
218
|
+
}
|
|
219
|
+
interface YouTubeKindFields {
|
|
220
|
+
kind: "youtube";
|
|
221
|
+
/** Extracted YouTube video id (the `?v=` token). */
|
|
222
|
+
videoId: string | null;
|
|
223
|
+
}
|
|
224
|
+
type MatrxImageBlock = MediaBlockBase & MatrxOriginFields & ImageKindFields;
|
|
225
|
+
type ExternalImageBlock = MediaBlockBase & ExternalOriginFields & ImageKindFields;
|
|
226
|
+
type ImageBlock = MatrxImageBlock | ExternalImageBlock;
|
|
227
|
+
type MatrxVideoBlock = MediaBlockBase & MatrxOriginFields & VideoKindFields;
|
|
228
|
+
type ExternalVideoBlock = MediaBlockBase & ExternalOriginFields & VideoKindFields;
|
|
229
|
+
type VideoBlock = MatrxVideoBlock | ExternalVideoBlock;
|
|
230
|
+
type MatrxAudioBlock = MediaBlockBase & MatrxOriginFields & AudioKindFields;
|
|
231
|
+
type ExternalAudioBlock = MediaBlockBase & ExternalOriginFields & AudioKindFields;
|
|
232
|
+
type AudioBlock = MatrxAudioBlock | ExternalAudioBlock;
|
|
233
|
+
type MatrxDocumentBlock = MediaBlockBase & MatrxOriginFields & DocumentKindFields;
|
|
234
|
+
type ExternalDocumentBlock = MediaBlockBase & ExternalOriginFields & DocumentKindFields;
|
|
235
|
+
type DocumentBlock = MatrxDocumentBlock | ExternalDocumentBlock;
|
|
236
|
+
/** YouTube is always external per the Python contract. */
|
|
237
|
+
type YouTubeBlock = MediaBlockBase & ExternalOriginFields & YouTubeKindFields;
|
|
238
|
+
/**
|
|
239
|
+
* The unified discriminated union for every media reference in the app.
|
|
240
|
+
*
|
|
241
|
+
* Narrowing recipe:
|
|
242
|
+
* if (block.kind === "image") {
|
|
243
|
+
* if (block.origin === "matrx") {
|
|
244
|
+
* // block.fileId, block.visibility, block.cdnUrl, ...
|
|
245
|
+
* } else {
|
|
246
|
+
* // block.externalUrl, block.sourceLabel
|
|
247
|
+
* }
|
|
248
|
+
* // block.width, block.height (image-specific)
|
|
249
|
+
* }
|
|
250
|
+
*/
|
|
251
|
+
type UnifiedMediaBlock = ImageBlock | VideoBlock | AudioBlock | DocumentBlock | YouTubeBlock;
|
|
252
|
+
type MediaGenerationKind = "image" | "video" | "audio" | "speech" | "music";
|
|
253
|
+
interface MediaGenerationMetadata {
|
|
254
|
+
kind: MediaGenerationKind;
|
|
255
|
+
provider: string;
|
|
256
|
+
model: string;
|
|
257
|
+
prompt: string;
|
|
258
|
+
negativePrompt: string | null;
|
|
259
|
+
/**
|
|
260
|
+
* Provider's rewrite of the user's prompt (OpenAI's `revised_prompt`).
|
|
261
|
+
* Surface to the user — improves transparency and trust.
|
|
262
|
+
*/
|
|
263
|
+
revisedPrompt: string | null;
|
|
264
|
+
width: number | null;
|
|
265
|
+
height: number | null;
|
|
266
|
+
/** Free-form, e.g. "16:9", "1:1", "9:16". */
|
|
267
|
+
aspectRatio: string | null;
|
|
268
|
+
durationSeconds: number | null;
|
|
269
|
+
quality: string | null;
|
|
270
|
+
style: string | null;
|
|
271
|
+
seed: number | null;
|
|
272
|
+
steps: number | null;
|
|
273
|
+
cfgScale: number | null;
|
|
274
|
+
nRequested: number;
|
|
275
|
+
nReturned: number;
|
|
276
|
+
responseId: string | null;
|
|
277
|
+
durationMs: number | null;
|
|
278
|
+
costUsd: number | null;
|
|
279
|
+
/** E.g. "completed", "content_filter", "error". */
|
|
280
|
+
finishReason: string | null;
|
|
281
|
+
safetyFlagged: boolean;
|
|
282
|
+
/** Catch-all for provider-native fields we haven't canonicalized yet. */
|
|
283
|
+
providerExtras: Record<string, unknown>;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Wire shape (snake_case) as Python emits it. Adapters read this; consumers
|
|
287
|
+
* generally don't.
|
|
288
|
+
*/
|
|
289
|
+
interface MediaGenerationMetadataWire {
|
|
290
|
+
kind: MediaGenerationKind;
|
|
291
|
+
provider: string;
|
|
292
|
+
model: string;
|
|
293
|
+
prompt: string;
|
|
294
|
+
negative_prompt?: string | null;
|
|
295
|
+
revised_prompt?: string | null;
|
|
296
|
+
width?: number | null;
|
|
297
|
+
height?: number | null;
|
|
298
|
+
aspect_ratio?: string | null;
|
|
299
|
+
duration_seconds?: number | null;
|
|
300
|
+
quality?: string | null;
|
|
301
|
+
style?: string | null;
|
|
302
|
+
seed?: number | null;
|
|
303
|
+
steps?: number | null;
|
|
304
|
+
cfg_scale?: number | null;
|
|
305
|
+
n_requested?: number;
|
|
306
|
+
n_returned?: number;
|
|
307
|
+
response_id?: string | null;
|
|
308
|
+
duration_ms?: number | null;
|
|
309
|
+
cost_usd?: number | null;
|
|
310
|
+
finish_reason?: string | null;
|
|
311
|
+
safety_flagged?: boolean;
|
|
312
|
+
provider_extras?: Record<string, unknown>;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Pull the typed `MediaGenerationMetadata` out of a block's free-form
|
|
316
|
+
* metadata bag. Returns null when no `generation` key is present.
|
|
317
|
+
*
|
|
318
|
+
* Usage:
|
|
319
|
+
* const gen = parseGenerationMetadata(block.metadata);
|
|
320
|
+
* if (gen?.revisedPrompt) { renderRevisedPromptBanner(gen.revisedPrompt); }
|
|
321
|
+
*/
|
|
322
|
+
declare function parseGenerationMetadata(metadata: Record<string, unknown> | null | undefined): MediaGenerationMetadata | null;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* features/files/blocks/guards.ts
|
|
326
|
+
*
|
|
327
|
+
* Runtime type guards for `UnifiedMediaBlock` and its variants. Used at
|
|
328
|
+
* trust boundaries (Redux reads, adapter inputs) to prove the shape
|
|
329
|
+
* rather than force-cast.
|
|
330
|
+
*
|
|
331
|
+
* All guards accept `unknown` so they're safe to use against raw
|
|
332
|
+
* `Record<string, unknown>` data (e.g. `block.data` on a render-block
|
|
333
|
+
* envelope, `block.metadata` on a free-form bag).
|
|
334
|
+
*/
|
|
335
|
+
|
|
336
|
+
declare function isMatrxMediaBlock(value: unknown): value is Extract<UnifiedMediaBlock, {
|
|
337
|
+
origin: "matrx";
|
|
338
|
+
}>;
|
|
339
|
+
declare function isExternalMediaBlock(value: unknown): value is Extract<UnifiedMediaBlock, {
|
|
340
|
+
origin: "external";
|
|
341
|
+
}>;
|
|
342
|
+
declare function isUnifiedMediaBlock(value: unknown): value is UnifiedMediaBlock;
|
|
343
|
+
declare function isImageBlock(value: unknown): value is ImageBlock;
|
|
344
|
+
declare function isVideoBlock(value: unknown): value is VideoBlock;
|
|
345
|
+
declare function isAudioBlock(value: unknown): value is AudioBlock;
|
|
346
|
+
declare function isDocumentBlock(value: unknown): value is DocumentBlock;
|
|
347
|
+
declare function isYouTubeBlock(value: unknown): value is YouTubeBlock;
|
|
348
|
+
declare function isMatrxImageBlock(value: unknown): value is MatrxImageBlock;
|
|
349
|
+
declare function isExternalImageBlock(value: unknown): value is ExternalImageBlock;
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* features/files/blocks/image/types.ts
|
|
353
|
+
*
|
|
354
|
+
* Image-specific types — re-exported from the canonical `UnifiedMediaBlock`
|
|
355
|
+
* union in `../types.ts`. THIS FILE IS THIN BY DESIGN: there is no
|
|
356
|
+
* image-only shape anymore; an `ImageBlock` is just the `kind: "image"`
|
|
357
|
+
* variant of the platform-wide media block.
|
|
358
|
+
*
|
|
359
|
+
* Keep this file alive so existing imports
|
|
360
|
+
* import type { UnifiedImageBlock, MatrxImageBlock, ExternalImageBlock }
|
|
361
|
+
* from "@/features/files/blocks/image/types";
|
|
362
|
+
* continue to work without churn. New code should prefer importing from
|
|
363
|
+
* `@/features/files/blocks/types` directly.
|
|
364
|
+
*
|
|
365
|
+
* See ../UNIFIED_IMAGE_BLOCK.md and docs/PYTHON_UPDATES.md for the wire
|
|
366
|
+
* contract.
|
|
367
|
+
*/
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Back-compat alias. Past consumers know "UnifiedImageBlock" — this is the
|
|
371
|
+
* same thing as `ImageBlock` from the canonical union. New code should
|
|
372
|
+
* spell it `ImageBlock` for consistency with the other kinds.
|
|
373
|
+
*/
|
|
374
|
+
type UnifiedImageBlock = ImageBlock;
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* features/files/blocks/image/guards.ts
|
|
378
|
+
*
|
|
379
|
+
* Image-specific runtime guards. Re-exported from the canonical guards in
|
|
380
|
+
* `../guards.ts`. New code should prefer importing from there directly.
|
|
381
|
+
*
|
|
382
|
+
* Usage:
|
|
383
|
+
*
|
|
384
|
+
* if (!isUnifiedImageBlock(block.data)) return null;
|
|
385
|
+
* // block.data is now narrowed to ImageBlock (kind: "image")
|
|
386
|
+
* // by TypeScript.
|
|
387
|
+
*
|
|
388
|
+
* These guards check BOTH `kind === "image"` and the origin discriminator,
|
|
389
|
+
* so a stray video/audio/document block will NOT pass through them.
|
|
390
|
+
*/
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Back-compat alias for `isImageBlock` — narrows to `UnifiedImageBlock`
|
|
394
|
+
* (a.k.a. the `kind: "image"` variant of `UnifiedMediaBlock`).
|
|
395
|
+
*/
|
|
396
|
+
declare function isUnifiedImageBlock(value: unknown): value is UnifiedImageBlock;
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* features/files/blocks/adapters/from-cx-av-part.ts
|
|
400
|
+
*
|
|
401
|
+
* Convert a DB-stored `cx_message.content[]` media part (kind: "audio" |
|
|
402
|
+
* "video") into the `AudioOutputData` / `VideoOutputData` render-block payload
|
|
403
|
+
* the chat media renderers consume.
|
|
404
|
+
*
|
|
405
|
+
* This is the audio/video twin of the image adapter
|
|
406
|
+
* (`../image/adapters/from-cx-media-part.ts`) and exists for the same reason:
|
|
407
|
+
* **media identity is the `file_id`, never a URL.** The stored `url` is
|
|
408
|
+
* whatever happened to be visible at save time (legacy rows: a long-dead
|
|
409
|
+
* signed S3 URL). `file_id` is what lets `buildMediaSource` → `useFileSrc`
|
|
410
|
+
* resolve the durable URL on every render.
|
|
411
|
+
*
|
|
412
|
+
* `file_id` lives at the TOP LEVEL of the stored media part (and is sometimes
|
|
413
|
+
* mirrored into `metadata`); the URL flavors are dumped into `metadata`. Both
|
|
414
|
+
* are lifted back out here so the round-trip is lossless.
|
|
415
|
+
*/
|
|
416
|
+
|
|
417
|
+
declare function fromCxAudioPart(part: AudioMediaPart): AudioOutputData & {
|
|
418
|
+
transcription_result: string | null;
|
|
419
|
+
};
|
|
420
|
+
declare function fromCxVideoPart(part: VideoMediaPart): VideoOutputData & {
|
|
421
|
+
reference_role: string | null;
|
|
422
|
+
reference_name: string | null;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* features/files/blocks/adapters/from-media-block.ts
|
|
427
|
+
*
|
|
428
|
+
* THE primary inbound adapter for the new `data.type === "media_block"`
|
|
429
|
+
* stream event (Phase 0 of Python's unified-media rollout — see
|
|
430
|
+
* docs/PYTHON_UPDATES.md).
|
|
431
|
+
*
|
|
432
|
+
* The wire shape (Python's `UnifiedMediaBlock` Pydantic union) is
|
|
433
|
+
* snake_case and nearly identical to our domain shape. This adapter does
|
|
434
|
+
* exactly two things:
|
|
435
|
+
* 1. Rename fields snake_case → camelCase.
|
|
436
|
+
* 2. Enforce invariants:
|
|
437
|
+
* - matrx → fileId non-null
|
|
438
|
+
* - external → externalUrl non-null
|
|
439
|
+
* Anything that doesn't satisfy these falls through to an external
|
|
440
|
+
* "broken" block; the renderer shows an error state.
|
|
441
|
+
*
|
|
442
|
+
* Anything kind-specific (image's `vision_class`, video's `duration_ms`,
|
|
443
|
+
* etc.) is just propagated — no per-kind logic here.
|
|
444
|
+
*
|
|
445
|
+
* Note: Python `main` hasn't deployed yet (as of 2026-05-16). The shape
|
|
446
|
+
* below is what we'll receive once it does. Until then this adapter is
|
|
447
|
+
* cold-path; the existing `image_output` / `partial_image` legacy
|
|
448
|
+
* adapters carry traffic.
|
|
449
|
+
*/
|
|
450
|
+
|
|
451
|
+
interface WireMediaBlockBase {
|
|
452
|
+
origin: "matrx" | "external";
|
|
453
|
+
status?: MediaStatus | null;
|
|
454
|
+
progress?: number | null;
|
|
455
|
+
error_message?: string | null;
|
|
456
|
+
mime_type?: string | null;
|
|
457
|
+
file_name?: string | null;
|
|
458
|
+
size_bytes?: number | null;
|
|
459
|
+
base64?: string | null;
|
|
460
|
+
metadata?: Record<string, unknown> | null;
|
|
461
|
+
file_id?: string | null;
|
|
462
|
+
visibility?: MediaVisibility | null;
|
|
463
|
+
cdn_url?: string | null;
|
|
464
|
+
download_url?: string | null;
|
|
465
|
+
parent_file_id?: string | null;
|
|
466
|
+
derivation_kind?: string | null;
|
|
467
|
+
external_url?: string | null;
|
|
468
|
+
source_label?: string | null;
|
|
469
|
+
}
|
|
470
|
+
interface WireImageBlock extends WireMediaBlockBase {
|
|
471
|
+
kind: "image";
|
|
472
|
+
width?: number | null;
|
|
473
|
+
height?: number | null;
|
|
474
|
+
vision_class?: string | null;
|
|
475
|
+
}
|
|
476
|
+
interface WireVideoBlock extends WireMediaBlockBase {
|
|
477
|
+
kind: "video";
|
|
478
|
+
width?: number | null;
|
|
479
|
+
height?: number | null;
|
|
480
|
+
duration_ms?: number | null;
|
|
481
|
+
poster_url?: string | null;
|
|
482
|
+
}
|
|
483
|
+
interface WireAudioBlock extends WireMediaBlockBase {
|
|
484
|
+
kind: "audio";
|
|
485
|
+
duration_ms?: number | null;
|
|
486
|
+
transcript?: string | null;
|
|
487
|
+
}
|
|
488
|
+
interface WireDocumentBlock extends WireMediaBlockBase {
|
|
489
|
+
kind: "document";
|
|
490
|
+
page_count?: number | null;
|
|
491
|
+
page1_url?: string | null;
|
|
492
|
+
}
|
|
493
|
+
interface WireYouTubeBlock extends WireMediaBlockBase {
|
|
494
|
+
kind: "youtube";
|
|
495
|
+
video_id?: string | null;
|
|
496
|
+
}
|
|
497
|
+
type WireMediaBlock = WireImageBlock | WireVideoBlock | WireAudioBlock | WireDocumentBlock | WireYouTubeBlock;
|
|
498
|
+
/**
|
|
499
|
+
* Wire shape for the `media_block` stream event envelope. Always has
|
|
500
|
+
* `type: "media_block"` and a `block: WireMediaBlock` payload.
|
|
501
|
+
*/
|
|
502
|
+
interface WireMediaBlockData {
|
|
503
|
+
type: "media_block";
|
|
504
|
+
block: WireMediaBlock;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Lift a wire-shape `UnifiedMediaBlock` (as emitted by Python's new
|
|
508
|
+
* `media_block` data event) into our domain `UnifiedMediaBlock` shape.
|
|
509
|
+
*
|
|
510
|
+
* Discriminates on `kind` and delegates to the kind-specific lifter.
|
|
511
|
+
* Each lifter handles the matrx-vs-external origin split internally.
|
|
512
|
+
*/
|
|
513
|
+
declare function fromMediaBlock(wire: WireMediaBlock): UnifiedMediaBlock;
|
|
514
|
+
/**
|
|
515
|
+
* Predicate for narrowing a raw `data` payload (typed as
|
|
516
|
+
* `Record<string, unknown>` in Redux) down to a `media_block` event.
|
|
517
|
+
*
|
|
518
|
+
* Use at the stream-event boundary:
|
|
519
|
+
* if (isMediaBlockData(d)) { upsert(fromMediaBlock(d.block)); }
|
|
520
|
+
*/
|
|
521
|
+
declare function isMediaBlockData(value: unknown): value is WireMediaBlockData;
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* features/files/blocks/adapters/from-media-ref.ts
|
|
525
|
+
*
|
|
526
|
+
* Generic platform primitive: turn a bare `MediaRef` (or file_id / URL)
|
|
527
|
+
* into a minimal `UnifiedMediaBlock` so it can be fed to the canonical
|
|
528
|
+
* renderers (`UnifiedImageBlockRenderer`, `UnifiedVideoBlockRenderer`)
|
|
529
|
+
* which consume a `block`, not a `MediaRef`.
|
|
530
|
+
*
|
|
531
|
+
* Why this exists: many callsites only ever have a durable reference to a
|
|
532
|
+
* file — a `{file_id}` (we own it) or a `{url}` (external / already-public)
|
|
533
|
+
* — yet they want the full rich-media affordances (expand → fullscreen,
|
|
534
|
+
* the "…" menu, mobile long-press, share, download). Those affordances
|
|
535
|
+
* live ONLY on the canonical block renderers. This adapter bridges the
|
|
536
|
+
* gap without forcing every caller to hand-build a block literal (which
|
|
537
|
+
* the CLAUDE.md file-handling rules forbid).
|
|
538
|
+
*
|
|
539
|
+
* Mapping (consistent with the invariants in `../types.ts`):
|
|
540
|
+
* - `{ file_id }` ref → an `origin: "matrx"` block. `fileId` is set;
|
|
541
|
+
* `visibility` is "personal" (unknown) and `cdnUrl` is null, so
|
|
542
|
+
* `useBlockMediaSource` resolves the durable URL via the injected
|
|
543
|
+
* `MediaClient`.
|
|
544
|
+
* - `{ url }` ref → an `origin: "external"` block. `externalUrl` is set;
|
|
545
|
+
* the renderer uses it as-is.
|
|
546
|
+
* - A ref that resolves to neither → an `external` "broken" block with a
|
|
547
|
+
* null/empty `externalUrl`; the renderer shows its error state.
|
|
548
|
+
*
|
|
549
|
+
* `status` is always "complete" — these refs point at finished media. For
|
|
550
|
+
* streaming partials, use the streaming adapters in `from-media-block.ts`.
|
|
551
|
+
*/
|
|
552
|
+
|
|
553
|
+
/** The kinds this generic adapter can synthesize a block for. */
|
|
554
|
+
type MediaBlockKindArg = "image" | "video";
|
|
555
|
+
/**
|
|
556
|
+
* Build a minimal `ImageBlock` from a durable reference.
|
|
557
|
+
*/
|
|
558
|
+
declare function imageBlockFromMediaRef(ref: MediaRef | null): ImageBlock | null;
|
|
559
|
+
/**
|
|
560
|
+
* Build a minimal `VideoBlock` from a durable reference.
|
|
561
|
+
*/
|
|
562
|
+
declare function videoBlockFromMediaRef(ref: MediaRef | null): VideoBlock | null;
|
|
563
|
+
/**
|
|
564
|
+
* Kind-dispatching convenience: build an image or video block from a ref.
|
|
565
|
+
* Returns `null` when `ref` is null.
|
|
566
|
+
*/
|
|
567
|
+
declare function blockFromMediaRef(ref: MediaRef | null, kind: MediaBlockKindArg): ImageBlock | VideoBlock | null;
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* features/files/blocks/image/adapters/from-cx-media-part.ts
|
|
571
|
+
*
|
|
572
|
+
* Convert a DB-stored `cx_message.content[]` media part (kind: "image") into
|
|
573
|
+
* a UnifiedImageBlock.
|
|
574
|
+
*
|
|
575
|
+
* Today's storage shape (CxMediaContent → ImageMediaPart):
|
|
576
|
+
* { type: "media", kind: "image", url?, mime_type?, base64_data?,
|
|
577
|
+
* metadata? }
|
|
578
|
+
*
|
|
579
|
+
* Everything else (file_id, cdn_url, visibility, thumbnails,
|
|
580
|
+
* dimensions, file_name, prompt, model, etc.) gets dumped into `metadata`
|
|
581
|
+
* today by `assembleMessageParts`. This adapter pulls them BACK out so the
|
|
582
|
+
* round-trip is lossless.
|
|
583
|
+
*
|
|
584
|
+
* Delete when `cx_message.content[]` storage shape switches to
|
|
585
|
+
* UnifiedImageBlock natively (Phase 3).
|
|
586
|
+
*/
|
|
587
|
+
|
|
588
|
+
declare function fromCxMediaPart(part: ImageMediaPart): UnifiedImageBlock;
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* features/files/blocks/image/adapters/from-image-output-data.ts
|
|
592
|
+
*
|
|
593
|
+
* Convert a Python `image_output` data event into a UnifiedImageBlock.
|
|
594
|
+
*
|
|
595
|
+
* Today's Python wire shape (ImageOutputData):
|
|
596
|
+
* { type: "image_output", url, mime_type, file_id?, cdn_url?, download_url? }
|
|
597
|
+
*
|
|
598
|
+
* What this adapter does:
|
|
599
|
+
* - Lifts `file_id` to identify a matrx-owned file (most common case).
|
|
600
|
+
* - Tries to extract `file_id` from `url` if Python didn't supply one
|
|
601
|
+
* (legacy fallback — eventually deletable).
|
|
602
|
+
* - Promotes additional fields from `metadata` if Python included them
|
|
603
|
+
* there as a transitional shim:
|
|
604
|
+
* visibility, thumbnail_url, parent_file_id, derivation_kind,
|
|
605
|
+
* file_name, width, height, size_bytes.
|
|
606
|
+
* - When no `file_id` is recoverable, falls back to an external block
|
|
607
|
+
* using whichever URL is most likely permanent.
|
|
608
|
+
*
|
|
609
|
+
* Delete when Python emits UnifiedImageBlock directly (Phase 2).
|
|
610
|
+
*/
|
|
611
|
+
|
|
612
|
+
declare function fromImageOutputData(data: ImageOutputData, carriedMetadata?: Record<string, unknown> | null): UnifiedImageBlock;
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* features/files/blocks/image/adapters/from-partial-image-data.ts
|
|
616
|
+
*
|
|
617
|
+
* Convert a Python `partial_image` event into an in-flight UnifiedImageBlock.
|
|
618
|
+
*
|
|
619
|
+
* Partial images are base64 frames Python emits during image generation.
|
|
620
|
+
* They arrive BEFORE the final `image_output` event lands, so there's no
|
|
621
|
+
* fileId yet. We model them as an external block in "streaming" status
|
|
622
|
+
* whose `base64` carries the latest frame.
|
|
623
|
+
*
|
|
624
|
+
* When the final `image_output` arrives, the stream-ingest layer should
|
|
625
|
+
* upsert by `blockId` so a single block transitions from streaming
|
|
626
|
+
* (base64-only) → complete (matrx variant with URLs). This means partial
|
|
627
|
+
* and final SHARE the same blockId in Redux.
|
|
628
|
+
*
|
|
629
|
+
* Delete when Python emits UnifiedImageBlock directly with status: "streaming"
|
|
630
|
+
* (Phase 2).
|
|
631
|
+
*/
|
|
632
|
+
|
|
633
|
+
declare function fromPartialImageData(data: PartialImageData, carriedMetadata?: Record<string, unknown> | null): UnifiedImageBlock;
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* features/files/blocks/image/adapters/from-render-block.ts
|
|
637
|
+
*
|
|
638
|
+
* Convert a markdown-parsed image render_block (e.g. from a text chunk that
|
|
639
|
+
* contained ``) into a UnifiedImageBlock.
|
|
640
|
+
*
|
|
641
|
+
* Today's wire shape is minimal — `{ src, alt }`. We treat these as external
|
|
642
|
+
* unless the URL pattern matches our canonical S3 key scheme, in which case
|
|
643
|
+
* we promote to matrx via `extractFileIdFromUrl` (inside `fromImageOutputData`).
|
|
644
|
+
*
|
|
645
|
+
* Input type is `RenderBlockPayload` (not `ImageRenderBlock`) so callers can
|
|
646
|
+
* pass the redux-stored loose shape without a force-cast. We validate the
|
|
647
|
+
* payload structure internally; an empty src yields an external block with an
|
|
648
|
+
* empty URL — the renderer's error state covers it.
|
|
649
|
+
*
|
|
650
|
+
* Delete when render_block:image emits UnifiedImageBlock in its `data` field
|
|
651
|
+
* (Phase 2).
|
|
652
|
+
*/
|
|
653
|
+
|
|
654
|
+
declare function fromRenderBlock(block: RenderBlockPayload): UnifiedImageBlock;
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* features/files/blocks/image/adapters/to-cx-media-part.ts
|
|
658
|
+
*
|
|
659
|
+
* Convert a `UnifiedImageBlock` into the generated DB on-disk
|
|
660
|
+
* `ImageMediaPart` shape.
|
|
661
|
+
*
|
|
662
|
+
* On-disk shape today (`ImageMediaPart`):
|
|
663
|
+
* { type: "media", kind: "image", file_id? | url?, origin?,
|
|
664
|
+
* mime_type?, size_bytes?, width?, height?, metadata? }
|
|
665
|
+
*
|
|
666
|
+
* Strategy:
|
|
667
|
+
* - Keep generated top-level identity fields populated so
|
|
668
|
+
* legacy readers that haven't migrated keep working.
|
|
669
|
+
* - Pack EVERY canonical field (origin, fileId, cdnUrl, downloadUrl,
|
|
670
|
+
* visibility, thumbnails, dimensions, etc.) into
|
|
671
|
+
* `metadata` under stable keys so `fromCxMediaPart` can re-lift them
|
|
672
|
+
* losslessly when the message is reloaded.
|
|
673
|
+
*
|
|
674
|
+
* Delete when `cx_message.content[]` storage switches to UnifiedImageBlock
|
|
675
|
+
* natively (Phase 3).
|
|
676
|
+
*/
|
|
677
|
+
|
|
678
|
+
declare function toCxMediaPart(block: UnifiedImageBlock): ImageMediaPart;
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* features/files/blocks/image/helpers/derive-viewer-url.ts
|
|
682
|
+
*
|
|
683
|
+
* Derive the internal viewer route for a matrx-owned image. The route
|
|
684
|
+
* `/files/f/{fileId}` is the canonical "deep link" for any cld_files row.
|
|
685
|
+
*
|
|
686
|
+
* External blocks do not have a viewer URL — return null.
|
|
687
|
+
*/
|
|
688
|
+
|
|
689
|
+
declare function deriveViewerUrl(block: UnifiedImageBlock): string | null;
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* features/files/blocks/image/helpers/extract-file-id-from-url.ts
|
|
693
|
+
*
|
|
694
|
+
* Best-effort extraction of a cld_files UUID from a storage / CDN URL.
|
|
695
|
+
*
|
|
696
|
+
* Canonical S3 key scheme: `/{owner_id}/{file_id}` (no subfolder, no extension).
|
|
697
|
+
* Legacy fallback: `/{owner_id}/{folder}/{file_id}.{ext}`.
|
|
698
|
+
*
|
|
699
|
+
* Used by adapters as a last-resort when Python's payload doesn't carry an
|
|
700
|
+
* explicit `file_id` field — e.g. legacy stream events. Once Python emits
|
|
701
|
+
* `file_id` consistently, this becomes dead code.
|
|
702
|
+
*/
|
|
703
|
+
declare function extractFileIdFromUrl(url: string | null | undefined): string | null;
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* features/files/blocks/image/helpers/parse-filename-from-url.ts
|
|
707
|
+
*
|
|
708
|
+
* S3 signed URLs the backend mints for AI-generated images carry the
|
|
709
|
+
* intended filename in `response-content-disposition`, e.g.:
|
|
710
|
+
*
|
|
711
|
+
* ?response-content-disposition=inline%3B%20filename%3D%22kitten.png%22
|
|
712
|
+
*
|
|
713
|
+
* Python's AI-naming step (`features/ai/...`) writes a meaningful name
|
|
714
|
+
* there based on the prompt, so the browser shows it on download. But
|
|
715
|
+
* when we use `<a download="...">` we override that — we need the name
|
|
716
|
+
* in `block.fileName` to pass it back.
|
|
717
|
+
*
|
|
718
|
+
* This helper extracts the filename from the query param if present.
|
|
719
|
+
* Returns null on anything malformed so callers always get a string or
|
|
720
|
+
* null (never an empty string).
|
|
721
|
+
*
|
|
722
|
+
* Handles both RFC 5987 forms:
|
|
723
|
+
* - `filename="kitten.png"` (quoted, ASCII)
|
|
724
|
+
* - `filename*=UTF-8''ki%CC%88tten.png` (extended, percent-encoded)
|
|
725
|
+
*/
|
|
726
|
+
declare function parseFilenameFromUrl(url: string | null | undefined): string | null;
|
|
727
|
+
|
|
728
|
+
/** One compact publish-date treatment shared by every video surface. */
|
|
729
|
+
declare function VideoPublishDate({ publishedAt, className, }: {
|
|
730
|
+
publishedAt?: string | null;
|
|
731
|
+
className?: string;
|
|
732
|
+
}): react.JSX.Element;
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* features/files/handler/errors.ts
|
|
736
|
+
*
|
|
737
|
+
* Error taxonomy for the universal file handler. Every failure inside the
|
|
738
|
+
* handler maps to ONE of these classes — callers `instanceof`-check to
|
|
739
|
+
* decide whether to retry, refresh, surface a UI, or reject.
|
|
740
|
+
*
|
|
741
|
+
* The crucial distinction the handler exists to enforce:
|
|
742
|
+
*
|
|
743
|
+
* - FileAccessDeniedError → user does NOT have access. Reject. Never retry.
|
|
744
|
+
* - a transient auth failure on a durable URL → refresh the file session
|
|
745
|
+
* (see ../session.ts) and retry the SAME URL; never a terminal error.
|
|
746
|
+
*/
|
|
747
|
+
type FileHandlerErrorCode = "access_denied" | "not_found" | "deleted" | "share_link_invalid" | "external_fetch_failed" | "cors_blocked" | "mime_unknown" | "upload_failed" | "upload_cancelled" | "quota_exceeded" | "in_flight" | "internal";
|
|
748
|
+
declare class FileHandlerError extends Error {
|
|
749
|
+
readonly code: FileHandlerErrorCode;
|
|
750
|
+
readonly fileId?: string | undefined;
|
|
751
|
+
readonly cause?: unknown;
|
|
752
|
+
constructor(code: FileHandlerErrorCode, message: string, opts?: {
|
|
753
|
+
fileId?: string;
|
|
754
|
+
cause?: unknown;
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
declare class FileAccessDeniedError extends FileHandlerError {
|
|
758
|
+
constructor(message?: string, opts?: {
|
|
759
|
+
fileId?: string;
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
declare class FileNotFoundError extends FileHandlerError {
|
|
763
|
+
constructor(message?: string, opts?: {
|
|
764
|
+
fileId?: string;
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
declare class FileDeletedError extends FileHandlerError {
|
|
768
|
+
constructor(message?: string, opts?: {
|
|
769
|
+
fileId?: string;
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
declare class ShareLinkInvalidError extends FileHandlerError {
|
|
773
|
+
constructor(message?: string);
|
|
774
|
+
}
|
|
775
|
+
declare class ExternalFetchError extends FileHandlerError {
|
|
776
|
+
constructor(message?: string, opts?: {
|
|
777
|
+
cause?: unknown;
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
declare class FileUploadError extends FileHandlerError {
|
|
781
|
+
constructor(message: string, opts?: {
|
|
782
|
+
cause?: unknown;
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* The person was asked which workspace an upload belongs to and declined.
|
|
787
|
+
*
|
|
788
|
+
* Deliberately a distinct class, not a `FileUploadError` with a flag: nothing
|
|
789
|
+
* failed. No bytes moved, no row was written, and there is nothing to retry or
|
|
790
|
+
* report. Every catch site must swallow it silently — cancelling has to return
|
|
791
|
+
* the person exactly where they were, which a red toast does not.
|
|
792
|
+
*/
|
|
793
|
+
declare class UploadCancelledError extends FileHandlerError {
|
|
794
|
+
constructor(message?: string);
|
|
795
|
+
}
|
|
796
|
+
declare function isUploadCancelledError(error: unknown): boolean;
|
|
797
|
+
|
|
798
|
+
/** Compose a canonical Files folder path from an optional logical root. */
|
|
799
|
+
declare function composeUploadFolderPath(root?: string, path?: string): string;
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* features/files/utils/folder-conventions.ts
|
|
803
|
+
*
|
|
804
|
+
* Canonical folder paths for every feature that uploads on behalf of the
|
|
805
|
+
* user. The rule is simple: **organize files the way a user would expect to
|
|
806
|
+
* find them.** When a user opens `/files`, they should be able to
|
|
807
|
+
* point at a folder and say "that's where X goes" without us explaining it.
|
|
808
|
+
*
|
|
809
|
+
* Two buckets:
|
|
810
|
+
*
|
|
811
|
+
* 1. Visible folders — real, user-facing. Files go here when the user
|
|
812
|
+
* produced them, uploaded them, or attached them to something. Names are
|
|
813
|
+
* human-readable (Title Case) because they're shown in the tree.
|
|
814
|
+
*
|
|
815
|
+
* 2. Hidden folders (leading `.`) — ephemeral / infrastructure files the
|
|
816
|
+
* user doesn't need to see or manage. Auto-collapsed in the UI. These
|
|
817
|
+
* should be rare; only use them for true transients (short-lived URL
|
|
818
|
+
* staging, drafts) and prefer deletion on completion.
|
|
819
|
+
*
|
|
820
|
+
* Every consumer migration should pick its folder from this file or add a
|
|
821
|
+
* new constant here — do NOT hand-roll folder paths at call sites.
|
|
822
|
+
*
|
|
823
|
+
* This file is also the source of truth for the **default visibility** of
|
|
824
|
+
* every folder — see `resolveDefaultVisibility` near the bottom. Callers
|
|
825
|
+
* should pick visibility based on render context, not user role: anything
|
|
826
|
+
* rendered on a public page (org logo, podcast cover, agent-app icon)
|
|
827
|
+
* defaults to `"public"` so it gets a CDN URL; anything user-scoped
|
|
828
|
+
* (chat attachment, task attachment, ephemeral temp) stays `"private"`.
|
|
829
|
+
*/
|
|
830
|
+
|
|
831
|
+
declare const CloudFolders: {
|
|
832
|
+
/** Top-level "Images" drawer. */
|
|
833
|
+
readonly IMAGES: "Images";
|
|
834
|
+
/** Images pasted/attached inside a chat. */
|
|
835
|
+
readonly IMAGES_CHAT: "Images/Chat";
|
|
836
|
+
/** Screenshots dragged or pasted into the app. */
|
|
837
|
+
readonly IMAGES_SCREENSHOTS: "Images/Screenshots";
|
|
838
|
+
/** User avatars / profile images. */
|
|
839
|
+
readonly IMAGES_AVATARS: "Images/Avatars";
|
|
840
|
+
/** Images generated by AI tools. */
|
|
841
|
+
readonly IMAGES_GENERATED: "Images/Generated";
|
|
842
|
+
/** Screenshots and images flattened with user-authored markup. */
|
|
843
|
+
readonly IMAGES_ANNOTATED: "Images/Annotated";
|
|
844
|
+
/** Top-level "Audio" drawer. */
|
|
845
|
+
readonly AUDIO: "Audio";
|
|
846
|
+
/** User-initiated audio recordings (voice-to-text, voice pad, etc.). */
|
|
847
|
+
readonly AUDIO_RECORDINGS: "Audio/Recordings";
|
|
848
|
+
/** Podcast asset uploads. */
|
|
849
|
+
readonly AUDIO_PODCASTS: "Audio/Podcasts";
|
|
850
|
+
/** Top-level "Captures" drawer — everything the in-browser media-capture
|
|
851
|
+
* system saves (features/media-capture). User-produced, so visible. */
|
|
852
|
+
readonly CAPTURES: "Captures";
|
|
853
|
+
/** Photos taken with the in-app camera (Capture Studio / device fallback). */
|
|
854
|
+
readonly CAPTURES_PHOTOS: "Captures/Photos";
|
|
855
|
+
/** Videos recorded with the in-app camera (media-capture Phase 7). */
|
|
856
|
+
readonly CAPTURES_VIDEOS: "Captures/Videos";
|
|
857
|
+
/** Audio recorded through the Capture Studio (media-capture Phase 7). */
|
|
858
|
+
readonly CAPTURES_AUDIO: "Captures/Audio";
|
|
859
|
+
/** Product-capture staging (features/product-capture) — per-item folders of
|
|
860
|
+
* photos/videos/voice notes shot ahead of listing creation. Org-namespaced
|
|
861
|
+
* via `folderForProductCaptureItem` (same server one-path-one-org rule as
|
|
862
|
+
* Captures). User-produced, so visible. */
|
|
863
|
+
readonly PRODUCT_CAPTURES: "Product Captures";
|
|
864
|
+
/** Commerce intake (W4 capture app): per-asset media filed under
|
|
865
|
+
* `Commerce Intake/<orgId>/<batchId>/<asset-leaf>` via
|
|
866
|
+
* `folderForIntakeAsset` (same server one-path-one-org rule as Captures).
|
|
867
|
+
* User-produced, so visible. */
|
|
868
|
+
readonly COMMERCE_INTAKE: "Commerce Intake";
|
|
869
|
+
/**
|
|
870
|
+
* Static app assets (sounds, hero images, model-card thumbnails, demo
|
|
871
|
+
* data) owned by an admin service account and rendered globally.
|
|
872
|
+
* Always public — CDN URL is the goal. Source of truth for the
|
|
873
|
+
* static-asset migration (see scripts/migrate-public-assets-to-cdn.ts).
|
|
874
|
+
*/
|
|
875
|
+
readonly APP_ASSETS: "App Assets";
|
|
876
|
+
/** Top-level documents (PDF, DOCX, etc.). */
|
|
877
|
+
readonly DOCUMENTS: "Documents";
|
|
878
|
+
/** PDF-specific drawer. */
|
|
879
|
+
readonly DOCUMENTS_PDFS: "Documents/PDFs";
|
|
880
|
+
/** Top-level code files (scripts, snippets). */
|
|
881
|
+
readonly CODE: "Code";
|
|
882
|
+
/** Code saved via the code-editor window. */
|
|
883
|
+
readonly CODE_EDITOR: "Code/Editor";
|
|
884
|
+
/** Generic AI-generated outputs. */
|
|
885
|
+
readonly GENERATED: "Generated";
|
|
886
|
+
/** Files attached to agent apps, keyed by app id at runtime. */
|
|
887
|
+
readonly AGENT_APPS: "Agent Apps";
|
|
888
|
+
/** Files attached to tasks, keyed by task id at runtime. */
|
|
889
|
+
readonly TASK_ATTACHMENTS: "Task Attachments";
|
|
890
|
+
/** Files attached to War Room tiles, keyed by tile id at runtime. */
|
|
891
|
+
readonly WAR_ROOM: "War Room";
|
|
892
|
+
/** Files dropped into a chat composer (pre-send or history). */
|
|
893
|
+
readonly CHAT_ATTACHMENTS: "Chat Attachments";
|
|
894
|
+
/** Slack-imported files. */
|
|
895
|
+
readonly SLACK_IMPORTS: "Slack Imports";
|
|
896
|
+
/** Web-scrape captures (HTML snapshots, exported markdown). */
|
|
897
|
+
readonly SCRAPED_CONTENT: "Scraped Content";
|
|
898
|
+
/**
|
|
899
|
+
* Org-scoped shared assets — logos, hero images, anything an
|
|
900
|
+
* organization renders on a public surface. Keyed by org id at
|
|
901
|
+
* runtime (e.g. `Shared Assets/orgs/<org-id>`). Always public —
|
|
902
|
+
* the CDN URL is the rendering target.
|
|
903
|
+
*/
|
|
904
|
+
readonly SHARED_ASSETS_ORGS: "Shared Assets/orgs";
|
|
905
|
+
/** Public screenshot attachments submitted through the feedback window. */
|
|
906
|
+
readonly FEEDBACK_IMAGES: "Shared Assets/feedback-images";
|
|
907
|
+
/**
|
|
908
|
+
* OG / social-preview covers for HTML pages the user publishes.
|
|
909
|
+
* Lives under Shared Assets so all org/public-surface previews
|
|
910
|
+
* share one CDN-backed bucket.
|
|
911
|
+
*/
|
|
912
|
+
readonly HTML_PAGES_OG: "Shared Assets/html-pages";
|
|
913
|
+
/**
|
|
914
|
+
* Favicons rendered by prompt-app / agent-app standalone pages.
|
|
915
|
+
* Always public — favicon requests are anonymous.
|
|
916
|
+
*/
|
|
917
|
+
readonly PROMPT_APPS_FAVICONS: "App Assets/prompt-apps/favicons";
|
|
918
|
+
/**
|
|
919
|
+
* Per-block assets attached to agent blocks (icons, illustrations,
|
|
920
|
+
* inline media). Keyed by block id at runtime via
|
|
921
|
+
* `folderForAgentBlock(blockId)`.
|
|
922
|
+
*/
|
|
923
|
+
readonly AGENT_BLOCKS: "Agent Apps/blocks";
|
|
924
|
+
/**
|
|
925
|
+
* Cover images rendered on canvas social/share surfaces. Public so
|
|
926
|
+
* the share-card OG image is a stable CDN URL.
|
|
927
|
+
*/
|
|
928
|
+
readonly CANVAS_COVERS: "Images/Canvas/Covers";
|
|
929
|
+
/**
|
|
930
|
+
* Source images the user is editing in the image studio. Private —
|
|
931
|
+
* these are the user's working files, NOT public render targets.
|
|
932
|
+
* Outputs that the user explicitly publishes land elsewhere.
|
|
933
|
+
*/
|
|
934
|
+
readonly IMAGES_EDITED_SOURCES: "Images/Edited/Sources";
|
|
935
|
+
/** Root for all ephemeral cloud-files work. Hidden in the tree. */
|
|
936
|
+
readonly TMP: ".matrx-tmp";
|
|
937
|
+
/** Short-lived audio files staged for URL-based transcription. */
|
|
938
|
+
readonly TMP_TRANSCRIPTS: ".matrx-tmp/transcripts";
|
|
939
|
+
/** Upload-in-progress staging (future use). */
|
|
940
|
+
readonly TMP_UPLOADS: ".matrx-tmp/uploads";
|
|
941
|
+
/** Root for backend-owned infrastructure files (variants, posters, etc.). */
|
|
942
|
+
readonly SYSTEM_FILES: "system-files";
|
|
943
|
+
/** SOCIAL_BASELINE variant storage: og.jpg / thumb.jpg / tiny.jpg / page1_url.jpg. */
|
|
944
|
+
readonly SYSTEM_VARIANTS: "system-files/variants";
|
|
945
|
+
/**
|
|
946
|
+
* Backend "generations" registry root — every AI generation (image,
|
|
947
|
+
* video, audio/TTS) the server materializes lands under
|
|
948
|
+
* `generations/<images|video|audio>/...`. Backend-owned; mirrors the
|
|
949
|
+
* server's `is_system_path()` (which treats `system-files` AND
|
|
950
|
+
* `generations` as system). Never shown in the user tree.
|
|
951
|
+
*/
|
|
952
|
+
readonly GENERATIONS: "generations";
|
|
953
|
+
/**
|
|
954
|
+
* Recorded audio captured by the Transcripts / voice features. As of
|
|
955
|
+
* 2026-06-14 the backend relocates every `origin: "transcripts"` upload
|
|
956
|
+
* here, UNDER the hidden `system-files/` root — one
|
|
957
|
+
* `recording_<iso>_<rand>.webm` per capture (80+ for an active user).
|
|
958
|
+
* Because it's under `system-files/`, `isSystemPath` already hides it
|
|
959
|
+
* from the tree, folder views, AND Recents — no separate predicate
|
|
960
|
+
* needed. Managed exclusively via the Transcripts UI (by `cld_files.id`).
|
|
961
|
+
* See docs/files/transcript-recordings-system-relocation.md.
|
|
962
|
+
*/
|
|
963
|
+
readonly TRANSCRIPT_RECORDINGS: "system-files/transcripts/Recordings";
|
|
964
|
+
/**
|
|
965
|
+
* LEGACY user-namespace location for transcript recordings, before the
|
|
966
|
+
* 2026-06-14 backend relocation to `system-files/transcripts/...`. Kept
|
|
967
|
+
* ONLY as a defensive Recents guard: the backend relocates every
|
|
968
|
+
* `origin: "transcripts"` upload, but the presigned/TUS (chunked + large
|
|
969
|
+
* file) relocation path was genuinely missing and is committed but NOT yet
|
|
970
|
+
* in prod as of 2026-06-15.
|
|
971
|
+
*
|
|
972
|
+
* DO NOT DROP THIS GUARD until that backend fix has DEPLOYED to prod —
|
|
973
|
+
* dropping it earlier would let a chunked/large recording slip through
|
|
974
|
+
* unhidden. Sequence: backend deploys → then remove this constant from
|
|
975
|
+
* `isSystemManagedContentPath` (and delete it). See the FE-response /
|
|
976
|
+
* "open items" in docs/files/transcript-recordings-system-relocation.md.
|
|
977
|
+
*/
|
|
978
|
+
readonly TRANSCRIPT_RECORDINGS_LEGACY: "Transcripts/Recordings";
|
|
979
|
+
/**
|
|
980
|
+
* Per-tool generated image variants — `tool-images/<id>/v/<name>.jpg`,
|
|
981
|
+
* multiple sizes per source. Machine-produced output, excluded from
|
|
982
|
+
* Recents (see `isSystemManagedContentPath`).
|
|
983
|
+
*/
|
|
984
|
+
readonly TOOL_IMAGES: "tool-images";
|
|
985
|
+
/**
|
|
986
|
+
* FastFire voice-drill recordings — full-session + per-card response clips.
|
|
987
|
+
* Live under the hidden `system-files/fastfire/...` root (same class as
|
|
988
|
+
* transcript recordings). Managed via Education / FastFire UI only.
|
|
989
|
+
*/
|
|
990
|
+
readonly SYSTEM_FASTFIRE: "system-files/fastfire";
|
|
991
|
+
readonly SYSTEM_FASTFIRE_SESSIONS: "system-files/fastfire/sessions";
|
|
992
|
+
readonly SYSTEM_FASTFIRE_RESPONSES: "system-files/fastfire/responses";
|
|
993
|
+
/**
|
|
994
|
+
* Spoken Practice (oral exam / interview / debate) recordings — full-session
|
|
995
|
+
* clips + per-prompt spoken-answer clips. Same hidden `system-files/` class as
|
|
996
|
+
* FastFire (so `isSystemPath` keeps them out of the Files browser + Recents).
|
|
997
|
+
*/
|
|
998
|
+
readonly SYSTEM_SPOKEN_PRACTICE: "system-files/spoken-practice";
|
|
999
|
+
readonly SYSTEM_SPOKEN_PRACTICE_SESSIONS: "system-files/spoken-practice/sessions";
|
|
1000
|
+
readonly SYSTEM_SPOKEN_PRACTICE_RESPONSES: "system-files/spoken-practice/responses";
|
|
1001
|
+
/**
|
|
1002
|
+
* Handwritten / image-answer grading — photos of worked problems a learner
|
|
1003
|
+
* submits to the vision grader (assessment written-item photo answers +
|
|
1004
|
+
* the standalone "Grade my handwritten work" surface). Same hidden
|
|
1005
|
+
* `system-files/` class (kept out of the Files browser + Recents).
|
|
1006
|
+
*/
|
|
1007
|
+
readonly SYSTEM_IMAGE_GRADE: "system-files/image-grade";
|
|
1008
|
+
readonly SYSTEM_IMAGE_GRADE_RESPONSES: "system-files/image-grade/responses";
|
|
1009
|
+
/**
|
|
1010
|
+
* LEGACY user-namespace FastFire paths (pre-2026-07 relocation). Kept as a
|
|
1011
|
+
* defensive tree + Recents guard until every row is backfilled under
|
|
1012
|
+
* `system-files/fastfire/`.
|
|
1013
|
+
*/
|
|
1014
|
+
readonly FASTFIRE_SESSIONS: "FastFire/sessions";
|
|
1015
|
+
readonly FASTFIRE_RESPONSES: "FastFire/responses";
|
|
1016
|
+
};
|
|
1017
|
+
/**
|
|
1018
|
+
* Files attached to a specific task.
|
|
1019
|
+
* folderForTask("abc-123") → "Task Attachments/abc-123"
|
|
1020
|
+
*/
|
|
1021
|
+
declare function folderForTask(taskId: string): string;
|
|
1022
|
+
/**
|
|
1023
|
+
* Files attached to a specific War Room tile.
|
|
1024
|
+
* folderForWarRoomThread("abc-123") → "War Room/abc-123"
|
|
1025
|
+
*/
|
|
1026
|
+
declare function folderForWarRoomThread(threadId: string): string;
|
|
1027
|
+
/**
|
|
1028
|
+
* Files attached to a specific chat / conversation.
|
|
1029
|
+
* folderForConversation("xyz") → "Chat Attachments/xyz"
|
|
1030
|
+
*/
|
|
1031
|
+
declare function folderForConversation(conversationId: string): string;
|
|
1032
|
+
/**
|
|
1033
|
+
* Assets for a specific agent app (favicon, icons, etc.).
|
|
1034
|
+
* folderForAgentApp("abc") → "Agent Apps/abc"
|
|
1035
|
+
*/
|
|
1036
|
+
declare function folderForAgentApp(appId: string): string;
|
|
1037
|
+
/**
|
|
1038
|
+
* Per-podcast episode assets.
|
|
1039
|
+
*/
|
|
1040
|
+
declare function folderForPodcast(podcastId: string): string;
|
|
1041
|
+
/**
|
|
1042
|
+
* Per-org shared assets.
|
|
1043
|
+
* folderForOrg("org-abc") → "Shared Assets/orgs/org-abc"
|
|
1044
|
+
*/
|
|
1045
|
+
declare function folderForOrg(orgId: string): string;
|
|
1046
|
+
/**
|
|
1047
|
+
* Per-agent-block assets (icons, illustrations, attached media).
|
|
1048
|
+
* folderForAgentBlock("blk_123") → "Agent Apps/blocks/blk_123"
|
|
1049
|
+
*/
|
|
1050
|
+
declare function folderForAgentBlock(blockId: string): string;
|
|
1051
|
+
/** Per-org capture subtree. Org id in the path keeps each workspace's
|
|
1052
|
+
* captures a DISTINCT global folder path (the server allows one folder
|
|
1053
|
+
* path per user, pinned to one org — a flat `Captures/Videos` cannot exist
|
|
1054
|
+
* under two orgs). Falls back to the flat path when there is no active org
|
|
1055
|
+
* (personal context).
|
|
1056
|
+
*
|
|
1057
|
+
* Nested output still resolves to `personal` visibility: the
|
|
1058
|
+
* `resolveDefaultVisibility` rule keys on the `Captures` prefix (a
|
|
1059
|
+
* longest-prefix match), so `Captures/<org>/Videos` matches the same rule as
|
|
1060
|
+
* the flat `Captures/Videos`. The org id in the path is filing, not access —
|
|
1061
|
+
* personal-visibility files stay owner-gated regardless of org. */
|
|
1062
|
+
declare function folderForCaptures(kind: "photo" | "video" | "audio", orgId?: string | null): string;
|
|
1063
|
+
/** Per-item product-capture folder: `Product Captures/<orgId>/<leaf>`.
|
|
1064
|
+
* `leaf` is the item's sanitized code (the QR value / SKU) when known at
|
|
1065
|
+
* creation, else the item id — set ONCE at item creation and never renamed.
|
|
1066
|
+
* Org id in the path for the same one-path-one-org server rule as
|
|
1067
|
+
* `folderForCaptures`. Files here upload with explicit `visibility:
|
|
1068
|
+
* "internal"` (org-wide access is the point of the surface), so the
|
|
1069
|
+
* `resolveDefaultVisibility` prefix rules are deliberately not consulted. */
|
|
1070
|
+
declare function folderForProductCaptureItem(orgId: string, leaf: string): string;
|
|
1071
|
+
/**
|
|
1072
|
+
* Commerce-intake artifact folder for one asset (or the batch-level stream in
|
|
1073
|
+
* untracked mode — pass the batch id as `leaf`). Fixed at creation, NEVER
|
|
1074
|
+
* renamed — a QR/serial that arrives later lives on identifier rows only
|
|
1075
|
+
* (PROTOTYPE-CONCEPTS P13: the cloud path is never renamed).
|
|
1076
|
+
*/
|
|
1077
|
+
declare function folderForIntakeAsset(orgId: string, batchId: string, leaf: string): string;
|
|
1078
|
+
/** True if the folder is under `.matrx-tmp/` (user-hidden). */
|
|
1079
|
+
declare function isHiddenFolder(folderPath: string): boolean;
|
|
1080
|
+
/**
|
|
1081
|
+
* True if `path` (a `cld_files.file_path` or `cld_folders.folder_path`)
|
|
1082
|
+
* points at a backend-owned system / infrastructure row that must NEVER
|
|
1083
|
+
* surface in the user's tree.
|
|
1084
|
+
*
|
|
1085
|
+
* Today this covers:
|
|
1086
|
+
* - `system-files` / `system-files/...` — backfilled SOCIAL_BASELINE
|
|
1087
|
+
* variant rows (og/thumb/tiny/page1_url) and their per-source-file
|
|
1088
|
+
* folders. ~3–4 rows per uploaded image, ~10k+ for active accounts.
|
|
1089
|
+
* - `generations` / `generations/...` — the backend AI-generation
|
|
1090
|
+
* registry root (image/video/audio output). Mirrors the server's
|
|
1091
|
+
* `is_system_path()`, which treats BOTH `system-files` and
|
|
1092
|
+
* `generations` as system; the tree RPC already drops these, this is
|
|
1093
|
+
* the FE-side parity guard at every other tree boundary.
|
|
1094
|
+
* - `.matrx-tmp` / `.matrx-tmp/...` — ephemeral staging the user
|
|
1095
|
+
* shouldn't see or manage.
|
|
1096
|
+
*
|
|
1097
|
+
* The predicate is intentionally path-based (not `derivation_kind` or
|
|
1098
|
+
* `parent_file_id`) because the Python backfill didn't populate those
|
|
1099
|
+
* fields on variant rows. Once they do, we'll add data-shape signals
|
|
1100
|
+
* here and drop the path heuristic.
|
|
1101
|
+
*
|
|
1102
|
+
* Pass either a file path or a folder path — both follow the same
|
|
1103
|
+
* convention (system paths are prefix-rooted).
|
|
1104
|
+
*/
|
|
1105
|
+
declare function isSystemPath(path: string | null | undefined): boolean;
|
|
1106
|
+
/**
|
|
1107
|
+
* THE VISIBILITY RULE IS THE DATABASE'S, NOT THIS FILE'S.
|
|
1108
|
+
*
|
|
1109
|
+
* `isHiddenFromUserTree()` and `isFastFirePath()` used to live here and were a
|
|
1110
|
+
* SECOND copy of the server's rule — the browser hid top-level `FastFire/**`
|
|
1111
|
+
* that the server called user-visible, so the sync daemon wrote those bytes to
|
|
1112
|
+
* disk while the browser refused to show them (folder-sync DECISIONS D16,
|
|
1113
|
+
* SPEC-SERVER §1.4 consumer 2). They are deleted.
|
|
1114
|
+
*
|
|
1115
|
+
* Rows that arrive through `get_user_file_tree` are already filtered by
|
|
1116
|
+
* `files.is_user_visible_path` — never filter them again. Realtime payloads,
|
|
1117
|
+
* which bypass the RPC, use the parity-tested mirror in
|
|
1118
|
+
* `features/files/utils/user-visible.ts`.
|
|
1119
|
+
*/
|
|
1120
|
+
/**
|
|
1121
|
+
* RECENTS IS THE DATABASE'S RULE TOO. `isExcludedFromRecents`,
|
|
1122
|
+
* `isGeneratedContentPath` and `isSystemManagedContentPath` lived here as a
|
|
1123
|
+
* client-only Recents rule; they are deleted (2026-09-24). Their roots moved
|
|
1124
|
+
* into the one declaration (`files.is_recent_activity*`, aidream
|
|
1125
|
+
* matrx_files/user_visible.py RECENT_EXCLUDED_ROOTS) and the browser applies
|
|
1126
|
+
* its parity-guarded mirror, `isRecentActivityFile` / `isRecentActivityPath`
|
|
1127
|
+
* in `features/files/utils/user-visible.ts`. Rule + registry: common-docs
|
|
1128
|
+
* systems/files/file-service/USER_FILES_VS_MACHINE_FILES.md.
|
|
1129
|
+
*/
|
|
1130
|
+
/**
|
|
1131
|
+
* True if the folder path matches one of our canonical conventions (either
|
|
1132
|
+
* visible or hidden). Useful for UI that wants to show a pretty icon next
|
|
1133
|
+
* to known folders.
|
|
1134
|
+
*/
|
|
1135
|
+
declare function isConventionalFolder(folderPath: string): boolean;
|
|
1136
|
+
/**
|
|
1137
|
+
* Maps a CloudFolders constant → human-friendly description for tooltips /
|
|
1138
|
+
* empty states. Extend as needed.
|
|
1139
|
+
*/
|
|
1140
|
+
declare const CloudFolderDescriptions: Record<string, string>;
|
|
1141
|
+
/**
|
|
1142
|
+
* Resolve the default visibility for a given folder path. Longest-prefix
|
|
1143
|
+
* match against `DEFAULT_VISIBILITY_RULES`; falls back to `"personal"` for
|
|
1144
|
+
* any folder not in the table. Use at upload sites that don't have an
|
|
1145
|
+
* obvious context-driven choice.
|
|
1146
|
+
*/
|
|
1147
|
+
declare function resolveDefaultVisibility(folderPath: string): Visibility;
|
|
1148
|
+
|
|
1149
|
+
/** Stable, narrow video publish-date text for cards, rows, and embeds. */
|
|
1150
|
+
declare function formatVideoPublishDate(value: string | null | undefined): string;
|
|
1151
|
+
/** Accessible detail for the compact video publish-date treatment. */
|
|
1152
|
+
declare function formatVideoPublishDateTitle(value: string | null | undefined): string;
|
|
1153
|
+
/**
|
|
1154
|
+
* Read a provider/schema publish date from the common metadata envelopes used
|
|
1155
|
+
* by research media, crawled site videos, and brand-library assets.
|
|
1156
|
+
*/
|
|
1157
|
+
declare function videoPublishDateFromMetadata(value: unknown): string | null;
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Shared bounded registry for ephemeral `URL.createObjectURL` blobs.
|
|
1161
|
+
*
|
|
1162
|
+
* Consumers that create local blob URLs use this primitive so previews cannot
|
|
1163
|
+
* leak memory without bound. Explicit cleanup remains preferred; the global
|
|
1164
|
+
* ceiling is a final safety net for callers that no longer own their URL.
|
|
1165
|
+
*/
|
|
1166
|
+
/** Create a tracked object URL; evicts the oldest if over the cap. */
|
|
1167
|
+
declare function createTrackedObjectUrl(blob: Blob): string;
|
|
1168
|
+
/** Explicitly revoke a tracked URL when a consumer knows it is finished. */
|
|
1169
|
+
declare function revokeTrackedObjectUrl(url: string | undefined | null): void;
|
|
1170
|
+
|
|
1171
|
+
export { type AudioBlock, CloudFolderDescriptions, CloudFolders, type DocumentBlock, type ExternalAudioBlock, type ExternalDocumentBlock, ExternalFetchError, type ExternalImageBlock, type ExternalVideoBlock, FileAccessDeniedError, FileDeletedError, FileHandlerError, type FileHandlerErrorCode, type FileIdentityHint, FileNotFoundError, FileUploadError, type ImageBlock, type MatrxAudioBlock, type MatrxDocumentBlock, type MatrxImageBlock, type MatrxVideoBlock, type MediaBlockKindArg, type MediaGenerationKind, type MediaGenerationMetadata, type MediaGenerationMetadataWire, type MediaKind, type MediaOrigin, type MediaRef, type MediaStatus, type MediaVisibility, type PermissionLevel, ShareLinkInvalidError, type UnifiedImageBlock, type UnifiedMediaBlock, UploadCancelledError, type VideoBlock, VideoPublishDate, type Visibility, type WireMediaBlock, type WireMediaBlockData, type YouTubeBlock, blockFromMediaRef, composeUploadFolderPath, createTrackedObjectUrl, deriveViewerUrl, extractFileIdFromUrl, fileIdToMediaRef, folderForAgentApp, folderForAgentBlock, folderForCaptures, folderForConversation, folderForIntakeAsset, folderForOrg, folderForPodcast, folderForProductCaptureItem, folderForTask, folderForWarRoomThread, formatVideoPublishDate, formatVideoPublishDateTitle, fromCxAudioPart, fromCxMediaPart, fromCxVideoPart, fromImageOutputData, fromMediaBlock, fromPartialImageData, fromRenderBlock, imageBlockFromMediaRef, isAudioBlock, isConventionalFolder, isDocumentBlock, isExternalImageBlock, isExternalMediaBlock, isHiddenFolder, isImageBlock, isMatrxImageBlock, isMatrxMediaBlock, isMediaBlockData, isSystemPath, isUnifiedImageBlock, isUnifiedMediaBlock, isUploadCancelledError, isVideoBlock, isYouTubeBlock, parseFilenameFromUrl, parseGenerationMetadata, resolveDefaultVisibility, revokeTrackedObjectUrl, toCxMediaPart, toVisibility, urlToMediaRef, videoBlockFromMediaRef, videoPublishDateFromMetadata };
|